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 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
- async with self.get_handler(database) as handler:
244
- result = await handler.execute_query(sql)
245
- return [types.TextContent(type="text", text=result)]
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"""
@@ -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
- config = cls(
69
- path=db_config['path'],
70
- password=db_config.get('password'),
71
- uri=True
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.4.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 (when using Docker)
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
- # SQLite example (when using Docker)
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 # Mapped path inside container
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=xdeLOKv9-dbMUzlIbD-BbRVbqAf1a3RjO25aw61vAzY,9691
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=QK1JlY-ZBB5VyhydbiU-aAKNESTWMtyBfawbv2DAVCQ,2452
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.4.0.dist-info/METADATA,sha256=1c8QEdqJmgHUCjsMxJEMh1C5Qdpq0EdXWAO07yIv77k,9478
15
- mcp_dbutils-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- mcp_dbutils-0.4.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
17
- mcp_dbutils-0.4.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
18
- mcp_dbutils-0.4.0.dist-info/RECORD,,
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,,