sqlsaber 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sqlsaber might be problematic. Click here for more details.

Files changed (50) hide show
  1. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/CHANGELOG.md +16 -0
  2. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/PKG-INFO +72 -3
  3. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/README.md +70 -2
  4. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/pyproject.toml +4 -1
  5. sqlsaber-0.4.1/src/sqlsaber/agents/mcp.py +21 -0
  6. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/commands.py +3 -0
  7. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/interactive.py +9 -6
  8. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/database/schema.py +2 -2
  9. sqlsaber-0.4.1/src/sqlsaber/mcp/__init__.py +5 -0
  10. sqlsaber-0.4.1/src/sqlsaber/mcp/mcp.py +138 -0
  11. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/uv.lock +171 -1
  12. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/.github/workflows/publish.yml +0 -0
  13. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/.gitignore +0 -0
  14. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/.python-version +0 -0
  15. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/CLAUDE.md +0 -0
  16. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/LICENSE +0 -0
  17. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/pytest.ini +0 -0
  18. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/__init__.py +0 -0
  19. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/__main__.py +0 -0
  20. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/agents/__init__.py +0 -0
  21. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/agents/anthropic.py +0 -0
  22. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/agents/base.py +0 -0
  23. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/agents/streaming.py +0 -0
  24. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/__init__.py +0 -0
  25. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/database.py +0 -0
  26. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/display.py +0 -0
  27. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/memory.py +0 -0
  28. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/models.py +0 -0
  29. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/cli/streaming.py +0 -0
  30. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/config/__init__.py +0 -0
  31. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/config/api_keys.py +0 -0
  32. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/config/database.py +0 -0
  33. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/config/settings.py +0 -0
  34. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/database/__init__.py +0 -0
  35. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/database/connection.py +0 -0
  36. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/memory/__init__.py +0 -0
  37. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/memory/manager.py +0 -0
  38. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/memory/storage.py +0 -0
  39. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/models/__init__.py +0 -0
  40. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/models/events.py +0 -0
  41. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/src/sqlsaber/models/types.py +0 -0
  42. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/__init__.py +0 -0
  43. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/conftest.py +0 -0
  44. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_cli/__init__.py +0 -0
  45. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_cli/test_commands.py +0 -0
  46. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_config/__init__.py +0 -0
  47. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_config/test_database.py +0 -0
  48. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_config/test_settings.py +0 -0
  49. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_database/__init__.py +0 -0
  50. {sqlsaber-0.3.0 → sqlsaber-0.4.1}/tests/test_database/test_connection.py +0 -0
@@ -4,6 +4,22 @@ All notable changes to SQLSaber will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.4.1] - 2025-06-26
8
+
9
+ ### Added
10
+
11
+ - Show connected database information at the start of a session
12
+ - Update welcome message for clarity
13
+
14
+ ## [0.4.0] - 2025-06-25
15
+
16
+ ### Added
17
+
18
+ - MCP (Model Context Protocol) server support
19
+ - `saber-mcp` console script for running MCP server
20
+ - MCP tools: `get_databases()`, `list_tables()`, `introspect_schema()`, `execute_sql()`
21
+ - Instructions and documentation for configuring MCP clients (Claude Code, etc.)
22
+
7
23
  ## [0.3.0] - 2025-06-25
8
24
 
9
25
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: SQLSaber - Agentic SQL assistant like Claude Code
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -8,6 +8,7 @@ Requires-Dist: aiomysql>=0.2.0
8
8
  Requires-Dist: aiosqlite>=0.21.0
9
9
  Requires-Dist: anthropic>=0.54.0
10
10
  Requires-Dist: asyncpg>=0.30.0
11
+ Requires-Dist: fastmcp>=2.9.0
11
12
  Requires-Dist: httpx>=0.28.1
12
13
  Requires-Dist: keyring>=25.6.0
13
14
  Requires-Dist: pandas>=2.0.0
@@ -23,7 +24,28 @@ Description-Content-Type: text/markdown
23
24
 
24
25
  SQLSaber is an agentic SQL assistant. Think Claude Code but for SQL.
25
26
 
