sqlsaber 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.

sqlsaber/agents/mcp.py ADDED
@@ -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
+ )
@@ -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"]
sqlsaber/mcp/mcp.py ADDED
@@ -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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlsaber
3
- Version: 0.3.0
3
+ Version: 0.4.0
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
@@ -34,6 +35,7 @@ Ask your questions in natural language and it will gather the right context and
34
35
  - 💬 Interactive REPL mode
35
36
  - 🎨 Beautiful formatted output with syntax highlighting
36
37
  - 🗄️ Support for PostgreSQL, SQLite, and MySQL
38
+ - 🔌 MCP (Model Context Protocol) server support
37
39
 
38
40
  ## Installation
39
41
 
@@ -140,6 +142,43 @@ saber query "show me the distribution of customer ages"
140
142
  saber query "which products had the highest sales growth last quarter?"
141
143
  ```
142
144
 
145
+ ## MCP Server Integration
146
+
147
+ SQLSaber includes an MCP (Model Context Protocol) server that allows AI agents like Claude Code to directly leverage tools available in SQLSaber.
148
+
149
+ ### Starting the MCP Server
150
+
151
+ Run the MCP server using uvx:
152
+
153
+ ```bash
154
+ uvx saber-mcp
155
+ ```
156
+
157
+ ### Configuring MCP Clients
158
+
159
+ #### Claude Code
160
+
161
+ Add SQLSaber as an MCP server in Claude Code:
162
+
163
+ ```bash
164
+ claude mcp add -- uvx saber-mcp
165
+ ```
166
+
167
+ #### Other MCP Clients
168
+
169
+ For other MCP clients, configure them to run the command: `uvx saber-mcp`
170
+
171
+ ### Available MCP Tools
172
+
173
+ Once connected, the MCP client will have access to these tools:
174
+
175
+ - `get_databases()` - Lists all configured databases
176
+ - `list_tables(database)` - Get all tables in a database with row counts
177
+ - `introspect_schema(database, table_pattern?)` - Get detailed schema information
178
+ - `execute_sql(database, query, limit?)` - Execute SQL queries (read-only)
179
+
180
+ The MCP server uses your existing SQLSaber database configurations, so make sure to set up your databases using `saber db add` first.
181
+
143
182
  ## How It Works
144
183
 
145
184
  SQLSaber uses an intelligent three-step process optimized for minimal token usage:
@@ -3,6 +3,7 @@ sqlsaber/__main__.py,sha256=RIHxWeWh2QvLfah-2OkhI5IJxojWfy4fXpMnVEJYvxw,78
3
3
  sqlsaber/agents/__init__.py,sha256=LWeSeEUE4BhkyAYFF3TE-fx8TtLud3oyEtyB8ojFJgo,167
4
4
  sqlsaber/agents/anthropic.py,sha256=xAjKeQSnaut-P5VBeBISbQeqdP41epDjX6MJb2ZUXWg,14060
5
5
  sqlsaber/agents/base.py,sha256=IuVyCaA7VsA92odfQS2_lYNzwIZwPxK55mL_xRewgwQ,6943
6
+ sqlsaber/agents/mcp.py,sha256=FKtXgDrPZ2-xqUYCw2baI5JzrWekXaC5fjkYW1_Mg50,827
6
7
  sqlsaber/agents/streaming.py,sha256=_EO390-FHUrL1fRCNfibtE9QuJz3LGQygbwG3CB2ViY,533
7
8
  sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
8
9
  sqlsaber/cli/commands.py,sha256=h418lgh_Xp7XEQ1xvjcDyplC2JON0-y98QMaDm6o29k,4919
@@ -18,15 +19,17 @@ sqlsaber/config/database.py,sha256=vKFOxPjVakjQhj1uoLcfzhS9ZFr6Z2F5b4MmYALQZoA,1
18
19
  sqlsaber/config/settings.py,sha256=zjQ7nS3ybcCb88Ea0tmwJox5-q0ettChZw89ZqRVpX8,3975
19
20
  sqlsaber/database/__init__.py,sha256=a_gtKRJnZVO8-fEZI7g3Z8YnGa6Nio-5Y50PgVp07ss,176
20
21
  sqlsaber/database/connection.py,sha256=s8GSFZebB8be8sVUr-N0x88-20YfkfljJFRyfoB1gH0,15154
21
- sqlsaber/database/schema.py,sha256=S3uPSXcrU3swoOPPjbXSxo1_fZ8vV3ELuPoV2GRy1ZI,27953
22
+ sqlsaber/database/schema.py,sha256=9QoH-gADzWlepq-tGz3nPU3miSUU0koWmpDaoWvz8Q0,27951
23
+ sqlsaber/mcp/__init__.py,sha256=COdWq7wauPBp5Ew8tfZItFzbcLDSEkHBJSMhxzy8C9c,112
24
+ sqlsaber/mcp/mcp.py,sha256=ACm1P1TnicjOptQgeLNhXg5xgZf4MYq2kqdfVdj6wh0,4477
22
25
  sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
23
26
  sqlsaber/memory/manager.py,sha256=ML2NEO5Z4Aw36sEI9eOvWVnjl-qT2VOTojViJAj7Seo,2777
24
27
  sqlsaber/memory/storage.py,sha256=DvZBsSPaAfk_DqrNEn86uMD-TQsWUI6rQLfNw6PSCB8,5788
25
28
  sqlsaber/models/__init__.py,sha256=RJ7p3WtuSwwpFQ1Iw4_DHV2zzCtHqIzsjJzxv8kUjUE,287
26
29
  sqlsaber/models/events.py,sha256=55m41tDwMsFxnKKA5_VLJz8iV-V4Sq3LDfta4VoutJI,737
27
30
  sqlsaber/models/types.py,sha256=3U_30n91EB3IglBTHipwiW4MqmmaA2qfshfraMZyPps,896
28
- sqlsaber-0.3.0.dist-info/METADATA,sha256=YKw06cBqX0bNZK98kJfYAES_c8VPXOhvOtIKONt1xFk,3982
29
- sqlsaber-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- sqlsaber-0.3.0.dist-info/entry_points.txt,sha256=POwcsEskUp7xQQWabrAi6Eawz4qc5eBlB3KzAiBq-Y0,124
31
- sqlsaber-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
32
- sqlsaber-0.3.0.dist-info/RECORD,,
31
+ sqlsaber-0.4.0.dist-info/METADATA,sha256=CL1mNjOLrc6VDJqE2dSrCXO5OJz9gTMxYNoYq6jtzYE,5071
32
+ sqlsaber-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
+ sqlsaber-0.4.0.dist-info/entry_points.txt,sha256=jmFo96Ylm0zIKXJBwhv_P5wQ7SXP9qdaBcnTp8iCEe8,195
34
+ sqlsaber-0.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
35
+ sqlsaber-0.4.0.dist-info/RECORD,,
@@ -1,4 +1,6 @@
1
1
  [console_scripts]
2
2
  saber = sqlsaber.cli.commands:main
3
+ saber-mcp = sqlsaber.mcp.mcp:main
3
4
  sql = sqlsaber.cli.commands:main
4
5
  sqlsaber = sqlsaber.cli.commands:main
6
+ sqlsaber-mcp = sqlsaber.mcp.mcp:main