mcp-dbutils 0.16.1__py3-none-any.whl → 0.18.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 +364 -178
- mcp_dbutils/mysql/handler.py +102 -35
- mcp_dbutils/postgres/handler.py +50 -30
- mcp_dbutils/sqlite/handler.py +124 -67
- mcp_dbutils-0.18.0.dist-info/METADATA +138 -0
- {mcp_dbutils-0.16.1.dist-info → mcp_dbutils-0.18.0.dist-info}/RECORD +9 -9
- mcp_dbutils-0.16.1.dist-info/METADATA +0 -572
- {mcp_dbutils-0.16.1.dist-info → mcp_dbutils-0.18.0.dist-info}/WHEEL +0 -0
- {mcp_dbutils-0.16.1.dist-info → mcp_dbutils-0.18.0.dist-info}/entry_points.txt +0 -0
- {mcp_dbutils-0.16.1.dist-info → mcp_dbutils-0.18.0.dist-info}/licenses/LICENSE +0 -0
mcp_dbutils/sqlite/handler.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""SQLite connection handler implementation"""
|
2
2
|
|
3
3
|
import sqlite3
|
4
|
+
import time
|
4
5
|
|
5
6
|
import mcp.types as types
|
6
7
|
|
@@ -78,28 +79,51 @@ class SQLiteHandler(ConnectionHandler):
|
|
78
79
|
async def _execute_query(self, sql: str) -> str:
|
79
80
|
"""Execute SQL query"""
|
80
81
|
try:
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
# Check if the query is a DDL statement
|
83
|
+
sql_upper = sql.strip().upper()
|
84
|
+
is_ddl = sql_upper.startswith(("CREATE", "DROP", "ALTER", "TRUNCATE"))
|
85
|
+
is_dml = sql_upper.startswith(("INSERT", "UPDATE", "DELETE"))
|
86
|
+
is_select = sql_upper.startswith("SELECT")
|
87
|
+
|
88
|
+
if not (is_select or is_ddl or is_dml):
|
89
|
+
raise ConnectionHandlerError("Only SELECT, DDL, and DML statements are allowed")
|
90
|
+
|
91
|
+
conn = sqlite3.connect(self.config.path)
|
92
|
+
cur = conn.cursor()
|
93
|
+
|
94
|
+
try:
|
95
|
+
start_time = time.time()
|
90
96
|
cur.execute(sql)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
conn.commit()
|
98
|
+
end_time = time.time()
|
99
|
+
elapsed_ms = (end_time - start_time) * 1000
|
100
|
+
self.log("debug", f"Query executed in {elapsed_ms:.2f}ms")
|
101
|
+
|
102
|
+
if is_select:
|
103
|
+
# Get column names
|
104
|
+
columns = [description[0] for description in cur.description]
|
105
|
+
# Fetch results and convert to dictionaries
|
106
|
+
results = []
|
107
|
+
for row in cur.fetchall():
|
108
|
+
# Convert each row to a dictionary
|
109
|
+
row_dict = {}
|
110
|
+
for i, col_name in enumerate(columns):
|
111
|
+
row_dict[col_name] = row[i]
|
112
|
+
results.append(row_dict)
|
100
113
|
|
101
|
-
|
102
|
-
|
114
|
+
return str({
|
115
|
+
"columns": columns,
|
116
|
+
"rows": results
|
117
|
+
})
|
118
|
+
else:
|
119
|
+
# For DDL/DML statements
|
120
|
+
return "Query executed successfully"
|
121
|
+
except sqlite3.Error as e:
|
122
|
+
self.log("error", f"Query error: {str(e)}")
|
123
|
+
raise ConnectionHandlerError(str(e))
|
124
|
+
finally:
|
125
|
+
cur.close()
|
126
|
+
conn.close()
|
103
127
|
except sqlite3.Error as e:
|
104
128
|
error_msg = f"[{self.db_type}] Query execution failed: {str(e)}"
|
105
129
|
raise ConnectionHandlerError(error_msg)
|
@@ -112,13 +136,13 @@ class SQLiteHandler(ConnectionHandler):
|
|
112
136
|
# 获取表信息
|
113
137
|
cur.execute(f"PRAGMA table_info({table_name})")
|
114
138
|
columns = cur.fetchall()
|
115
|
-
|
139
|
+
|
116
140
|
# SQLite不支持表级注释,但我们可以获取表的详细信息
|
117
141
|
description = [
|
118
142
|
f"Table: {table_name}\n",
|
119
143
|
COLUMNS_HEADER
|
120
144
|
]
|
121
|
-
|
145
|
+
|
122
146
|
for col in columns:
|
123
147
|
col_info = [
|
124
148
|
f" {col[1]} ({col[2]})",
|
@@ -128,9 +152,9 @@ class SQLiteHandler(ConnectionHandler):
|
|
128
152
|
]
|
129
153
|
description.extend(col_info)
|
130
154
|
description.append("") # Empty line between columns
|
131
|
-
|
155
|
+
|
132
156
|
return "\n".join(description)
|
133
|
-
|
157
|
+
|
134
158
|
except sqlite3.Error as e:
|
135
159
|
error_msg = f"Failed to get table description: {str(e)}"
|
136
160
|
self.stats.record_error(e.__class__.__name__)
|
@@ -144,25 +168,25 @@ class SQLiteHandler(ConnectionHandler):
|
|
144
168
|
# SQLite provides the complete CREATE statement
|
145
169
|
cur.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
146
170
|
result = cur.fetchone()
|
147
|
-
|
171
|
+
|
148
172
|
if not result:
|
149
173
|
return f"Table {table_name} not found"
|
150
|
-
|
174
|
+
|
151
175
|
ddl = result[0]
|
152
|
-
|
176
|
+
|
153
177
|
# Get indexes
|
154
178
|
cur.execute("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name=?", (table_name,))
|
155
179
|
indexes = cur.fetchall()
|
156
|
-
|
180
|
+
|
157
181
|
# Add index definitions
|
158
182
|
if indexes:
|
159
183
|
ddl = ddl + "\n\n-- Indexes:"
|
160
184
|
for idx in indexes:
|
161
185
|
if idx[0]: # Some internal indexes might have NULL sql
|
162
186
|
ddl = ddl + "\n" + idx[0] + ";"
|
163
|
-
|
187
|
+
|
164
188
|
return ddl
|
165
|
-
|
189
|
+
|
166
190
|
except sqlite3.Error as e:
|
167
191
|
error_msg = f"Failed to get table DDL: {str(e)}"
|
168
192
|
self.stats.record_error(e.__class__.__name__)
|
@@ -173,43 +197,49 @@ class SQLiteHandler(ConnectionHandler):
|
|
173
197
|
try:
|
174
198
|
with sqlite3.connect(self.config.path) as conn:
|
175
199
|
cur = conn.cursor()
|
200
|
+
|
201
|
+
# Check if table exists
|
202
|
+
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
203
|
+
if not cur.fetchone():
|
204
|
+
raise ConnectionHandlerError(f"Table '{table_name}' doesn't exist")
|
205
|
+
|
176
206
|
# 获取索引列表
|
177
207
|
cur.execute(f"PRAGMA index_list({table_name})")
|
178
208
|
indexes = cur.fetchall()
|
179
|
-
|
209
|
+
|
180
210
|
if not indexes:
|
181
211
|
return f"No indexes found on table {table_name}"
|
182
|
-
|
212
|
+
|
183
213
|
formatted_indexes = [f"Indexes for {table_name}:"]
|
184
|
-
|
214
|
+
|
185
215
|
for idx in indexes:
|
186
216
|
# 获取索引详细信息
|
187
217
|
cur.execute(f"PRAGMA index_info({idx[1]})")
|
188
218
|
index_info = cur.fetchall()
|
189
|
-
|
219
|
+
|
190
220
|
# 获取索引的SQL定义
|
191
221
|
cur.execute("SELECT sql FROM sqlite_master WHERE type='index' AND name=?", (idx[1],))
|
192
222
|
sql = cur.fetchone()
|
193
|
-
|
223
|
+
|
194
224
|
index_details = [
|
195
225
|
f"\nIndex: {idx[1]}",
|
196
226
|
f"Type: {'UNIQUE' if idx[2] else 'INDEX'}",
|
197
227
|
COLUMNS_HEADER
|
198
228
|
]
|
199
|
-
|
229
|
+
|
200
230
|
for col in index_info:
|
201
231
|
index_details.append(f" - {col[2]}")
|
202
|
-
|
232
|
+
|
203
233
|
if sql and sql[0]:
|
204
234
|
index_details.extend([
|
205
235
|
"Definition:",
|
206
236
|
f" {sql[0]}"
|
207
237
|
])
|
208
|
-
|
238
|
+
|
209
239
|
formatted_indexes.extend(index_details)
|
210
|
-
|
240
|
+
|
211
241
|
return "\n".join(formatted_indexes)
|
212
|
-
|
242
|
+
|
213
243
|
except sqlite3.Error as e:
|
214
244
|
error_msg = f"Failed to get index information: {str(e)}"
|
215
245
|
self.stats.record_error(e.__class__.__name__)
|
@@ -220,28 +250,33 @@ class SQLiteHandler(ConnectionHandler):
|
|
220
250
|
try:
|
221
251
|
with sqlite3.connect(self.config.path) as conn:
|
222
252
|
cur = conn.cursor()
|
223
|
-
|
253
|
+
|
254
|
+
# Check if table exists
|
255
|
+
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
|
256
|
+
if not cur.fetchone():
|
257
|
+
raise ConnectionHandlerError(f"Table '{table_name}' doesn't exist")
|
258
|
+
|
224
259
|
# Get basic table information
|
225
260
|
cur.execute(f"PRAGMA table_info({table_name})")
|
226
261
|
columns = cur.fetchall()
|
227
|
-
|
262
|
+
|
228
263
|
# Count rows
|
229
264
|
cur.execute(f"SELECT COUNT(*) FROM {table_name}")
|
230
265
|
row_count = cur.fetchone()[0]
|
231
|
-
|
266
|
+
|
232
267
|
# Get index information
|
233
268
|
cur.execute(f"PRAGMA index_list({table_name})")
|
234
269
|
indexes = cur.fetchall()
|
235
|
-
|
270
|
+
|
236
271
|
# Get page count and size
|
237
272
|
cur.execute("PRAGMA page_count")
|
238
273
|
page_count = cur.fetchone()[0]
|
239
274
|
cur.execute("PRAGMA page_size")
|
240
275
|
page_size = cur.fetchone()[0]
|
241
|
-
|
276
|
+
|
242
277
|
# Calculate total size
|
243
278
|
total_size = page_count * page_size
|
244
|
-
|
279
|
+
|
245
280
|
# Format size in human readable format
|
246
281
|
def format_size(size):
|
247
282
|
for unit in ['B', 'KB', 'MB', 'GB']:
|
@@ -249,7 +284,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
249
284
|
return f"{size:.2f} {unit}"
|
250
285
|
size /= 1024
|
251
286
|
return f"{size:.2f} TB"
|
252
|
-
|
287
|
+
|
253
288
|
# Get column statistics
|
254
289
|
column_stats = []
|
255
290
|
for col in columns:
|
@@ -260,7 +295,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
260
295
|
# Get distinct value count
|
261
296
|
cur.execute(f"SELECT COUNT(DISTINCT {col_name}) FROM {table_name}")
|
262
297
|
distinct_count = cur.fetchone()[0]
|
263
|
-
|
298
|
+
|
264
299
|
column_stats.append({
|
265
300
|
'name': col_name,
|
266
301
|
'type': col[2],
|
@@ -268,7 +303,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
268
303
|
'null_percent': (null_count / row_count * 100) if row_count > 0 else 0,
|
269
304
|
'distinct_count': distinct_count
|
270
305
|
})
|
271
|
-
|
306
|
+
|
272
307
|
# Format output
|
273
308
|
output = [
|
274
309
|
f"Table Statistics for {table_name}:",
|
@@ -279,7 +314,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
279
314
|
f" Index Count: {len(indexes)}\n",
|
280
315
|
"Column Statistics:"
|
281
316
|
]
|
282
|
-
|
317
|
+
|
283
318
|
for stat in column_stats:
|
284
319
|
col_info = [
|
285
320
|
f" {stat['name']} ({stat['type']}):",
|
@@ -288,7 +323,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
288
323
|
]
|
289
324
|
output.extend(col_info)
|
290
325
|
output.append("") # Empty line between columns
|
291
|
-
|
326
|
+
|
292
327
|
return "\n".join(output)
|
293
328
|
|
294
329
|
except sqlite3.Error as e:
|
@@ -301,21 +336,21 @@ class SQLiteHandler(ConnectionHandler):
|
|
301
336
|
try:
|
302
337
|
with sqlite3.connect(self.config.path) as conn:
|
303
338
|
cur = conn.cursor()
|
304
|
-
|
339
|
+
|
305
340
|
# Get table info (includes PRIMARY KEY)
|
306
341
|
cur.execute(f"PRAGMA table_info({table_name})")
|
307
342
|
columns = cur.fetchall()
|
308
|
-
|
343
|
+
|
309
344
|
# Get foreign keys
|
310
345
|
cur.execute(f"PRAGMA foreign_key_list({table_name})")
|
311
346
|
foreign_keys = cur.fetchall()
|
312
|
-
|
347
|
+
|
313
348
|
# Get indexes (for UNIQUE constraints)
|
314
349
|
cur.execute(f"PRAGMA index_list({table_name})")
|
315
350
|
indexes = cur.fetchall()
|
316
|
-
|
351
|
+
|
317
352
|
output = [f"Constraints for {table_name}:"]
|
318
|
-
|
353
|
+
|
319
354
|
# Primary Key constraints
|
320
355
|
pk_columns = [col[1] for col in columns if col[5]] # col[5] is pk flag
|
321
356
|
if pk_columns:
|
@@ -323,13 +358,13 @@ class SQLiteHandler(ConnectionHandler):
|
|
323
358
|
"\nPrimary Key Constraints:",
|
324
359
|
f" PRIMARY KEY ({', '.join(pk_columns)})"
|
325
360
|
])
|
326
|
-
|
361
|
+
|
327
362
|
# Foreign Key constraints
|
328
363
|
if foreign_keys:
|
329
364
|
output.append("\nForeign Key Constraints:")
|
330
365
|
current_fk = None
|
331
366
|
fk_columns = []
|
332
|
-
|
367
|
+
|
333
368
|
for fk in foreign_keys:
|
334
369
|
# SQLite foreign_key_list format:
|
335
370
|
# id, seq, table, from, to, on_update, on_delete, match
|
@@ -345,10 +380,10 @@ class SQLiteHandler(ConnectionHandler):
|
|
345
380
|
output.append(f" ON UPDATE: {fk[5]}")
|
346
381
|
if fk[6]: # on_delete
|
347
382
|
output.append(f" ON DELETE: {fk[6]}")
|
348
|
-
|
383
|
+
|
349
384
|
if fk_columns:
|
350
385
|
output.append(f" ({', '.join(fk_columns)})")
|
351
|
-
|
386
|
+
|
352
387
|
# Unique constraints (from indexes)
|
353
388
|
unique_indexes = [idx for idx in indexes if idx[2]] # idx[2] is unique flag
|
354
389
|
if unique_indexes:
|
@@ -359,7 +394,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
359
394
|
index_info = cur.fetchall()
|
360
395
|
columns = [info[2] for info in index_info] # info[2] is column name
|
361
396
|
output.append(f" UNIQUE ({', '.join(columns)})")
|
362
|
-
|
397
|
+
|
363
398
|
# Check constraints
|
364
399
|
# Note: SQLite doesn't provide direct access to CHECK constraints through PRAGMA
|
365
400
|
# We need to parse the table creation SQL
|
@@ -368,7 +403,7 @@ class SQLiteHandler(ConnectionHandler):
|
|
368
403
|
if "CHECK" in create_sql.upper():
|
369
404
|
output.append("\nCheck Constraints:")
|
370
405
|
output.append(" See table DDL for CHECK constraints")
|
371
|
-
|
406
|
+
|
372
407
|
return "\n".join(output)
|
373
408
|
|
374
409
|
except sqlite3.Error as e:
|
@@ -381,11 +416,18 @@ class SQLiteHandler(ConnectionHandler):
|
|
381
416
|
try:
|
382
417
|
with sqlite3.connect(self.config.path) as conn:
|
383
418
|
cur = conn.cursor()
|
384
|
-
|
419
|
+
|
420
|
+
# Check if the query is valid by preparing it
|
421
|
+
try:
|
422
|
+
# Use prepare to validate the query without executing it
|
423
|
+
conn.execute(f"EXPLAIN {sql}")
|
424
|
+
except sqlite3.Error as e:
|
425
|
+
raise ConnectionHandlerError(f"Failed to explain query: {str(e)}")
|
426
|
+
|
385
427
|
# Get EXPLAIN output
|
386
428
|
cur.execute(f"EXPLAIN QUERY PLAN {sql}")
|
387
429
|
plan = cur.fetchall()
|
388
|
-
|
430
|
+
|
389
431
|
# Format the output
|
390
432
|
output = [
|
391
433
|
"Query Execution Plan:",
|
@@ -393,20 +435,20 @@ class SQLiteHandler(ConnectionHandler):
|
|
393
435
|
"Details:",
|
394
436
|
"--------"
|
395
437
|
]
|
396
|
-
|
438
|
+
|
397
439
|
for step in plan:
|
398
440
|
# EXPLAIN QUERY PLAN format:
|
399
441
|
# id | parent | notused | detail
|
400
442
|
indent = " " * (step[0] - step[1] if step[1] >= 0 else step[0])
|
401
443
|
output.append(f"{indent}{step[3]}")
|
402
|
-
|
444
|
+
|
403
445
|
# Add query statistics
|
404
446
|
output.extend([
|
405
447
|
"\nNote: SQLite's EXPLAIN QUERY PLAN provides a high-level overview.",
|
406
448
|
"For detailed execution statistics, consider using EXPLAIN (not QUERY PLAN)",
|
407
449
|
"which shows the virtual machine instructions."
|
408
450
|
])
|
409
|
-
|
451
|
+
|
410
452
|
return "\n".join(output)
|
411
453
|
|
412
454
|
except sqlite3.Error as e:
|
@@ -414,6 +456,21 @@ class SQLiteHandler(ConnectionHandler):
|
|
414
456
|
self.stats.record_error(e.__class__.__name__)
|
415
457
|
raise ConnectionHandlerError(error_msg)
|
416
458
|
|
459
|
+
async def test_connection(self) -> bool:
|
460
|
+
"""Test database connection
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
bool: True if connection is successful, False otherwise
|
464
|
+
"""
|
465
|
+
try:
|
466
|
+
with sqlite3.connect(self.config.path) as conn:
|
467
|
+
cur = conn.cursor()
|
468
|
+
cur.execute("SELECT 1")
|
469
|
+
return True
|
470
|
+
except sqlite3.Error as e:
|
471
|
+
self.log("error", f"Connection test failed: {str(e)}")
|
472
|
+
return False
|
473
|
+
|
417
474
|
async def cleanup(self):
|
418
475
|
"""Cleanup resources"""
|
419
476
|
# Log final stats
|
@@ -0,0 +1,138 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: mcp-dbutils
|
3
|
+
Version: 0.18.0
|
4
|
+
Summary: MCP Database Utilities Service
|
5
|
+
Author: Dong Hao
|
6
|
+
License-Expression: MIT
|
7
|
+
License-File: LICENSE
|
8
|
+
Requires-Python: >=3.10
|
9
|
+
Requires-Dist: mcp>=1.2.1
|
10
|
+
Requires-Dist: mysql-connector-python>=8.2.0
|
11
|
+
Requires-Dist: psycopg2-binary>=2.9.10
|
12
|
+
Requires-Dist: python-dotenv>=1.0.1
|
13
|
+
Requires-Dist: pyyaml>=6.0.2
|
14
|
+
Provides-Extra: test
|
15
|
+
Requires-Dist: aiosqlite>=0.19.0; extra == 'test'
|
16
|
+
Requires-Dist: docker>=7.0.0; extra == 'test'
|
17
|
+
Requires-Dist: pre-commit>=3.6.0; extra == 'test'
|
18
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
|
19
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
|
20
|
+
Requires-Dist: pytest-docker>=2.0.0; extra == 'test'
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
22
|
+
Requires-Dist: ruff>=0.3.0; extra == 'test'
|
23
|
+
Requires-Dist: testcontainers>=3.7.0; extra == 'test'
|
24
|
+
Description-Content-Type: text/markdown
|
25
|
+
|
26
|
+
# MCP 数据库工具
|
27
|
+
|
28
|
+
<!-- 项目状态徽章 -->
|
29
|
+
[](https://github.com/donghao1393/mcp-dbutils/actions)
|
30
|
+
[](https://github.com/donghao1393/mcp-dbutils/actions)
|
31
|
+
[](https://sonarcloud.io/dashboard?id=donghao1393_mcp-dbutils)
|
32
|
+
|
33
|
+
<!-- 版本和安装徽章 -->
|
34
|
+
[](https://pypi.org/project/mcp-dbutils/)
|
35
|
+
[](https://pypi.org/project/mcp-dbutils/)
|
36
|
+
[](https://smithery.ai/server/@donghao1393/mcp-dbutils)
|
37
|
+
|
38
|
+
<!-- 技术规格徽章 -->
|
39
|
+
[](https://www.python.org/)
|
40
|
+
[](LICENSE)
|
41
|
+
[](https://github.com/donghao1393/mcp-dbutils/stargazers)
|
42
|
+
|
43
|
+
[English](README_EN.md) | [文档导航](#文档导航)
|
44
|
+
|
45
|
+

|
46
|
+
|
47
|
+
## 简介
|
48
|
+
|
49
|
+
MCP Database Utilities 是一个多功能的 MCP 服务,它使您的 AI 能够通过统一的连接配置安全地访问各种类型的数据库(SQLite、MySQL、PostgreSQL 等)进行数据分析。
|
50
|
+
|
51
|
+
您可以将其视为 AI 系统和数据库之间的安全桥梁,允许 AI 在不直接访问数据库或冒数据修改风险的情况下读取和分析您的数据。
|
52
|
+
|
53
|
+
### 核心特性
|
54
|
+
|
55
|
+
- **安全优先**:严格只读操作,无直接数据库访问,隔离连接,按需连接,自动超时
|
56
|
+
- **隐私保障**:本地处理,最小数据暴露,凭证保护,敏感数据屏蔽
|
57
|
+
- **多数据库支持**:使用相同的接口连接 SQLite、MySQL、PostgreSQL
|
58
|
+
- **简单配置**:所有数据库连接使用单个 YAML 文件
|
59
|
+
- **高级功能**:表格浏览、架构分析和查询执行
|
60
|
+
|
61
|
+
> 🔒 **安全说明**:MCP 数据库工具采用安全优先的架构设计,非常适合注重数据保护的企业、初创公司和个人用户。详细了解我们的[安全架构](docs/zh/technical/security.md)。
|
62
|
+
|
63
|
+
## 快速入门
|
64
|
+
|
65
|
+
我们提供了多种安装方式,包括 uvx、Docker 和 Smithery。详细的安装和配置步骤请参阅[安装指南](docs/zh/installation.md)。
|
66
|
+
|
67
|
+
### 基本步骤
|
68
|
+
|
69
|
+
1. **安装**:选择适合您的安装方式([详细说明](docs/zh/installation.md))
|
70
|
+
2. **配置**:创建包含数据库连接信息的 YAML 文件([配置指南](docs/zh/configuration.md))
|
71
|
+
3. **连接**:将配置添加到您的 AI 客户端
|
72
|
+
4. **使用**:开始与您的数据库交互([使用指南](docs/zh/usage.md))
|
73
|
+
|
74
|
+
### 示例交互
|
75
|
+
|
76
|
+
**您**:"能否列出我的数据库中的所有表?"
|
77
|
+
|
78
|
+
**AI**:"以下是您的数据库中的表:
|
79
|
+
- customers(客户)
|
80
|
+
- products(产品)
|
81
|
+
- orders(订单)
|
82
|
+
- inventory(库存)"
|
83
|
+
|
84
|
+
**您**:"customers 表的结构是什么样的?"
|
85
|
+
|
86
|
+
**AI**:"customers 表有以下结构:
|
87
|
+
- id(整数,主键)
|
88
|
+
- name(文本)
|
89
|
+
- email(文本)
|
90
|
+
- registration_date(日期)"
|
91
|
+
|
92
|
+
## 文档导航
|
93
|
+
|
94
|
+
### 入门指南
|
95
|
+
- [安装指南](docs/zh/installation.md) - 详细的安装步骤和配置说明
|
96
|
+
- [平台特定安装指南](docs/zh/installation-platform-specific.md) - 针对不同操作系统的安装说明
|
97
|
+
- [配置指南](docs/zh/configuration.md) - 数据库连接配置示例和最佳实践
|
98
|
+
- [使用指南](docs/zh/usage.md) - 基本操作流程和常见使用场景
|
99
|
+
|
100
|
+
### 技术文档
|
101
|
+
- [架构设计](docs/zh/technical/architecture.md) - 系统架构和组件说明
|
102
|
+
- [安全架构](docs/zh/technical/security.md) - 安全特性和保护机制
|
103
|
+
- [开发指南](docs/zh/technical/development.md) - 代码质量和开发流程
|
104
|
+
- [测试指南](docs/zh/technical/testing.md) - 测试框架和最佳实践
|
105
|
+
- [SonarCloud 集成](docs/zh/technical/sonarcloud-integration.md) - SonarCloud 与 AI 集成指南
|
106
|
+
|
107
|
+
### 示例文档
|
108
|
+
- [SQLite 示例](docs/zh/examples/sqlite-examples.md) - SQLite 数据库操作示例
|
109
|
+
- [PostgreSQL 示例](docs/zh/examples/postgresql-examples.md) - PostgreSQL 数据库操作示例
|
110
|
+
- [MySQL 示例](docs/zh/examples/mysql-examples.md) - MySQL 数据库操作示例
|
111
|
+
- [高级 LLM 交互示例](docs/zh/examples/advanced-llm-interactions.md) - 与各类 LLM 的高级交互示例
|
112
|
+
|
113
|
+
### 支持与反馈
|
114
|
+
- [GitHub Issues](https://github.com/donghao1393/mcp-dbutils/issues) - 报告问题或请求功能
|
115
|
+
- [Smithery](https://smithery.ai/server/@donghao1393/mcp-dbutils) - 简化安装和更新
|
116
|
+
|
117
|
+
## 可用工具
|
118
|
+
|
119
|
+
MCP 数据库工具提供了多种工具,使 AI 能够与您的数据库交互:
|
120
|
+
|
121
|
+
- **dbutils-list-connections**:列出配置中的所有可用数据库连接
|
122
|
+
- **dbutils-list-tables**:列出数据库中的所有表
|
123
|
+
- **dbutils-run-query**:执行 SQL 查询(仅 SELECT)
|
124
|
+
- **dbutils-get-stats**:获取表统计信息
|
125
|
+
- **dbutils-list-constraints**:列出表约束
|
126
|
+
- **dbutils-explain-query**:获取查询执行计划
|
127
|
+
- **dbutils-get-performance**:获取数据库性能指标
|
128
|
+
- **dbutils-analyze-query**:分析查询以进行优化
|
129
|
+
|
130
|
+
有关这些工具的详细说明和使用示例,请参阅[使用指南](docs/zh/usage.md)。
|
131
|
+
|
132
|
+
## 星标历史
|
133
|
+
|
134
|
+
[](https://starchart.cc/donghao1393/mcp-dbutils)
|
135
|
+
|
136
|
+
## 许可证
|
137
|
+
|
138
|
+
本项目采用 MIT 许可证 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。
|
@@ -1,22 +1,22 @@
|
|
1
1
|
mcp_dbutils/__init__.py,sha256=6LLccQv7je2L4IpY_I3OzSJZcK32VUDJv2IY31y6eYg,1900
|
2
|
-
mcp_dbutils/base.py,sha256=
|
2
|
+
mcp_dbutils/base.py,sha256=ptQfJwzYKteGokOrrkIcG3QIlz-f1Afk_XxyEHYcFLI,35957
|
3
3
|
mcp_dbutils/config.py,sha256=bmXpOd1fyYfoyUS75I035ChT6t3wP5AyEnJ06e2ZS2o,1848
|
4
4
|
mcp_dbutils/log.py,sha256=mqxi6I_IL-MF1F_pxBtnYZQKOHbGBJ74gsvZHVelr1w,823
|
5
5
|
mcp_dbutils/stats.py,sha256=wMqWPfGnEOg9v5YBtTsARV-1YsFUMM_pKdzitzSU9x4,7137
|
6
6
|
mcp_dbutils/mysql/__init__.py,sha256=gNhoHaxK1qhvMAH5AVl1vfV1rUpcbV9KZWUQb41aaQk,129
|
7
7
|
mcp_dbutils/mysql/config.py,sha256=BTPPFqlhoTp7EBFIeLJZh8x6bCn3q9NivHYz9yZHziw,9820
|
8
|
-
mcp_dbutils/mysql/handler.py,sha256=
|
8
|
+
mcp_dbutils/mysql/handler.py,sha256=L2COmWts8WNQaI7wZSPtQ-BqKnpYL7DRnNk7Yw3UsxU,22285
|
9
9
|
mcp_dbutils/mysql/server.py,sha256=1bWAu7qHYXVeTZu4wdEpS6gSVB0RoXKI3Smy_ix-y8A,8586
|
10
10
|
mcp_dbutils/postgres/__init__.py,sha256=-2zYuEJEQ2AMvmGhH5Z_umerSvt7S4xOa_XV4wgvGfI,154
|
11
11
|
mcp_dbutils/postgres/config.py,sha256=NyQOVhkXJ1S-JD0w-ePNjTKI1Ja-aZQkDUdHi6U7Vl4,7752
|
12
|
-
mcp_dbutils/postgres/handler.py,sha256=
|
12
|
+
mcp_dbutils/postgres/handler.py,sha256=JKnh3QsF5oQ4nS-OdhMC1nWtTp235PArFncFN4pmDZ8,24670
|
13
13
|
mcp_dbutils/postgres/server.py,sha256=_CiJC9PitpI1NB99Q1Bcs5TYADNgDpYMwv88fRHQunE,8640
|
14
14
|
mcp_dbutils/sqlite/__init__.py,sha256=fK_3-WylCBYpBAzwuopi8hlwoIGJm2TPAlwcPWG46I0,134
|
15
15
|
mcp_dbutils/sqlite/config.py,sha256=j67TJ8mQJ2D886MthSa-zYMtvUUYyyxYLMlNxkYoqZE,4509
|
16
|
-
mcp_dbutils/sqlite/handler.py,sha256=
|
16
|
+
mcp_dbutils/sqlite/handler.py,sha256=nCgBeBp3zjpE2HNohMbh3Jpz5tNeMczt5K87JOhVWzY,19320
|
17
17
|
mcp_dbutils/sqlite/server.py,sha256=jqpE8d9vJETMs5xYGB7P0tvNDPes6Yn5ZM_iCCF7Tv4,7181
|
18
|
-
mcp_dbutils-0.
|
19
|
-
mcp_dbutils-0.
|
20
|
-
mcp_dbutils-0.
|
21
|
-
mcp_dbutils-0.
|
22
|
-
mcp_dbutils-0.
|
18
|
+
mcp_dbutils-0.18.0.dist-info/METADATA,sha256=z41vH5CzRdtLPcbzjYJ8HukwkFCPDY4ifAmmfrTh1no,6695
|
19
|
+
mcp_dbutils-0.18.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
20
|
+
mcp_dbutils-0.18.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
|
21
|
+
mcp_dbutils-0.18.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
|
22
|
+
mcp_dbutils-0.18.0.dist-info/RECORD,,
|