26
- Ask your questions in natural language and it will gather the right context and answer your query by writing SQL and analyzing the results.
27
+ Ask your questions in natural language and it will gather the right context automatically and answer your query by writing SQL and analyzing the results.
28
+
29
+ ## Table of Contents
30
+
31
+ - [Features](#features)
32
+ - [Installation](#installation)
33
+ - [Configuration](#configuration)
34
+ - [Database Connection](#database-connection)
35
+ - [AI Model Configuration](#ai-model-configuration)
36
+ - [Memory Management](#memory-management)
37
+ - [Usage](#usage)
38
+ - [Interactive Mode](#interactive-mode)
39
+ - [Single Query](#single-query)
40
+ - [Database Selection](#database-selection)
41
+ - [Examples](#examples)
42
+ - [MCP Server Integration](#mcp-server-integration)
43
+ - [Starting the MCP Server](#starting-the-mcp-server)
44
+ - [Configuring MCP Clients](#configuring-mcp-clients)
45
+ - [Available MCP Tools](#available-mcp-tools)
46
+ - [How It Works](#how-it-works)
47
+ - [Contributing](#contributing)
48
+ - [License](#license)
27
49
 
28
50
  ## Features
29
51
 
@@ -34,19 +56,29 @@ Ask your questions in natural language and it will gather the right context and
34
56
  - 💬 Interactive REPL mode
35
57
  - 🎨 Beautiful formatted output with syntax highlighting
36
58
  - 🗄️ Support for PostgreSQL, SQLite, and MySQL
59
+ - 🔌 MCP (Model Context Protocol) server support
37
60
 
38
61
  ## Installation
39
62
 
63
+ ### `uv`
64
+
40
65
  ```bash
41
66
  uv tool install sqlsaber
42
67
  ```
43
68
 
44
- or
69
+ ### `pipx`
45
70
 
46
71
  ```bash
47
72
  pipx install sqlsaber
48
73
  ```
49
74
 
75
+ ### `brew`
76
+
77
+ ```bash
78
+ brew install uv
79
+ uv tool install sqlsaber
80
+ ```
81
+
50
82
  ## Configuration
51
83
 
52
84
  ### Database Connection
@@ -140,6 +172,43 @@ saber query "show me the distribution of customer ages"
140
172
  saber query "which products had the highest sales growth last quarter?"
141
173
  ```
142
174
 
175
+ ## MCP Server Integration
176
+
177
+ SQLSaber includes an MCP (Model Context Protocol) server that allows AI agents like Claude Code to directly leverage tools available in SQLSaber.
178
+
179
+ ### Starting the MCP Server
180
+
181
+ Run the MCP server using uvx:
182
+
183
+ ```bash
184
+ uvx --from sqlsaber saber-mcp
185
+ ```
186
+
187
+ ### Configuring MCP Clients
188
+
189
+ #### Claude Code
190
+
191
+ Add SQLSaber as an MCP server in Claude Code:
192
+
193
+ ```bash
194
+ claude mcp add -- uvx --from sqlsaber saber-mcp
195
+ ```
196
+
197
+ #### Other MCP Clients
198
+
199
+ For other MCP clients, configure them to run the command: `uvx --from sqlsaber saber-mcp`
200
+
201
+ ### Available MCP Tools
202
+
203
+ Once connected, the MCP client will have access to these tools:
204
+
205
+ - `get_databases()` - Lists all configured databases
206
+ - `list_tables(database)` - Get all tables in a database with row counts
207
+ - `introspect_schema(database, table_pattern?)` - Get detailed schema information
208
+ - `execute_sql(database, query, limit?)` - Execute SQL queries (read-only)
209
+
210
+ The MCP server uses your existing SQLSaber database configurations, so make sure to set up your databases using `saber db add` first.
211
+
143
212
  ## How It Works
144
213
 
145
214
  SQLSaber uses an intelligent three-step process optimized for minimal token usage:
@@ -4,7 +4,28 @@
4
4
 
5
5
  SQLSaber is an agentic SQL assistant. Think Claude Code but for SQL.
6
6
 
7
- Ask your questions in natural language and it will gather the right context and answer your query by writing SQL and analyzing the results.
7
+ Ask your questions in natural language and it will gather the right context automatically and answer your query by writing SQL and analyzing the results.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Installation](#installation)
13
+ - [Configuration](#configuration)
14
+ - [Database Connection](#database-connection)
15
+ - [AI Model Configuration](#ai-model-configuration)
16
+ - [Memory Management](#memory-management)
17
+ - [Usage](#usage)
18
+ - [Interactive Mode](#interactive-mode)
19
+ - [Single Query](#single-query)
20
+ - [Database Selection](#database-selection)
21
+ - [Examples](#examples)
22
+ - [MCP Server Integration](#mcp-server-integration)
23
+ - [Starting the MCP Server](#starting-the-mcp-server)
24
+ - [Configuring MCP Clients](#configuring-mcp-clients)
25
+ - [Available MCP Tools](#available-mcp-tools)
26
+ - [How It Works](#how-it-works)
27
+ - [Contributing](#contributing)
28
+ - [License](#license)
8
29
 
9
30
  ## Features
10
31
 
@@ -15,19 +36,29 @@ Ask your questions in natural language and it will gather the right context and
15
36
  - 💬 Interactive REPL mode
16
37
  - 🎨 Beautiful formatted output with syntax highlighting
17
38
  - 🗄️ Support for PostgreSQL, SQLite, and MySQL
39
+ - 🔌 MCP (Model Context Protocol) server support
18
40
 
19
41
  ## Installation
20
42
 
43
+ ### `uv`
44
+
21
45
  ```bash
22
46
  uv tool install sqlsaber
23
47
  ```
24
48
 
25
- or
49
+ ### `pipx`
26
50
 
27
51
  ```bash
28
52
  pipx install sqlsaber
29
53
  ```
30
54
 
55
+ ### `brew`
56
+
57
+ ```bash
58
+ brew install uv
59
+ uv tool install sqlsaber
60
+ ```
61
+
31
62
  ## Configuration
32
63
 
33
64
  ### Database Connection
@@ -121,6 +152,43 @@ saber query "show me the distribution of customer ages"
121
152
  saber query "which products had the highest sales growth last quarter?"
122
153
  ```
123
154
 
155
+ ## MCP Server Integration
156
+
157
+ SQLSaber includes an MCP (Model Context Protocol) server that allows AI agents like Claude Code to directly leverage tools available in SQLSaber.
158
+
159
+ ### Starting the MCP Server
160
+
161
+ Run the MCP server using uvx:
162
+
163
+ ```bash
164
+ uvx --from sqlsaber saber-mcp
165
+ ```
166
+
167
+ ### Configuring MCP Clients
168
+
169
+ #### Claude Code
170
+
171
+ Add SQLSaber as an MCP server in Claude Code:
172
+
173
+ ```bash
174
+ claude mcp add -- uvx --from sqlsaber saber-mcp
175
+ ```
176
+
177
+ #### Other MCP Clients
178
+
179
+ For other MCP clients, configure them to run the command: `uvx --from sqlsaber saber-mcp`
180
+
181
+ ### Available MCP Tools
182
+
183
+ Once connected, the MCP client will have access to these tools:
184
+
185
+ - `get_databases()` - Lists all configured databases
186
+ - `list_tables(database)` - Get all tables in a database with row counts
187
+ - `introspect_schema(database, table_pattern?)` - Get detailed schema information
188
+ - `execute_sql(database, query, limit?)` - Execute SQL queries (read-only)
189
+
190
+ The MCP server uses your existing SQLSaber database configurations, so make sure to set up your databases using `saber db add` first.
191
+
124
192
  ## How It Works
125
193
 
126
194
  SQLSaber uses an intelligent three-step process optimized for minimal token usage:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlsaber"
3
- version = "0.3.0"
3
+ version = "0.4.1"
4
4
  description = "SQLSaber - Agentic SQL assistant like Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -16,6 +16,7 @@ dependencies = [
16
16
  "aiomysql>=0.2.0",
17
17
  "aiosqlite>=0.21.0",
18
18
  "pandas>=2.0.0",
19
+ "fastmcp>=2.9.0",
19
20
  ]
20
21
 
21
22
  [tool.uv]
@@ -32,3 +33,5 @@ packages = ["src/sqlsaber"]
32
33
  sqlsaber = "sqlsaber.cli.commands:main"
33
34
  saber = "sqlsaber.cli.commands:main"
34
35
  sql = "sqlsaber.cli.commands:main"
36
+ sqlsaber-mcp = "sqlsaber.mcp.mcp:main"
37
+ saber-mcp = "sqlsaber.mcp.mcp:main"
@@ -0,0 +1,21 @@
1
+ """Generic SQL agent implementation for MCP tools."""
2
+
3
+ from typing import AsyncIterator
4
+ from sqlsaber.agents.base import BaseSQLAgent
5
+ from sqlsaber.database.connection import BaseDatabaseConnection
6
+ from sqlsaber.models.events import StreamEvent
7
+
8
+
9
+ class MCPSQLAgent(BaseSQLAgent):
10
+ """MCP SQL Agent for MCP tool operations without LLM-specific logic."""
11
+
12
+ def __init__(self, db_connection: BaseDatabaseConnection):
13
+ super().__init__(db_connection)
14
+
15
+ async def query_stream(
16
+ self, user_query: str, use_history: bool = True
17
+ ) -> AsyncIterator[StreamEvent]:
18
+ """Not implemented for generic agent as it's only used for tool operations."""
19
+ raise NotImplementedError(
20
+ "MCPSQLAgent does not support query streaming. Use specific agent implementations for conversation."
21
+ )
@@ -117,6 +117,9 @@ def query(
117
117
  if query_text:
118
118
  # Single query mode with streaming
119
119
  streaming_handler = StreamingQueryHandler(console)
120
+ console.print(
121
+ f"[bold blue]Connected to:[/bold blue] {db_name} {agent._get_database_type_name()}\n"
122
+ )
120
123
  await streaming_handler.execute_streaming_query(query_text, agent)
121
124
  else:
122
125
  # Interactive mode
@@ -20,21 +20,24 @@ class InteractiveSession:
20
20
 
21
21
  def show_welcome_message(self):
22
22
  """Display welcome message for interactive mode."""
23
+ # Show database information
24
+ db_name = getattr(self.agent, "database_name", None) or "Unknown"
25
+ db_type = self.agent._get_database_type_name()
26
+
23
27
  self.console.print(
24
28
  Panel.fit(
25
29
  "[bold green]SQLSaber - Use the agent Luke![/bold green]\n\n"
26
- "Type your queries in natural language.\n\n"
27
- "Press Esc-Enter or Meta-Enter to submit your query.\n\n"
28
- "Type 'exit' or 'quit' to leave.",
30
+ "[bold]Your agentic SQL assistant.[/bold]\n\n\n"
31
+ "[dim]Use 'clear' to reset conversation, 'exit' or 'quit' to leave.[/dim]\n\n"
32
+ "[dim]Start a message with '#' to add something to agent's memory for this database.[/dim]",
29
33
  border_style="green",
30
34
  )
31
35
  )
32
-
33
36
  self.console.print(
34
- "[dim]Commands: 'clear' to reset conversation, 'exit' or 'quit' to leave[/dim]"
37
+ f"[bold blue]Connected to:[/bold blue] {db_name} ({db_type})\n"
35
38
  )
36
39
  self.console.print(
37
- "[dim]Memory: Start a message with '#' to add it as a memory for this database[/dim]\n"
40
+ "[dim]Press Esc-Enter or Meta-Enter to submit your query.[/dim]\n"
38
41
  )
39
42
 
40
43
  async def run(self):
@@ -201,8 +201,8 @@ class PostgreSQLSchemaIntrospector(BaseSchemaIntrospector):
201
201
  t.table_type,
202
202
  COALESCE(ts.approximate_row_count, 0) as row_count
203
203
  FROM information_schema.tables t
204
- LEFT JOIN table_stats ts
205
- ON t.table_schema = ts.schemaname
204
+ LEFT JOIN table_stats ts
205
+ ON t.table_schema = ts.schemaname
206
206
  AND t.table_name = ts.tablename
207
207
  WHERE t.table_schema NOT IN ('pg_catalog', 'information_schema')
208
208
  ORDER BY t.table_schema, t.table_name;
@@ -0,0 +1,5 @@
1
+ """MCP (Model Context Protocol) server implementation for SQLSaber."""
2
+
3
+ from .mcp import mcp
4
+
5
+ __all__ = ["mcp"]
@@ -0,0 +1,138 @@
1
+ """FastMCP server implementation for SQLSaber."""
2
+
3
+ import json
4
+ from typing import Optional
5
+
6
+ from fastmcp import FastMCP
7
+
8
+ from sqlsaber.agents.mcp import MCPSQLAgent
9
+ from sqlsaber.config.database import DatabaseConfigManager
10
+ from sqlsaber.database.connection import DatabaseConnection
11
+
12
+ INSTRUCTIONS = """
13
+ This server provides helpful resources and tools that will help you address users queries on their database.
14
+
15
+ - Get all databases using `get_databases()`
16
+ - Call `list_tables()` to get a list of all tables in the database with row counts. Use this first to discover available tables.
17
+ - Call `introspect_schema()` to introspect database schema to understand table structures.
18
+ - Call `execute_sql()` to execute SQL queries against the database and retrieve results.
19
+
20
+ Guidelines:
21
+ - Use list_tables first, then introspect_schema for specific tables only
22
+ - Use table patterns like 'sample%' or '%experiment%' to filter related tables
23
+ - Use proper JOIN syntax and avoid cartesian products
24
+ - Include appropriate WHERE clauses to limit results
25
+ - Handle errors gracefully and suggest fixes
26
+ """
27
+
28
+ # Create the FastMCP server instance
29
+ mcp = FastMCP(name="SQL Assistant", instructions=INSTRUCTIONS)
30
+
31
+ # Initialize the database config manager
32
+ config_manager = DatabaseConfigManager()
33
+
34
+
35
+ async def _create_agent_for_database(database_name: str) -> Optional[MCPSQLAgent]:
36
+ """Create a MCPSQLAgent for the specified database."""
37
+ try:
38
+ # Look up configured database connection
39
+ db_config = config_manager.get_database(database_name)
40
+ if not db_config:
41
+ return None
42
+ connection_string = db_config.to_connection_string()
43
+
44
+ # Create database connection
45
+ db_conn = DatabaseConnection(connection_string)
46
+
47
+ # Create and return the agent
48
+ agent = MCPSQLAgent(db_conn)
49
+ return agent
50
+
51
+ except Exception:
52
+ return None
53
+
54
+
55
+ @mcp.tool
56
+ def get_databases() -> dict:
57
+ """List all configured databases with their types."""
58
+ databases = []
59
+ for db_config in config_manager.list_databases():
60
+ databases.append(
61
+ {
62
+ "name": db_config.name,
63
+ "type": db_config.type,
64
+ "database": db_config.database,
65
+ "host": db_config.host,
66
+ "port": db_config.port,
67
+ "is_default": db_config.name == config_manager.get_default_name(),
68
+ }
69
+ )
70
+
71
+ return {"databases": databases, "count": len(databases)}
72
+
73
+
74
+ @mcp.tool
75
+ async def list_tables(database: str) -> str:
76
+ """
77
+ Get a list of all tables in the database with row counts. Use this first to discover available tables.
78
+ """
79
+ try:
80
+ agent = await _create_agent_for_database(database)
81
+ if not agent:
82
+ return json.dumps(
83
+ {"error": f"Database '{database}' not found or could not connect"}
84
+ )
85
+
86
+ result = await agent.list_tables()
87
+ await agent.db.close()
88
+ return result
89
+
90
+ except Exception as e:
91
+ return json.dumps({"error": f"Error listing tables: {str(e)}"})
92
+
93
+
94
+ @mcp.tool
95
+ async def introspect_schema(database: str, table_pattern: Optional[str] = None) -> str:
96
+ """
97
+ Introspect database schema to understand table structures. Use optional pattern to filter tables (e.g., 'public.users', 'user%', '%order%').
98
+ """
99
+ try:
100
+ agent = await _create_agent_for_database(database)
101
+ if not agent:
102
+ return json.dumps(
103
+ {"error": f"Database '{database}' not found or could not connect"}
104
+ )
105
+
106
+ result = await agent.introspect_schema(table_pattern)
107
+ await agent.db.close()
108
+ return result
109
+
110
+ except Exception as e:
111
+ return json.dumps({"error": f"Error introspecting schema: {str(e)}"})
112
+
113
+
114
+ @mcp.tool
115
+ async def execute_sql(database: str, query: str, limit: Optional[int] = 100) -> str:
116
+ """Execute a SQL query against the specified database."""
117
+ try:
118
+ agent = await _create_agent_for_database(database)
119
+ if not agent:
120
+ return json.dumps(
121
+ {"error": f"Database '{database}' not found or could not connect"}
122
+ )
123
+
124
+ result = await agent.execute_sql(query, limit)
125
+ await agent.db.close()
126
+ return result
127
+
128
+ except Exception as e:
129
+ return json.dumps({"error": f"Error executing SQL: {str(e)}"})
130
+
131
+
132
+ def main():
133
+ """Entry point for the MCP server console script."""
134
+ mcp.run()
135
+
136
+
137
+ if __name__ == "__main__":
138
+ main()
@@ -91,6 +91,18 @@ wheels = [
91
91
  { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623 },
92
92
  ]
93
93
 
94
+ [[package]]
95
+ name = "authlib"
96
+ version = "1.6.0"
97
+ source = { registry = "https://pypi.org/simple" }
98
+ dependencies = [
99
+ { name = "cryptography" },
100
+ ]
101
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/9d/b1e08d36899c12c8b894a44a5583ee157789f26fc4b176f8e4b6217b56e1/authlib-1.6.0.tar.gz", hash = "sha256:4367d32031b7af175ad3a323d571dc7257b7099d55978087ceae4a0d88cd3210", size = 158371 }
102
+ wheels = [
103
+ { url = "https://files.pythonhosted.org/packages/84/29/587c189bbab1ccc8c86a03a5d0e13873df916380ef1be461ebe6acebf48d/authlib-1.6.0-py2.py3-none-any.whl", hash = "sha256:91685589498f79e8655e8a8947431ad6288831d643f11c55c2143ffcc738048d", size = 239981 },
104
+ ]
105
+
94
106
  [[package]]
95
107
  name = "certifi"
96
108
  version = "2025.6.15"
@@ -109,6 +121,8 @@ dependencies = [
109
121
  ]
110
122
  sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
111
123
  wheels = [
124
+ { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
125
+ { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
112
126
  { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
113
127
  { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
114
128
  { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
@@ -116,6 +130,10 @@ wheels = [
116
130
  { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
117
131
  { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
118
132
  { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
133
+ { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
134
+ { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
135
+ { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
136
+ { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
119
137
  { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
120
138
  { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
121
139
  { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
@@ -123,6 +141,8 @@ wheels = [
123
141
  { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
124
142
  { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
125
143
  { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
144
+ { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
145
+ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
126
146
  ]
127
147
 
128
148
  [[package]]
@@ -155,6 +175,7 @@ dependencies = [
155
175
  ]
156
176
  sdist = { url = "https://files.pythonhosted.org/packages/fe/c8/a2a376a8711c1e11708b9c9972e0c3223f5fc682552c82d8db844393d6ce/cryptography-45.0.4.tar.gz", hash = "sha256:7405ade85c83c37682c8fe65554759800a4a8c54b2d96e0f8ad114d31b808d57", size = 744890 }
157
177
  wheels = [
178
+ { url = "https://files.pythonhosted.org/packages/cc/1c/92637793de053832523b410dbe016d3f5c11b41d0cf6eef8787aabb51d41/cryptography-45.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:425a9a6ac2823ee6e46a76a21a4e8342d8fa5c01e08b823c1f19a8b74f096069", size = 7055712 },
158
179
  { url = "https://files.pythonhosted.org/packages/ba/14/93b69f2af9ba832ad6618a03f8a034a5851dc9a3314336a3d71c252467e1/cryptography-45.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:680806cf63baa0039b920f4976f5f31b10e772de42f16310a6839d9f21a26b0d", size = 4205335 },
159
180
  { url = "https://files.pythonhosted.org/packages/67/30/fae1000228634bf0b647fca80403db5ca9e3933b91dd060570689f0bd0f7/cryptography-45.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ca0f52170e821bc8da6fc0cc565b7bb8ff8d90d36b5e9fdd68e8a86bdf72036", size = 4431487 },
160
181
  { url = "https://files.pythonhosted.org/packages/6d/5a/7dffcf8cdf0cb3c2430de7404b327e3db64735747d641fc492539978caeb/cryptography-45.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f3fe7a5ae34d5a414957cc7f457e2b92076e72938423ac64d215722f6cf49a9e", size = 4208922 },
@@ -164,6 +185,9 @@ wheels = [
164
185
  { url = "https://files.pythonhosted.org/packages/db/b7/a84bdcd19d9c02ec5807f2ec2d1456fd8451592c5ee353816c09250e3561/cryptography-45.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2882338b2a6e0bd337052e8b9007ced85c637da19ef9ecaf437744495c8c2999", size = 4463623 },
165
186
  { url = "https://files.pythonhosted.org/packages/d8/84/69707d502d4d905021cac3fb59a316344e9f078b1da7fb43ecde5e10840a/cryptography-45.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:23b9c3ea30c3ed4db59e7b9619272e94891f8a3a5591d0b656a7582631ccf750", size = 4332447 },
166
187
  { url = "https://files.pythonhosted.org/packages/f3/ee/d4f2ab688e057e90ded24384e34838086a9b09963389a5ba6854b5876598/cryptography-45.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0a97c927497e3bc36b33987abb99bf17a9a175a19af38a892dc4bbb844d7ee2", size = 4572830 },
188
+ { url = "https://files.pythonhosted.org/packages/70/d4/994773a261d7ff98034f72c0e8251fe2755eac45e2265db4c866c1c6829c/cryptography-45.0.4-cp311-abi3-win32.whl", hash = "sha256:e00a6c10a5c53979d6242f123c0a97cff9f3abed7f064fc412c36dc521b5f257", size = 2932769 },
189
+ { url = "https://files.pythonhosted.org/packages/5a/42/c80bd0b67e9b769b364963b5252b17778a397cefdd36fa9aa4a5f34c599a/cryptography-45.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:817ee05c6c9f7a69a16200f0c90ab26d23a87701e2a284bd15156783e46dbcc8", size = 3410441 },
190
+ { url = "https://files.pythonhosted.org/packages/ce/0b/2488c89f3a30bc821c9d96eeacfcab6ff3accc08a9601ba03339c0fd05e5/cryptography-45.0.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:964bcc28d867e0f5491a564b7debb3ffdd8717928d315d12e0d7defa9e43b723", size = 7031836 },
167
191
  { url = "https://files.pythonhosted.org/packages/fe/51/8c584ed426093aac257462ae62d26ad61ef1cbf5b58d8b67e6e13c39960e/cryptography-45.0.4-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6a5bf57554e80f75a7db3d4b1dacaa2764611ae166ab42ea9a72bcdb5d577637", size = 4195746 },
168
192
  { url = "https://files.pythonhosted.org/packages/5c/7d/4b0ca4d7af95a704eef2f8f80a8199ed236aaf185d55385ae1d1610c03c2/cryptography-45.0.4-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:46cf7088bf91bdc9b26f9c55636492c1cce3e7aaf8041bbf0243f5e5325cfb2d", size = 4424456 },
169
193
  { url = "https://files.pythonhosted.org/packages/1d/45/5fabacbc6e76ff056f84d9f60eeac18819badf0cefc1b6612ee03d4ab678/cryptography-45.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7bedbe4cc930fa4b100fc845ea1ea5788fcd7ae9562e669989c11618ae8d76ee", size = 4198495 },
@@ -173,6 +197,8 @@ wheels = [
173
197
  { url = "https://files.pythonhosted.org/packages/3a/c0/85fa358ddb063ec588aed4a6ea1df57dc3e3bc1712d87c8fa162d02a65fc/cryptography-45.0.4-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:06509dc70dd71fa56eaa138336244e2fbaf2ac164fc9b5e66828fccfd2b680d6", size = 4451442 },
174
198
  { url = "https://files.pythonhosted.org/packages/33/67/362d6ec1492596e73da24e669a7fbbaeb1c428d6bf49a29f7a12acffd5dc/cryptography-45.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5f31e6b0a5a253f6aa49be67279be4a7e5a4ef259a9f33c69f7d1b1191939872", size = 4325038 },
175
199
  { url = "https://files.pythonhosted.org/packages/53/75/82a14bf047a96a1b13ebb47fb9811c4f73096cfa2e2b17c86879687f9027/cryptography-45.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:944e9ccf67a9594137f942d5b52c8d238b1b4e46c7a0c2891b7ae6e01e7c80a4", size = 4560964 },
200
+ { url = "https://files.pythonhosted.org/packages/cd/37/1a3cba4c5a468ebf9b95523a5ef5651244693dc712001e276682c278fc00/cryptography-45.0.4-cp37-abi3-win32.whl", hash = "sha256:c22fe01e53dc65edd1945a2e6f0015e887f84ced233acecb64b4daadb32f5c97", size = 2924557 },
201
+ { url = "https://files.pythonhosted.org/packages/2a/4b/3256759723b7e66380397d958ca07c59cfc3fb5c794fb5516758afd05d41/cryptography-45.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:627ba1bc94f6adf0b0a2e35d87020285ead22d9f648c7e75bb64f367375f3b22", size = 3395508 },
176
202
  ]
177
203
 
178
204
  [[package]]
@@ -184,6 +210,37 @@ wheels = [
184
210
  { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
185
211
  ]
186
212
 
213
+ [[package]]
214
+ name = "exceptiongroup"
215
+ version = "1.3.0"
216
+ source = { registry = "https://pypi.org/simple" }
217
+ dependencies = [
218
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
219
+ ]
220
+ sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 }
221
+ wheels = [
222
+ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 },
223
+ ]
224
+
225
+ [[package]]
226
+ name = "fastmcp"
227
+ version = "2.9.0"
228
+ source = { registry = "https://pypi.org/simple" }
229
+ dependencies = [
230
+ { name = "authlib" },
231
+ { name = "exceptiongroup" },
232
+ { name = "httpx" },
233
+ { name = "mcp" },
234
+ { name = "openapi-pydantic" },
235
+ { name = "python-dotenv" },
236
+ { name = "rich" },
237
+ { name = "typer" },
238
+ ]
239
+ sdist = { url = "https://files.pythonhosted.org/packages/2d/43/31af92e392c8e28269599fe3228419fe4bbb829b04a61fac17ab8dd2a7a9/fastmcp-2.9.0.tar.gz", hash = "sha256:3f1dc97c409193729b4aa8ad240ad396fe767a982c55c3a3e788f422b1278dd6", size = 2650412 }
240
+ wheels = [
241
+ { url = "https://files.pythonhosted.org/packages/dc/43/1095c0cbcf9f3b25d048faaf361b81b4dec403c3f75990dc93eeb6f3c3af/fastmcp-2.9.0-py3-none-any.whl", hash = "sha256:e5b00e5fcea2d216d96f7cfb8b67836bd58fc3cd57147e0d89043d905a84334f", size = 159401 },
242
+ ]
243
+
187
244
  [[package]]
188
245
  name = "h11"
189
246
  version = "0.16.0"
@@ -221,6 +278,15 @@ wheels = [
221
278
  { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
222
279
  ]
223
280
 
281
+ [[package]]
282
+ name = "httpx-sse"
283
+ version = "0.4.1"
284
+ source = { registry = "https://pypi.org/simple" }
285
+ sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998 }
286
+ wheels = [
287
+ { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054 },
288
+ ]
289
+
224
290
  [[package]]
225
291
  name = "idna"
226
292
  version = "3.10"
@@ -358,6 +424,26 @@ wheels = [
358
424
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
359
425
  ]
360
426
 
427
+ [[package]]
428
+ name = "mcp"
429
+ version = "1.9.4"
430
+ source = { registry = "https://pypi.org/simple" }
431
+ dependencies = [
432
+ { name = "anyio" },
433
+ { name = "httpx" },
434
+ { name = "httpx-sse" },
435
+ { name = "pydantic" },
436
+ { name = "pydantic-settings" },
437
+ { name = "python-multipart" },
438
+ { name = "sse-starlette" },
439
+ { name = "starlette" },
440
+ { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
441
+ ]
442
+ sdist = { url = "https://files.pythonhosted.org/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f", size = 333294 }
443
+ wheels = [
444
+ { url = "https://files.pythonhosted.org/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0", size = 130232 },
445
+ ]
446
+
361
447
  [[package]]
362
448
  name = "mdurl"
363
449
  version = "0.1.2"
@@ -417,6 +503,18 @@ wheels = [
417
503
  { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376 },
418
504
  ]
419
505
 
506
+ [[package]]
507
+ name = "openapi-pydantic"
508
+ version = "0.5.1"
509
+ source = { registry = "https://pypi.org/simple" }
510
+ dependencies = [
511
+ { name = "pydantic" },
512
+ ]
513
+ sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 }
514
+ wheels = [
515
+ { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
516
+ ]
517
+
420
518
  [[package]]
421
519
  name = "packaging"
422
520
  version = "25.0"
@@ -556,6 +654,20 @@ wheels = [
556
654
  { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 },
557
655
  ]
558
656
 
657
+ [[package]]
658
+ name = "pydantic-settings"
659
+ version = "2.10.1"
660
+ source = { registry = "https://pypi.org/simple" }
661
+ dependencies = [
662
+ { name = "pydantic" },
663
+ { name = "python-dotenv" },
664
+ { name = "typing-inspection" },
665
+ ]
666
+ sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 }
667
+ wheels = [
668
+ { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 },
669
+ ]
670
+
559
671
  [[package]]
560
672
  name = "pygments"
561
673
  version = "2.19.1"
@@ -614,6 +726,24 @@ wheels = [
614
726
  { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
615
727
  ]
616
728
 
729
+ [[package]]
730
+ name = "python-dotenv"
731
+ version = "1.1.1"
732
+ source = { registry = "https://pypi.org/simple" }
733
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 }
734
+ wheels = [
735
+ { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 },
736
+ ]
737
+
738
+ [[package]]
739
+ name = "python-multipart"
740
+ version = "0.0.20"
741
+ source = { registry = "https://pypi.org/simple" }
742
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
743
+ wheels = [
744
+ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
745
+ ]
746
+
617
747
  [[package]]
618
748
  name = "pytz"
619
749
  version = "2025.2"
@@ -724,13 +854,14 @@ wheels = [
724
854
 
725
855
  [[package]]
726
856
  name = "sqlsaber"
727
- version = "0.3.0"
857
+ version = "0.4.1"
728
858
  source = { editable = "." }
729
859
  dependencies = [
730
860
  { name = "aiomysql" },
731
861
  { name = "aiosqlite" },
732
862
  { name = "anthropic" },
733
863
  { name = "asyncpg" },
864
+ { name = "fastmcp" },
734
865
  { name = "httpx" },
735
866
  { name = "keyring" },
736
867
  { name = "pandas" },
@@ -753,6 +884,7 @@ requires-dist = [
753
884
  { name = "aiosqlite", specifier = ">=0.21.0" },
754
885
  { name = "anthropic", specifier = ">=0.54.0" },
755
886
  { name = "asyncpg", specifier = ">=0.30.0" },
887
+ { name = "fastmcp", specifier = ">=2.9.0" },
756
888
  { name = "httpx", specifier = ">=0.28.1" },
757
889
  { name = "keyring", specifier = ">=25.6.0" },
758
890
  { name = "pandas", specifier = ">=2.0.0" },
@@ -769,6 +901,31 @@ dev = [
769
901
  { name = "ruff", specifier = ">=0.12.0" },
770
902
  ]
771
903
 
904
+ [[package]]
905
+ name = "sse-starlette"
906
+ version = "2.3.6"
907
+ source = { registry = "https://pypi.org/simple" }
908
+ dependencies = [
909
+ { name = "anyio" },
910
+ ]
911
+ sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284 }
912
+ wheels = [
913
+ { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606 },
914
+ ]
915
+
916
+ [[package]]
917
+ name = "starlette"
918
+ version = "0.47.1"
919
+ source = { registry = "https://pypi.org/simple" }
920
+ dependencies = [
921
+ { name = "anyio" },
922
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
923
+ ]
924
+ sdist = { url = "https://files.pythonhosted.org/packages/0a/69/662169fdb92fb96ec3eaee218cf540a629d629c86d7993d9651226a6789b/starlette-0.47.1.tar.gz", hash = "sha256:aef012dd2b6be325ffa16698f9dc533614fb1cebd593a906b90dc1025529a79b", size = 2583072 }
925
+ wheels = [
926
+ { url = "https://files.pythonhosted.org/packages/82/95/38ef0cd7fa11eaba6a99b3c4f5ac948d8bc6ff199aabd327a29cc000840c/starlette-0.47.1-py3-none-any.whl", hash = "sha256:5e11c9f5c7c3f24959edbf2dffdc01bba860228acf657129467d8a7468591527", size = 72747 },
927
+ ]
928
+
772
929
  [[package]]
773
930
  name = "typer"
774
931
  version = "0.16.0"
@@ -814,6 +971,19 @@ wheels = [
814
971
  { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 },
815
972
  ]
816
973
 
974
+ [[package]]
975
+ name = "uvicorn"
976
+ version = "0.34.3"
977
+ source = { registry = "https://pypi.org/simple" }
978
+ dependencies = [
979
+ { name = "click" },
980
+ { name = "h11" },
981
+ ]
982
+ sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631 }
983
+ wheels = [
984
+ { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431 },
985
+ ]
986
+
817
987
  [[package]]
818
988
  name = "wcwidth"
819
989
  version = "0.2.13"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes