Skip to content

Commit

Permalink
SQLite MCP Server update with demo
Browse files Browse the repository at this point in the history
  • Loading branch information
GarvanD committed Nov 21, 2024
1 parent 034f95c commit a0c864b
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 213 deletions.
22 changes: 14 additions & 8 deletions src/sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,18 @@ The server offers six core tools:
## Installation

```bash
# Required: Python 3.10+
python -m pip install mcp-sqlite-server

# Run with default settings
mcp-sqlite-server

# Run with custom database and Anthropic integration
mcp-sqlite-server --db-path ~/analysis.db --anthropic-key sk-xxx
# Add the server to your claude_desktop_config.json
"mcpServers": {
"sqlite": {
"command": "uv",
"args": [
"--directory",
"parent_of_servers_repo/servers/src/sqlite",
"run",
"sqlite",
"--db-path",
"~/test.db"
]
}
}
```
1 change: 0 additions & 1 deletion src/sqlite/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"mcp>=0.9.1",
"anthropic>=0.39.0",
]

[build-system]
Expand Down
5 changes: 1 addition & 4 deletions src/sqlite/src/sqlite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ def main():
parser.add_argument('--db-path',
default="./sqlite_mcp_server.db",
help='Path to SQLite database file')
parser.add_argument('--anthropic-api-key',
default=os.environ.get('ANTHROPIC_API_KEY'),
help='Anthropic API key (can also be set via ANTHROPIC_API_KEY environment variable)')

args = parser.parse_args()
asyncio.run(server.main(args.db_path, args.anthropic_api_key))
asyncio.run(server.main(args.db_path))


# Optionally expose other important items at package level
Expand Down
141 changes: 17 additions & 124 deletions src/sqlite/src/sqlite/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
import mcp.types as types
from mcp.server import NotificationOptions, Server, AnyUrl
import mcp.server.stdio
from anthropic import Anthropic

# Set up logging to file
log_file = Path('mcp_server.log')
handler = RotatingFileHandler(log_file, maxBytes=10*1024*1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger = logging.getLogger('mcp_sqlite_server')
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
Expand All @@ -25,23 +23,6 @@ def _init_database(self):
"""Initialize connection to the SQLite database"""
logger.debug("Initializing database connection")
with closing(sqlite3.connect(self.db_path)) as conn:
<<<<<<< HEAD
with closing(conn.cursor()) as cursor:
cursor.execute("""
CREATE TABLE IF NOT EXISTS notes (
name TEXT PRIMARY KEY,
content TEXT NOT NULL
)
""")
# Add example note if table is empty
cursor.execute("SELECT COUNT(*) FROM notes")
if cursor.fetchone()[0] == 0:
cursor.execute(
"INSERT INTO notes (name, content) VALUES (?, ?)",
("example", "This is an example note."),
)
conn.commit()
=======
conn.row_factory = sqlite3.Row
conn.close()

Expand All @@ -53,81 +34,17 @@ def _synthesize_memo(self) -> str:

insights = "\n".join(f"- {insight}" for insight in self.insights)

if self.anthropic_api_key is None:
memo = "📊 Business Intelligence Memo 📊\n\n"
memo += "Key Insights Discovered:\n\n"
memo += insights

if len(self.insights) > 1:
memo += "\nSummary:\n"
memo += f"Analysis has revealed {len(self.insights)} key business insights that suggest opportunities for strategic optimization and growth."

logger.debug("Generated basic memo format")
return memo
else:
try:
logger.debug("Requesting memo generation from Anthropic")
prompt = """
You are tasked with summarizing a set of business insights into a formal business memo. The insights are typically 1-2 sentences each and cover various aspects of the business. Your goal is to create a concise, well-organized memo that effectively communicates these insights to the recipient.
>>>>>>> 67b071f (new SQLite server for demo and Getting started guide. Added contributing.md and pull_request_template.md)
Here are the business insights you need to summarize:
<insights>
{insights}
</insights>
<<<<<<< HEAD
def _add_note(self, name: str, content: str):
"""Helper method to add or update a note in the database"""
with closing(sqlite3.connect(self.db_path)) as conn:
with closing(conn.cursor()) as cursor:
cursor.execute(
"INSERT OR REPLACE INTO notes (name, content) VALUES (?, ?)",
(name, content),
=======
To create the memo, follow these steps:
1. Review all the insights carefully.
2. Group related insights together under appropriate subheadings.
3. Summarize each group of insights into 1-2 concise paragraphs.
4. Ensure the memo flows logically from one point to the next.
5. Use professional language and maintain a formal tone throughout the memo.
Format the memo using these guidelines:
- Single-space the content, with a blank line between paragraphs
- Use bullet points or numbered lists where appropriate
- Keep the entire memo to one page if possible, two pages maximum
Write your final memo within <memo> tags. Ensure that all components of the memo are included and properly formatted.
""".format(insights=insights)
message = self.anthropic_client.messages.create(
max_tokens=4096,
messages=[
{
"role": "user",
"content": prompt
},
{
"role": "assistant",
"content": "<memo>"
}
],
model="claude-3-sonnet-20240229",
stop_sequences=["</memo>"],
>>>>>>> 67b071f (new SQLite server for demo and Getting started guide. Added contributing.md and pull_request_template.md)
)
logger.debug("Successfully received memo from Anthropic")
return message.content[0].text.strip()
except Exception as e:
logger.error(f"Error generating memo with Anthropic: {e}")
return insights

<<<<<<< HEAD
def __init__(self):
super().__init__("sqlite")
memo = "📊 Business Intelligence Memo 📊\n\n"
memo += "Key Insights Discovered:\n\n"
memo += insights

if len(self.insights) > 1:
memo += "\nSummary:\n"
memo += f"Analysis has revealed {len(self.insights)} key business insights that suggest opportunities for strategic optimization and growth."

logger.debug("Generated basic memo format")
return memo

=======
def _execute_query(self, query: str, params=None) -> list[dict]:
"""Execute a SQL query and return results as a list of dictionaries"""
logger.debug(f"Executing query: {query}")
Expand All @@ -153,22 +70,16 @@ def _execute_query(self, query: str, params=None) -> list[dict]:
logger.error(f"Database error executing query: {e}")
raise

def __init__(self, db_path: str = "~/sqlite_mcp_server.db", anthropic_api_key: str | None = None):
def __init__(self, db_path: str = "~/sqlite_mcp_server.db"):
logger.info("Initializing McpServer")
super().__init__("sqlite-manager")

>>>>>>> 67b071f (new SQLite server for demo and Getting started guide. Added contributing.md and pull_request_template.md)
# Initialize SQLite database
self.db_path = str(Path(db_path).expanduser())
Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)
self._init_database()
logger.debug(f"Initialized database at {self.db_path}")

# Initialize Anthropic API key
self.anthropic_api_key = anthropic_api_key
if anthropic_api_key:
self.anthropic_client = Anthropic(api_key=anthropic_api_key)
logger.debug("Initialized Anthropic client")

# Initialize insights list
self.insights = []
Expand All @@ -181,7 +92,7 @@ async def handle_list_resources() -> list[types.Resource]:
logger.debug("Handling list_resources request")
return [
types.Resource(
uri=AnyUrl("memo://insights"), # Changed from memo:///insights
uri=AnyUrl("memo://insights"),
name="Business Insights Memo",
description="A living document of discovered business insights",
mimeType="text/plain",
Expand All @@ -195,7 +106,7 @@ async def handle_read_resource(uri: AnyUrl) -> str:
logger.error(f"Unsupported URI scheme: {uri.scheme}")
raise ValueError(f"Unsupported URI scheme: {uri.scheme}")

path = str(uri).replace("memo://", "") # Changed to match new URI format
path = str(uri).replace("memo://", "")
if not path or path != "insights":
logger.error(f"Unknown resource path: {path}")
raise ValueError(f"Unknown resource path: {path}")
Expand Down Expand Up @@ -225,22 +136,6 @@ async def handle_get_prompt(name: str, arguments: dict[str, str] | None) -> type
if name != "mcp-demo":
logger.error(f"Unknown prompt: {name}")
raise ValueError(f"Unknown prompt: {name}")
<<<<<<< HEAD
notes = (
"<notes>\n"
+ "\n".join(
f"<note name='{name}'>\n{content}\n</note>"
for name, content in self._get_notes().items()
)
+ "\n</notes>"
)
style = (arguments or {}).get("style", "simple")
prompt = """
Your task is to provide a summary of the notes provided below.
{notes}
Ensure that the summary is in {style} style.
""".format(notes=notes, style=style)
=======

if not arguments or "topic" not in arguments:
logger.error("Missing required argument: topic")
Expand Down Expand Up @@ -329,7 +224,6 @@ async def handle_get_prompt(name: str, arguments: dict[str, str] | None) -> type
""".format(topic=topic)

logger.debug(f"Generated prompt template for topic: {topic}")
>>>>>>> 67b071f (new SQLite server for demo and Getting started guide. Added contributing.md and pull_request_template.md)
return types.GetPromptResult(
description=f"Demo template for {topic}",
messages=[
Expand Down Expand Up @@ -438,7 +332,7 @@ async def handle_call_tool(
memo = self._synthesize_memo()

# Notify clients that the memo resource has changed
await self.request_context.session.send_resource_updated("memo://insights") # Changed from memo:///insights
await self.request_context.session.send_resource_updated("memo://insights")

return [types.TextContent(type="text", text="Insight added to memo")]
if not arguments:
Expand Down Expand Up @@ -470,9 +364,9 @@ async def handle_call_tool(
except Exception as e:
return [types.TextContent(type="text", text=f"Error: {str(e)}")]

async def main(db_path: str, anthropic_api_key: str | None = None):
async def main(db_path: str):
logger.info(f"Starting SQLite MCP Server with DB path: {db_path}")
server = McpServer(db_path, anthropic_api_key)
server = McpServer(db_path)

async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
logger.info("Server running with stdio transport")
Expand All @@ -485,8 +379,7 @@ async def main(db_path: str, anthropic_api_key: str | None = None):
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={
"anthropic_api_key": {"key": anthropic_api_key}
} if anthropic_api_key else {},
},
),
),
)
Loading

0 comments on commit a0c864b

Please sign in to comment.