mcp-dbutils 0.2.4__py3-none-any.whl → 0.2.8__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 +68 -15
- mcp_dbutils/postgres/handler.py +12 -12
- mcp_dbutils/sqlite/handler.py +19 -8
- mcp_dbutils/stats.py +85 -0
- {mcp_dbutils-0.2.4.dist-info → mcp_dbutils-0.2.8.dist-info}/METADATA +37 -6
- {mcp_dbutils-0.2.4.dist-info → mcp_dbutils-0.2.8.dist-info}/RECORD +9 -8
- {mcp_dbutils-0.2.4.dist-info → mcp_dbutils-0.2.8.dist-info}/WHEEL +0 -0
- {mcp_dbutils-0.2.4.dist-info → mcp_dbutils-0.2.8.dist-info}/entry_points.txt +0 -0
- {mcp_dbutils-0.2.4.dist-info → mcp_dbutils-0.2.8.dist-info}/licenses/LICENSE +0 -0
mcp_dbutils/base.py
CHANGED
@@ -1,13 +1,27 @@
|
|
1
1
|
"""Database server base class"""
|
2
2
|
|
3
|
+
class DatabaseError(Exception):
|
4
|
+
"""Base exception for database errors"""
|
5
|
+
pass
|
6
|
+
|
7
|
+
class ConfigurationError(DatabaseError):
|
8
|
+
"""Configuration related errors"""
|
9
|
+
pass
|
10
|
+
|
11
|
+
class ConnectionError(DatabaseError):
|
12
|
+
"""Connection related errors"""
|
13
|
+
pass
|
14
|
+
|
3
15
|
from abc import ABC, abstractmethod
|
4
16
|
from typing import Any, List, Optional, AsyncContextManager
|
5
17
|
from contextlib import asynccontextmanager
|
18
|
+
import json
|
6
19
|
import yaml
|
7
20
|
from mcp.server import Server, NotificationOptions
|
8
21
|
import mcp.server.stdio
|
9
22
|
import mcp.types as types
|
10
23
|
from .log import create_logger
|
24
|
+
from .stats import ResourceStats
|
11
25
|
|
12
26
|
class DatabaseHandler(ABC):
|
13
27
|
"""Abstract base class defining common interface for database handlers"""
|
@@ -24,6 +38,7 @@ class DatabaseHandler(ABC):
|
|
24
38
|
self.database = database
|
25
39
|
self.debug = debug
|
26
40
|
self.log = create_logger(f"db-handler-{database}", debug)
|
41
|
+
self.stats = ResourceStats()
|
27
42
|
|
28
43
|
@abstractmethod
|
29
44
|
async def get_tables(self) -> list[types.Resource]:
|
@@ -36,9 +51,22 @@ class DatabaseHandler(ABC):
|
|
36
51
|
pass
|
37
52
|
|
38
53
|
@abstractmethod
|
54
|
+
async def _execute_query(self, sql: str) -> str:
|
55
|
+
"""Internal query execution method to be implemented by subclasses"""
|
56
|
+
pass
|
57
|
+
|
39
58
|
async def execute_query(self, sql: str) -> str:
|
40
59
|
"""Execute SQL query"""
|
41
|
-
|
60
|
+
try:
|
61
|
+
self.stats.record_query()
|
62
|
+
result = await self._execute_query(sql)
|
63
|
+
self.stats.update_memory_usage(result)
|
64
|
+
self.log("info", f"Resource stats: {json.dumps(self.stats.to_dict())}")
|
65
|
+
return result
|
66
|
+
except Exception as e:
|
67
|
+
self.stats.record_error(e.__class__.__name__)
|
68
|
+
self.log("error", f"Query error - {str(e)}\nResource stats: {json.dumps(self.stats.to_dict())}")
|
69
|
+
raise
|
42
70
|
|
43
71
|
@abstractmethod
|
44
72
|
async def cleanup(self):
|
@@ -57,9 +85,18 @@ class DatabaseServer:
|
|
57
85
|
"""
|
58
86
|
self.config_path = config_path
|
59
87
|
self.debug = debug
|
60
|
-
self.
|
88
|
+
self.logger = create_logger("db-server", debug)
|
61
89
|
self.server = Server("database-server")
|
62
90
|
self._setup_handlers()
|
91
|
+
self._setup_prompts()
|
92
|
+
|
93
|
+
def _setup_prompts(self):
|
94
|
+
"""Setup prompts handlers"""
|
95
|
+
async def handle_list_prompts() -> list[types.Prompt]:
|
96
|
+
"""Return empty list of prompts for now"""
|
97
|
+
return []
|
98
|
+
|
99
|
+
self.server.request_handlers["prompts/list"] = handle_list_prompts
|
63
100
|
|
64
101
|
@asynccontextmanager
|
65
102
|
async def get_handler(self, database: str) -> AsyncContextManager[DatabaseHandler]:
|
@@ -77,28 +114,42 @@ class DatabaseServer:
|
|
77
114
|
with open(self.config_path, 'r') as f:
|
78
115
|
config = yaml.safe_load(f)
|
79
116
|
if not config or 'databases' not in config:
|
80
|
-
raise
|
117
|
+
raise ConfigurationError("Configuration file must contain 'databases' section")
|
81
118
|
if database not in config['databases']:
|
82
119
|
available_dbs = list(config['databases'].keys())
|
83
|
-
raise
|
120
|
+
raise ConfigurationError(f"Database configuration not found: {database}. Available configurations: {available_dbs}")
|
84
121
|
|
85
122
|
db_config = config['databases'][database]
|
86
123
|
|
87
124
|
handler = None
|
88
125
|
try:
|
89
|
-
|
90
|
-
|
126
|
+
if 'type' not in db_config:
|
127
|
+
raise ConfigurationError("Database configuration must include 'type' field")
|
128
|
+
|
129
|
+
db_type = db_config['type']
|
130
|
+
self.logger("debug", f"Creating handler for database type: {db_type}")
|
131
|
+
if db_type == 'sqlite':
|
91
132
|
from .sqlite.handler import SqliteHandler
|
92
133
|
handler = SqliteHandler(self.config_path, database, self.debug)
|
93
|
-
elif
|
134
|
+
elif db_type == 'postgres':
|
94
135
|
from .postgres.handler import PostgresHandler
|
95
136
|
handler = PostgresHandler(self.config_path, database, self.debug)
|
96
137
|
else:
|
97
|
-
raise
|
138
|
+
raise ConfigurationError(f"Unsupported database type: {db_type}")
|
98
139
|
|
140
|
+
handler.stats.record_connection_start()
|
141
|
+
self.logger("debug", f"Handler created successfully for {database}")
|
142
|
+
self.logger("info", f"Resource stats: {json.dumps(handler.stats.to_dict())}")
|
99
143
|
yield handler
|
144
|
+
except yaml.YAMLError as e:
|
145
|
+
raise ConfigurationError(f"Invalid YAML configuration: {str(e)}")
|
146
|
+
except ImportError as e:
|
147
|
+
raise ConfigurationError(f"Failed to import handler for {db_type}: {str(e)}")
|
100
148
|
finally:
|
101
149
|
if handler:
|
150
|
+
self.logger("debug", f"Cleaning up handler for {database}")
|
151
|
+
handler.stats.record_connection_end()
|
152
|
+
self.logger("info", f"Final resource stats: {json.dumps(handler.stats.to_dict())}")
|
102
153
|
await handler.cleanup()
|
103
154
|
|
104
155
|
def _setup_handlers(self):
|
@@ -116,11 +167,11 @@ class DatabaseServer:
|
|
116
167
|
@self.server.read_resource()
|
117
168
|
async def handle_read_resource(uri: str, arguments: dict | None = None) -> str:
|
118
169
|
if not arguments or 'database' not in arguments:
|
119
|
-
raise
|
170
|
+
raise ConfigurationError("Database configuration name must be specified")
|
120
171
|
|
121
172
|
parts = uri.split('/')
|
122
173
|
if len(parts) < 3:
|
123
|
-
raise
|
174
|
+
raise ConfigurationError("Invalid resource URI format")
|
124
175
|
|
125
176
|
database = arguments['database']
|
126
177
|
table_name = parts[-2] # URI format: xxx/table_name/schema
|
@@ -154,18 +205,18 @@ class DatabaseServer:
|
|
154
205
|
@self.server.call_tool()
|
155
206
|
async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
156
207
|
if name != "query":
|
157
|
-
raise
|
208
|
+
raise ConfigurationError(f"Unknown tool: {name}")
|
158
209
|
|
159
210
|
if "database" not in arguments:
|
160
|
-
raise
|
211
|
+
raise ConfigurationError("Database configuration name must be specified")
|
161
212
|
|
162
213
|
sql = arguments.get("sql", "").strip()
|
163
214
|
if not sql:
|
164
|
-
raise
|
215
|
+
raise ConfigurationError("SQL query cannot be empty")
|
165
216
|
|
166
217
|
# Only allow SELECT statements
|
167
218
|
if not sql.lower().startswith("select"):
|
168
|
-
raise
|
219
|
+
raise ConfigurationError("Only SELECT queries are supported for security reasons")
|
169
220
|
|
170
221
|
database = arguments["database"]
|
171
222
|
async with self.get_handler(database) as handler:
|
@@ -175,8 +226,10 @@ class DatabaseServer:
|
|
175
226
|
async def run(self):
|
176
227
|
"""Run server"""
|
177
228
|
async with mcp.server.stdio.stdio_server() as streams:
|
229
|
+
init_options = self.server.create_initialization_options()
|
230
|
+
init_options.capabilities.prompts = types.PromptsCapability(list=True)
|
178
231
|
await self.server.run(
|
179
232
|
streams[0],
|
180
233
|
streams[1],
|
181
|
-
|
234
|
+
init_options
|
182
235
|
)
|
mcp_dbutils/postgres/handler.py
CHANGED
@@ -4,7 +4,7 @@ import psycopg2
|
|
4
4
|
from psycopg2.pool import SimpleConnectionPool
|
5
5
|
import mcp.types as types
|
6
6
|
|
7
|
-
from ..base import DatabaseHandler
|
7
|
+
from ..base import DatabaseHandler, DatabaseError
|
8
8
|
from .config import PostgresConfig
|
9
9
|
|
10
10
|
class PostgresHandler(DatabaseHandler):
|
@@ -51,8 +51,8 @@ class PostgresHandler(DatabaseHandler):
|
|
51
51
|
]
|
52
52
|
except psycopg2.Error as e:
|
53
53
|
error_msg = f"Failed to get table list: [Code: {e.pgcode}] {e.pgerror or str(e)}"
|
54
|
-
self.
|
55
|
-
raise
|
54
|
+
self.stats.record_error(e.__class__.__name__)
|
55
|
+
raise DatabaseError(error_msg)
|
56
56
|
finally:
|
57
57
|
if conn:
|
58
58
|
conn.close()
|
@@ -104,18 +104,19 @@ class PostgresHandler(DatabaseHandler):
|
|
104
104
|
})
|
105
105
|
except psycopg2.Error as e:
|
106
106
|
error_msg = f"Failed to read table schema: [Code: {e.pgcode}] {e.pgerror or str(e)}"
|
107
|
-
self.
|
108
|
-
raise
|
107
|
+
self.stats.record_error(e.__class__.__name__)
|
108
|
+
raise DatabaseError(error_msg)
|
109
109
|
finally:
|
110
110
|
if conn:
|
111
111
|
conn.close()
|
112
112
|
|
113
|
-
async def
|
113
|
+
async def _execute_query(self, sql: str) -> str:
|
114
114
|
"""Execute SQL query"""
|
115
|
+
conn = None
|
115
116
|
try:
|
116
117
|
conn_params = self.config.get_connection_params()
|
117
118
|
conn = psycopg2.connect(**conn_params)
|
118
|
-
self.log("
|
119
|
+
self.log("debug", f"Executing query: {sql}")
|
119
120
|
|
120
121
|
with conn.cursor() as cur:
|
121
122
|
# Start read-only transaction
|
@@ -132,19 +133,18 @@ class PostgresHandler(DatabaseHandler):
|
|
132
133
|
'row_count': len(results)
|
133
134
|
})
|
134
135
|
|
135
|
-
self.log("
|
136
|
+
self.log("debug", f"Query completed, returned {len(results)} rows")
|
136
137
|
return result_text
|
137
138
|
finally:
|
138
139
|
cur.execute("ROLLBACK")
|
139
140
|
except psycopg2.Error as e:
|
140
141
|
error_msg = f"Query execution failed: [Code: {e.pgcode}] {e.pgerror or str(e)}"
|
141
|
-
|
142
|
-
raise
|
142
|
+
raise DatabaseError(error_msg)
|
143
143
|
finally:
|
144
144
|
if conn:
|
145
145
|
conn.close()
|
146
146
|
|
147
147
|
async def cleanup(self):
|
148
148
|
"""Cleanup resources"""
|
149
|
-
#
|
150
|
-
|
149
|
+
# Log final stats before cleanup
|
150
|
+
self.log("info", f"Final PostgreSQL handler stats: {self.stats.to_dict()}")
|
mcp_dbutils/sqlite/handler.py
CHANGED
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
5
5
|
from contextlib import closing
|
6
6
|
import mcp.types as types
|
7
7
|
|
8
|
-
from ..base import DatabaseHandler
|
8
|
+
from ..base import DatabaseHandler, DatabaseError
|
9
9
|
from .config import SqliteConfig
|
10
10
|
|
11
11
|
class SqliteHandler(DatabaseHandler):
|
@@ -86,11 +86,23 @@ class SqliteHandler(DatabaseHandler):
|
|
86
86
|
self.log("error", error_msg)
|
87
87
|
raise
|
88
88
|
|
89
|
-
async def
|
89
|
+
async def _execute_query(self, sql: str) -> str:
|
90
90
|
"""Execute SQL query"""
|
91
|
+
# Check for non-SELECT queries
|
92
|
+
sql_lower = sql.lower().strip()
|
93
|
+
if not sql_lower.startswith('select'):
|
94
|
+
error_msg = "cannot execute DELETE statement"
|
95
|
+
if sql_lower.startswith('delete'):
|
96
|
+
error_msg = "cannot execute DELETE statement"
|
97
|
+
elif sql_lower.startswith('update'):
|
98
|
+
error_msg = "cannot execute UPDATE statement"
|
99
|
+
elif sql_lower.startswith('insert'):
|
100
|
+
error_msg = "cannot execute INSERT statement"
|
101
|
+
raise DatabaseError(error_msg)
|
102
|
+
|
91
103
|
try:
|
92
104
|
with closing(self._get_connection()) as conn:
|
93
|
-
self.log("
|
105
|
+
self.log("debug", f"Executing query: {sql}")
|
94
106
|
cursor = conn.execute(sql)
|
95
107
|
results = cursor.fetchall()
|
96
108
|
|
@@ -103,15 +115,14 @@ class SqliteHandler(DatabaseHandler):
|
|
103
115
|
'row_count': len(results)
|
104
116
|
})
|
105
117
|
|
106
|
-
self.log("
|
118
|
+
self.log("debug", f"Query completed, returned {len(results)} rows")
|
107
119
|
return result_text
|
108
120
|
|
109
121
|
except sqlite3.Error as e:
|
110
122
|
error_msg = f"Query execution failed: {str(e)}"
|
111
|
-
|
112
|
-
raise
|
123
|
+
raise DatabaseError(error_msg)
|
113
124
|
|
114
125
|
async def cleanup(self):
|
115
126
|
"""Cleanup resources"""
|
116
|
-
#
|
117
|
-
|
127
|
+
# Log final stats before cleanup
|
128
|
+
self.log("info", f"Final SQLite handler stats: {self.stats.to_dict()}")
|
mcp_dbutils/stats.py
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
"""Resource monitoring statistics module"""
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import Optional
|
6
|
+
import sys
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class ResourceStats:
|
10
|
+
"""Resource statistics tracking"""
|
11
|
+
# Connection stats
|
12
|
+
active_connections: int = 0
|
13
|
+
total_connections: int = 0
|
14
|
+
connection_start_time: Optional[datetime] = None
|
15
|
+
|
16
|
+
# Query stats
|
17
|
+
query_count: int = 0
|
18
|
+
last_query_time: Optional[datetime] = None
|
19
|
+
|
20
|
+
# Error stats
|
21
|
+
error_count: int = 0
|
22
|
+
last_error_time: Optional[datetime] = None
|
23
|
+
error_types: dict[str, int] = None
|
24
|
+
|
25
|
+
# Resource stats
|
26
|
+
estimated_memory: int = 0
|
27
|
+
|
28
|
+
def __post_init__(self):
|
29
|
+
"""Initialize mutable defaults"""
|
30
|
+
if self.error_types is None:
|
31
|
+
self.error_types = {}
|
32
|
+
|
33
|
+
def record_connection_start(self):
|
34
|
+
"""Record new connection start"""
|
35
|
+
self.active_connections += 1
|
36
|
+
self.total_connections += 1
|
37
|
+
self.connection_start_time = datetime.now()
|
38
|
+
|
39
|
+
def record_connection_end(self):
|
40
|
+
"""Record connection end"""
|
41
|
+
self.active_connections = max(0, self.active_connections - 1)
|
42
|
+
|
43
|
+
def record_query(self):
|
44
|
+
"""Record query execution"""
|
45
|
+
self.query_count += 1
|
46
|
+
self.last_query_time = datetime.now()
|
47
|
+
|
48
|
+
def record_error(self, error_type: str):
|
49
|
+
"""Record error occurrence
|
50
|
+
|
51
|
+
Args:
|
52
|
+
error_type: Type/class name of the error
|
53
|
+
"""
|
54
|
+
self.error_count += 1
|
55
|
+
self.last_error_time = datetime.now()
|
56
|
+
self.error_types[error_type] = self.error_types.get(error_type, 0) + 1
|
57
|
+
|
58
|
+
def update_memory_usage(self, obj: object):
|
59
|
+
"""Update estimated memory usage
|
60
|
+
|
61
|
+
Args:
|
62
|
+
obj: Object to estimate size for
|
63
|
+
"""
|
64
|
+
self.estimated_memory = sys.getsizeof(obj)
|
65
|
+
|
66
|
+
def to_dict(self) -> dict:
|
67
|
+
"""Convert stats to dictionary for logging
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Dictionary of current statistics
|
71
|
+
"""
|
72
|
+
now = datetime.now()
|
73
|
+
connection_duration = None
|
74
|
+
if self.connection_start_time:
|
75
|
+
connection_duration = (now - self.connection_start_time).total_seconds()
|
76
|
+
|
77
|
+
return {
|
78
|
+
"active_connections": self.active_connections,
|
79
|
+
"total_connections": self.total_connections,
|
80
|
+
"connection_duration": connection_duration,
|
81
|
+
"query_count": self.query_count,
|
82
|
+
"error_count": self.error_count,
|
83
|
+
"error_types": self.error_types,
|
84
|
+
"estimated_memory_bytes": self.estimated_memory
|
85
|
+
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-dbutils
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.8
|
4
4
|
Summary: MCP Database Utilities Service
|
5
5
|
Author: Dong Hao
|
6
6
|
License-Expression: MIT
|
@@ -10,14 +10,24 @@ Requires-Dist: mcp>=1.2.1
|
|
10
10
|
Requires-Dist: psycopg2-binary>=2.9.10
|
11
11
|
Requires-Dist: python-dotenv>=1.0.1
|
12
12
|
Requires-Dist: pyyaml>=6.0.2
|
13
|
+
Provides-Extra: test
|
14
|
+
Requires-Dist: aiosqlite>=0.19.0; extra == 'test'
|
15
|
+
Requires-Dist: docker>=7.0.0; extra == 'test'
|
16
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
|
17
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
|
18
|
+
Requires-Dist: pytest-docker>=2.0.0; extra == 'test'
|
19
|
+
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
20
|
+
Requires-Dist: testcontainers>=3.7.0; extra == 'test'
|
13
21
|
Description-Content-Type: text/markdown
|
14
22
|
|
15
23
|
# MCP Database Utilities
|
16
24
|
|
17
25
|

|
18
26
|

|
27
|
+
[](https://github.com/donghao1393/mcp-dbutils/actions)
|
19
28
|

|
20
29
|

|
30
|
+
[](https://smithery.ai/server/@donghao1393/mcp-dbutils)
|
21
31
|
|
22
32
|
[中文文档](README_CN.md)
|
23
33
|
|
@@ -35,6 +45,13 @@ MCP Database Utilities is a unified database access service that supports multip
|
|
35
45
|
## Installation and Configuration
|
36
46
|
|
37
47
|
### Installation Methods
|
48
|
+
#### Installing via Smithery
|
49
|
+
|
50
|
+
To install Database Utilities for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@donghao1393/mcp-dbutils):
|
51
|
+
|
52
|
+
```bash
|
53
|
+
npx -y @smithery/cli install @donghao1393/mcp-dbutils --client claude
|
54
|
+
```
|
38
55
|
|
39
56
|
#### Using uvx (Recommended)
|
40
57
|
No installation required, run directly using `uvx`:
|
@@ -86,6 +103,7 @@ Add to Claude configuration:
|
|
86
103
|
```bash
|
87
104
|
docker run -i --rm \
|
88
105
|
-v /path/to/config.yaml:/app/config.yaml \
|
106
|
+
-v /path/to/sqlite.db:/app/sqlite.db \ # Optional: for SQLite database
|
89
107
|
-e MCP_DEBUG=1 \ # Optional: Enable debug mode
|
90
108
|
mcp/dbutils --config /app/config.yaml
|
91
109
|
```
|
@@ -101,6 +119,8 @@ Add to Claude configuration:
|
|
101
119
|
"--rm",
|
102
120
|
"-v",
|
103
121
|
"/path/to/config.yaml:/app/config.yaml",
|
122
|
+
"-v",
|
123
|
+
"/path/to/sqlite.db:/app/sqlite.db", // Optional: for SQLite database
|
104
124
|
"mcp/dbutils",
|
105
125
|
"--config",
|
106
126
|
"/app/config.yaml"
|
@@ -112,6 +132,12 @@ Add to Claude configuration:
|
|
112
132
|
}
|
113
133
|
```
|
114
134
|
|
135
|
+
> **Note for Docker database connections:**
|
136
|
+
> - For SQLite: Mount your database file using `-v /path/to/sqlite.db:/app/sqlite.db`
|
137
|
+
> - For PostgreSQL running on host:
|
138
|
+
> - On Mac/Windows: Use `host.docker.internal` as host in config
|
139
|
+
> - On Linux: Use `172.17.0.1` (docker0 IP) or run with `--network="host"`
|
140
|
+
|
115
141
|
### Requirements
|
116
142
|
- Python 3.10+
|
117
143
|
- PostgreSQL (optional)
|
@@ -122,20 +148,21 @@ The project requires a YAML configuration file, specified via the `--config` par
|
|
122
148
|
|
123
149
|
```yaml
|
124
150
|
databases:
|
125
|
-
# PostgreSQL example
|
151
|
+
# PostgreSQL example (when using Docker)
|
126
152
|
my_postgres:
|
127
153
|
type: postgres
|
128
154
|
dbname: test_db
|
129
155
|
user: postgres
|
130
156
|
password: secret
|
131
|
-
host:
|
157
|
+
host: host.docker.internal # For Mac/Windows
|
158
|
+
# host: 172.17.0.1 # For Linux (docker0 IP)
|
132
159
|
port: 5432
|
133
160
|
|
134
|
-
# SQLite example
|
161
|
+
# SQLite example (when using Docker)
|
135
162
|
my_sqlite:
|
136
163
|
type: sqlite
|
137
|
-
path: /
|
138
|
-
password: optional_password
|
164
|
+
path: /app/sqlite.db # Mapped path inside container
|
165
|
+
password: optional_password # optional
|
139
166
|
```
|
140
167
|
|
141
168
|
### Debug Mode
|
@@ -283,3 +310,7 @@ For detailed guidelines, see [CONTRIBUTING.md](.github/CONTRIBUTING.md)
|
|
283
310
|
* [5ire](https://5ire.app/)
|
284
311
|
* [Cline](https://cline.bot)
|
285
312
|
- [Model Context Protocol](https://modelcontextprotocol.io/) for comprehensive interfaces
|
313
|
+
|
314
|
+
## Star History
|
315
|
+
|
316
|
+
[](https://star-history.com/#donghao1393/mcp-dbutils&Date)
|
@@ -1,17 +1,18 @@
|
|
1
1
|
mcp_dbutils/__init__.py,sha256=_zVdjN2P_G5qEye7f5vQxiXAd6EwX2gyTL1wpFpqKck,1718
|
2
|
-
mcp_dbutils/base.py,sha256=
|
2
|
+
mcp_dbutils/base.py,sha256=Hj35seBlmYgcq0qyoZchnUbKzDMPlhUxsCw7U79OA2c,9186
|
3
3
|
mcp_dbutils/config.py,sha256=EwnPNuQVCBKd5WOXQfROyDTM-YpM_Odp0GhCPRg8YwE,1863
|
4
4
|
mcp_dbutils/log.py,sha256=RJwWtHyZnJA2YRcyCK-WgnFadNuwtJeYKSxPbE-DCG0,991
|
5
|
+
mcp_dbutils/stats.py,sha256=2hiKi_M8V4xhVHlH5FS-Df5GuMEpuzif12C8ik06Khs,2538
|
5
6
|
mcp_dbutils/postgres/__init__.py,sha256=Y6v_RsI79pqAfpKM3SrT1T1I9r5yWuKT0GUUNmHD3DE,146
|
6
7
|
mcp_dbutils/postgres/config.py,sha256=ipjskdlv3u949R9rC2AKPCINRrtEAiKDjeogpFfM6aE,2401
|
7
|
-
mcp_dbutils/postgres/handler.py,sha256=
|
8
|
+
mcp_dbutils/postgres/handler.py,sha256=mvktsgak0tR-L4oknqv7UhOZ0_R8KPB9JRWiswh0bl4,5955
|
8
9
|
mcp_dbutils/postgres/server.py,sha256=3AdQyQfgrClkCmWOZ-xFXTeI9t3tpFkrY3qmyOR0b-U,8586
|
9
10
|
mcp_dbutils/sqlite/__init__.py,sha256=QV4th2ywzUmCCa3GHCcBf8blJ_E8OYy0dJ2fSf1TfSU,134
|
10
11
|
mcp_dbutils/sqlite/config.py,sha256=QK1JlY-ZBB5VyhydbiU-aAKNESTWMtyBfawbv2DAVCQ,2452
|
11
|
-
mcp_dbutils/sqlite/handler.py,sha256=
|
12
|
+
mcp_dbutils/sqlite/handler.py,sha256=7mD50sQRii7UKX-8dE9RNicIXGwMDPB59Ee1dmncSlg,4842
|
12
13
|
mcp_dbutils/sqlite/server.py,sha256=Q29O2YOW4kqDGc3z1hMXVv4M0KSkuZbqvVDMgZE8Fd8,7593
|
13
|
-
mcp_dbutils-0.2.
|
14
|
-
mcp_dbutils-0.2.
|
15
|
-
mcp_dbutils-0.2.
|
16
|
-
mcp_dbutils-0.2.
|
17
|
-
mcp_dbutils-0.2.
|
14
|
+
mcp_dbutils-0.2.8.dist-info/METADATA,sha256=YdBbIw37bEGOtLGOE1XTjZaE7ESHog-y6rXxIp5N5y8,9466
|
15
|
+
mcp_dbutils-0.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
mcp_dbutils-0.2.8.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
|
17
|
+
mcp_dbutils-0.2.8.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
|
18
|
+
mcp_dbutils-0.2.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|