mcp-dbutils 0.4.0__py3-none-any.whl → 0.6.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.
- mcp_dbutils/base.py +40 -14
- mcp_dbutils/sqlite/config.py +83 -7
- {mcp_dbutils-0.4.0.dist-info → mcp_dbutils-0.6.0.dist-info}/METADATA +30 -4
- {mcp_dbutils-0.4.0.dist-info → mcp_dbutils-0.6.0.dist-info}/RECORD +7 -7
- {mcp_dbutils-0.4.0.dist-info → mcp_dbutils-0.6.0.dist-info}/WHEEL +0 -0
- {mcp_dbutils-0.4.0.dist-info → mcp_dbutils-0.6.0.dist-info}/entry_points.txt +0 -0
- {mcp_dbutils-0.4.0.dist-info → mcp_dbutils-0.6.0.dist-info}/licenses/LICENSE +0 -0
mcp_dbutils/base.py
CHANGED
@@ -220,29 +220,55 @@ class DatabaseServer:
|
|
220
220
|
},
|
221
221
|
"required": ["database", "sql"]
|
222
222
|
}
|
223
|
+
),
|
224
|
+
types.Tool(
|
225
|
+
name="list_tables",
|
226
|
+
description="List all available tables in the specified database",
|
227
|
+
inputSchema={
|
228
|
+
"type": "object",
|
229
|
+
"properties": {
|
230
|
+
"database": {
|
231
|
+
"type": "string",
|
232
|
+
"description": "Database configuration name"
|
233
|
+
}
|
234
|
+
},
|
235
|
+
"required": ["database"]
|
236
|
+
}
|
223
237
|
)
|
224
238
|
]
|
225
239
|
|
226
240
|
@self.server.call_tool()
|
227
241
|
async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
228
|
-
if name != "query":
|
229
|
-
raise ConfigurationError(f"Unknown tool: {name}")
|
230
|
-
|
231
242
|
if "database" not in arguments:
|
232
243
|
raise ConfigurationError("Database configuration name must be specified")
|
233
244
|
|
234
|
-
sql = arguments.get("sql", "").strip()
|
235
|
-
if not sql:
|
236
|
-
raise ConfigurationError("SQL query cannot be empty")
|
237
|
-
|
238
|
-
# Only allow SELECT statements
|
239
|
-
if not sql.lower().startswith("select"):
|
240
|
-
raise ConfigurationError("Only SELECT queries are supported for security reasons")
|
241
|
-
|
242
245
|
database = arguments["database"]
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
+
|
247
|
+
if name == "list_tables":
|
248
|
+
async with self.get_handler(database) as handler:
|
249
|
+
tables = await handler.get_tables()
|
250
|
+
formatted_tables = "\n".join([
|
251
|
+
f"Table: {table.name}\n" +
|
252
|
+
f"URI: {table.uri}\n" +
|
253
|
+
(f"Description: {table.description}\n" if table.description else "") +
|
254
|
+
"---"
|
255
|
+
for table in tables
|
256
|
+
])
|
257
|
+
return [types.TextContent(type="text", text=formatted_tables)]
|
258
|
+
elif name == "query":
|
259
|
+
sql = arguments.get("sql", "").strip()
|
260
|
+
if not sql:
|
261
|
+
raise ConfigurationError("SQL query cannot be empty")
|
262
|
+
|
263
|
+
# Only allow SELECT statements
|
264
|
+
if not sql.lower().startswith("select"):
|
265
|
+
raise ConfigurationError("Only SELECT queries are supported for security reasons")
|
266
|
+
|
267
|
+
async with self.get_handler(database) as handler:
|
268
|
+
result = await handler.execute_query(sql)
|
269
|
+
return [types.TextContent(type="text", text=result)]
|
270
|
+
else:
|
271
|
+
raise ConfigurationError(f"Unknown tool: {name}")
|
246
272
|
|
247
273
|
async def run(self):
|
248
274
|
"""Run server"""
|
mcp_dbutils/sqlite/config.py
CHANGED
@@ -3,8 +3,50 @@
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Dict, Any, Optional, Literal
|
6
|
+
from urllib.parse import urlparse, parse_qs
|
6
7
|
from ..config import DatabaseConfig
|
7
8
|
|
9
|
+
def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
|
10
|
+
"""Parse JDBC URL into connection parameters
|
11
|
+
|
12
|
+
Args:
|
13
|
+
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/database.db or jdbc:sqlite:/path/to/database.db)
|
14
|
+
|
15
|
+
Returns:
|
16
|
+
Dictionary of connection parameters
|
17
|
+
|
18
|
+
Raises:
|
19
|
+
ValueError: If URL format is invalid
|
20
|
+
"""
|
21
|
+
if not jdbc_url.startswith('jdbc:sqlite:'):
|
22
|
+
raise ValueError("Invalid SQLite JDBC URL format")
|
23
|
+
|
24
|
+
# Remove jdbc:sqlite: prefix
|
25
|
+
url = jdbc_url[12:]
|
26
|
+
|
27
|
+
# Handle file: prefix
|
28
|
+
if url.startswith('file:'):
|
29
|
+
url = url[5:]
|
30
|
+
|
31
|
+
# Parse URL
|
32
|
+
parsed = urlparse(url)
|
33
|
+
path = parsed.path
|
34
|
+
|
35
|
+
# Extract query parameters
|
36
|
+
params = {}
|
37
|
+
if parsed.query:
|
38
|
+
query_params = parse_qs(parsed.query)
|
39
|
+
for key, values in query_params.items():
|
40
|
+
params[key] = values[0]
|
41
|
+
|
42
|
+
if not path:
|
43
|
+
raise ValueError("Database path must be specified in URL")
|
44
|
+
|
45
|
+
return {
|
46
|
+
'path': path,
|
47
|
+
'parameters': params
|
48
|
+
}
|
49
|
+
|
8
50
|
@dataclass
|
9
51
|
class SqliteConfig(DatabaseConfig):
|
10
52
|
path: str
|
@@ -12,6 +54,30 @@ class SqliteConfig(DatabaseConfig):
|
|
12
54
|
uri: bool = True # Enable URI mode to support parameters like password
|
13
55
|
type: Literal['sqlite'] = 'sqlite'
|
14
56
|
|
57
|
+
@classmethod
|
58
|
+
def from_jdbc_url(cls, jdbc_url: str, password: Optional[str] = None) -> 'SqliteConfig':
|
59
|
+
"""Create configuration from JDBC URL
|
60
|
+
|
61
|
+
Args:
|
62
|
+
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/database.db)
|
63
|
+
password: Optional password for database encryption
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
SqliteConfig instance
|
67
|
+
|
68
|
+
Raises:
|
69
|
+
ValueError: If URL format is invalid
|
70
|
+
"""
|
71
|
+
params = parse_jdbc_url(jdbc_url)
|
72
|
+
|
73
|
+
config = cls(
|
74
|
+
path=params['path'],
|
75
|
+
password=password,
|
76
|
+
uri=True
|
77
|
+
)
|
78
|
+
config.debug = cls.get_debug_mode()
|
79
|
+
return config
|
80
|
+
|
15
81
|
@property
|
16
82
|
def absolute_path(self) -> str:
|
17
83
|
"""Return absolute path to database file"""
|
@@ -62,13 +128,23 @@ class SqliteConfig(DatabaseConfig):
|
|
62
128
|
raise ValueError("Database configuration must include 'type' field")
|
63
129
|
if db_config['type'] != 'sqlite':
|
64
130
|
raise ValueError(f"Configuration is not SQLite type: {db_config['type']}")
|
65
|
-
if 'path' not in db_config:
|
66
|
-
raise ValueError("SQLite configuration must include 'path' field")
|
67
131
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
132
|
+
# Check if using JDBC URL configuration
|
133
|
+
if 'jdbc_url' in db_config:
|
134
|
+
params = parse_jdbc_url(db_config['jdbc_url'])
|
135
|
+
config = cls(
|
136
|
+
path=params['path'],
|
137
|
+
password=db_config.get('password'),
|
138
|
+
uri=True
|
139
|
+
)
|
140
|
+
else:
|
141
|
+
if 'path' not in db_config:
|
142
|
+
raise ValueError("SQLite configuration must include 'path' field")
|
143
|
+
config = cls(
|
144
|
+
path=db_config['path'],
|
145
|
+
password=db_config.get('password'),
|
146
|
+
uri=True
|
147
|
+
)
|
148
|
+
|
73
149
|
config.debug = cls.get_debug_mode()
|
74
150
|
return config
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-dbutils
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.6.0
|
4
4
|
Summary: MCP Database Utilities Service
|
5
5
|
Author: Dong Hao
|
6
6
|
License-Expression: MIT
|
@@ -148,7 +148,7 @@ The project requires a YAML configuration file, specified via the `--config` par
|
|
148
148
|
|
149
149
|
```yaml
|
150
150
|
databases:
|
151
|
-
# PostgreSQL example
|
151
|
+
# Standard PostgreSQL configuration example
|
152
152
|
my_postgres:
|
153
153
|
type: postgres
|
154
154
|
dbname: test_db
|
@@ -158,13 +158,39 @@ databases:
|
|
158
158
|
# host: 172.17.0.1 # For Linux (docker0 IP)
|
159
159
|
port: 5432
|
160
160
|
|
161
|
-
#
|
161
|
+
# PostgreSQL with JDBC URL example
|
162
|
+
my_postgres_jdbc:
|
163
|
+
type: postgres
|
164
|
+
jdbc_url: jdbc:postgresql://host.docker.internal:5432/test_db
|
165
|
+
user: postgres # Credentials must be provided separately
|
166
|
+
password: secret # Not included in JDBC URL for security
|
167
|
+
|
168
|
+
# SQLite standard configuration
|
162
169
|
my_sqlite:
|
163
170
|
type: sqlite
|
164
|
-
path: /app/sqlite.db #
|
171
|
+
path: /app/sqlite.db # Database file path
|
165
172
|
password: optional_password # optional
|
173
|
+
|
174
|
+
# SQLite with JDBC URL configuration
|
175
|
+
my_sqlite_jdbc:
|
176
|
+
type: sqlite
|
177
|
+
jdbc_url: jdbc:sqlite:/app/data.db?mode=ro&cache=shared # Supports query parameters
|
178
|
+
password: optional_password # Provided separately for security
|
166
179
|
```
|
167
180
|
|
181
|
+
The configuration supports JDBC URL format for both PostgreSQL and SQLite:
|
182
|
+
|
183
|
+
PostgreSQL:
|
184
|
+
1. Standard configuration with individual parameters
|
185
|
+
2. JDBC URL configuration with separate credentials
|
186
|
+
|
187
|
+
SQLite:
|
188
|
+
1. Standard configuration with path parameter
|
189
|
+
2. JDBC URL configuration with query parameters support:
|
190
|
+
- mode=ro: Read-only mode
|
191
|
+
- cache=shared: Shared cache mode
|
192
|
+
- Other SQLite URI parameters
|
193
|
+
|
168
194
|
### Debug Mode
|
169
195
|
Set environment variable `MCP_DEBUG=1` to enable debug mode for detailed logging output.
|
170
196
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
mcp_dbutils/__init__.py,sha256=xcfE1spAaONAoxBYB1ZyDX8tw7nxV1PMqo_RwxLDp0A,1892
|
2
|
-
mcp_dbutils/base.py,sha256=
|
2
|
+
mcp_dbutils/base.py,sha256=kKeTKW2BqiSu32VHElfDVBE1ziR9EvFw71AVbYf9Xq0,10919
|
3
3
|
mcp_dbutils/config.py,sha256=EwnPNuQVCBKd5WOXQfROyDTM-YpM_Odp0GhCPRg8YwE,1863
|
4
4
|
mcp_dbutils/log.py,sha256=fibVIwsb1HVU5zriGrDZTMEirKjgIuxuN_B_YTdAJ7I,996
|
5
5
|
mcp_dbutils/stats.py,sha256=2hiKi_M8V4xhVHlH5FS-Df5GuMEpuzif12C8ik06Khs,2538
|
@@ -8,11 +8,11 @@ mcp_dbutils/postgres/config.py,sha256=Np0GS5iUaUuWpYI8QfnyjUDy7v-vResQEAexR4W92-
|
|
8
8
|
mcp_dbutils/postgres/handler.py,sha256=JC_Qyw1s6qjVR0tdH7FLAEAs9EPl_-wgFQLryQyMq5c,6089
|
9
9
|
mcp_dbutils/postgres/server.py,sha256=_S3HF1KooxPB9gX1FedoOOGn93tHlIevCab6vjCt2TU,8715
|
10
10
|
mcp_dbutils/sqlite/__init__.py,sha256=QV4th2ywzUmCCa3GHCcBf8blJ_E8OYy0dJ2fSf1TfSU,134
|
11
|
-
mcp_dbutils/sqlite/config.py,sha256=
|
11
|
+
mcp_dbutils/sqlite/config.py,sha256=RTHT2Xx--g-osD73CpT8DrCk0VHpHfPil3D6YUzXD-g,4519
|
12
12
|
mcp_dbutils/sqlite/handler.py,sha256=bf_k93rCcJn09zc7tsqrlbiTGUg3FspimfWKxK_JQTs,4970
|
13
13
|
mcp_dbutils/sqlite/server.py,sha256=7Bbq9l7Ca_4dzkAbbdRcXxvHoO_NFLzZHwlhKB0HIJc,7724
|
14
|
-
mcp_dbutils-0.
|
15
|
-
mcp_dbutils-0.
|
16
|
-
mcp_dbutils-0.
|
17
|
-
mcp_dbutils-0.
|
18
|
-
mcp_dbutils-0.
|
14
|
+
mcp_dbutils-0.6.0.dist-info/METADATA,sha256=Dkyfy_4VUB4VO5tl6zKAljcvacBp-irM7xTxVzUjOIo,10390
|
15
|
+
mcp_dbutils-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
mcp_dbutils-0.6.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
|
17
|
+
mcp_dbutils-0.6.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
|
18
|
+
mcp_dbutils-0.6.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|