mcp-dbutils 0.8.0__py3-none-any.whl → 0.9.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/__init__.py +9 -9
- mcp_dbutils/base.py +383 -58
- mcp_dbutils/config.py +12 -12
- mcp_dbutils/postgres/__init__.py +3 -3
- mcp_dbutils/postgres/config.py +16 -16
- mcp_dbutils/postgres/handler.py +446 -14
- mcp_dbutils/postgres/server.py +16 -16
- mcp_dbutils/sqlite/__init__.py +3 -3
- mcp_dbutils/sqlite/config.py +12 -12
- mcp_dbutils/sqlite/handler.py +361 -77
- mcp_dbutils/sqlite/server.py +21 -21
- mcp_dbutils/stats.py +112 -3
- {mcp_dbutils-0.8.0.dist-info → mcp_dbutils-0.9.0.dist-info}/METADATA +42 -8
- mcp_dbutils-0.9.0.dist-info/RECORD +18 -0
- mcp_dbutils-0.8.0.dist-info/RECORD +0 -18
- {mcp_dbutils-0.8.0.dist-info → mcp_dbutils-0.9.0.dist-info}/WHEEL +0 -0
- {mcp_dbutils-0.8.0.dist-info → mcp_dbutils-0.9.0.dist-info}/entry_points.txt +0 -0
- {mcp_dbutils-0.8.0.dist-info → mcp_dbutils-0.9.0.dist-info}/licenses/LICENSE +0 -0
mcp_dbutils/postgres/server.py
CHANGED
@@ -4,14 +4,14 @@ from psycopg2.pool import SimpleConnectionPool
|
|
4
4
|
from typing import Optional, List
|
5
5
|
import mcp.types as types
|
6
6
|
from importlib.metadata import metadata
|
7
|
-
from ..base import
|
7
|
+
from ..base import ConnectionServer
|
8
8
|
from ..log import create_logger
|
9
|
-
from .config import
|
9
|
+
from .config import PostgreSQLConfig
|
10
10
|
|
11
11
|
# 获取包信息用于日志命名
|
12
12
|
pkg_meta = metadata("mcp-dbutils")
|
13
|
-
class
|
14
|
-
def __init__(self, config:
|
13
|
+
class PostgreSQLServer(ConnectionServer):
|
14
|
+
def __init__(self, config: PostgreSQLConfig, config_path: Optional[str] = None):
|
15
15
|
"""初始化PostgreSQL服务器
|
16
16
|
Args:
|
17
17
|
config: 数据库配置
|
@@ -32,9 +32,9 @@ class PostgresServer(DatabaseServer):
|
|
32
32
|
self.log("info", "测试连接成功")
|
33
33
|
# 创建连接池
|
34
34
|
self.pool = SimpleConnectionPool(1, 5, **conn_params)
|
35
|
-
self.log("info", "
|
35
|
+
self.log("info", "连接池创建成功")
|
36
36
|
except psycopg2.Error as e:
|
37
|
-
self.log("error", f"
|
37
|
+
self.log("error", f"连接失败: [Code: {e.pgcode}] {e.pgerror or str(e)}")
|
38
38
|
raise
|
39
39
|
async def list_resources(self) -> list[types.Resource]:
|
40
40
|
"""列出所有表资源"""
|
@@ -124,9 +124,9 @@ class PostgresServer(DatabaseServer):
|
|
124
124
|
inputSchema={
|
125
125
|
"type": "object",
|
126
126
|
"properties": {
|
127
|
-
"
|
127
|
+
"connection": {
|
128
128
|
"type": "string",
|
129
|
-
"description": "
|
129
|
+
"description": "数据库连接名称(可选)"
|
130
130
|
},
|
131
131
|
"sql": {
|
132
132
|
"type": "string",
|
@@ -147,16 +147,16 @@ class PostgresServer(DatabaseServer):
|
|
147
147
|
# 仅允许SELECT语句
|
148
148
|
if not sql.lower().startswith("select"):
|
149
149
|
raise ValueError("仅支持SELECT查询")
|
150
|
-
|
150
|
+
connection = arguments.get("connection")
|
151
151
|
use_pool = True
|
152
152
|
conn = None
|
153
153
|
try:
|
154
|
-
if
|
155
|
-
#
|
156
|
-
config =
|
154
|
+
if connection and self.config_path:
|
155
|
+
# 使用指定的数据库连接
|
156
|
+
config = PostgreSQLConfig.from_yaml(self.config_path, connection)
|
157
157
|
conn_params = config.get_connection_params()
|
158
158
|
masked_params = config.get_masked_connection_info()
|
159
|
-
self.log("info", f"使用配置 {
|
159
|
+
self.log("info", f"使用配置 {connection} 连接数据库: {masked_params}")
|
160
160
|
conn = psycopg2.connect(**conn_params)
|
161
161
|
use_pool = False
|
162
162
|
else:
|
@@ -173,7 +173,7 @@ class PostgresServer(DatabaseServer):
|
|
173
173
|
formatted_results = [dict(zip(columns, row)) for row in results]
|
174
174
|
result_text = str({
|
175
175
|
'type': 'postgres',
|
176
|
-
'config_name':
|
176
|
+
'config_name': connection or 'default',
|
177
177
|
'query_result': {
|
178
178
|
'columns': columns,
|
179
179
|
'rows': formatted_results,
|
@@ -191,7 +191,7 @@ class PostgresServer(DatabaseServer):
|
|
191
191
|
error = f"查询执行失败: {str(e)}"
|
192
192
|
error_msg = str({
|
193
193
|
'type': 'postgres',
|
194
|
-
'config_name':
|
194
|
+
'config_name': connection or 'default',
|
195
195
|
'error': error
|
196
196
|
})
|
197
197
|
self.log("error", error_msg)
|
@@ -205,5 +205,5 @@ class PostgresServer(DatabaseServer):
|
|
205
205
|
async def cleanup(self):
|
206
206
|
"""清理资源"""
|
207
207
|
if hasattr(self, 'pool'):
|
208
|
-
self.log("info", "
|
208
|
+
self.log("info", "关闭连接池")
|
209
209
|
self.pool.closeall()
|
mcp_dbutils/sqlite/__init__.py
CHANGED
mcp_dbutils/sqlite/config.py
CHANGED
@@ -4,13 +4,13 @@ from dataclasses import dataclass
|
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Dict, Any, Optional, Literal
|
6
6
|
from urllib.parse import urlparse, parse_qs
|
7
|
-
from ..config import
|
7
|
+
from ..config import ConnectionConfig
|
8
8
|
|
9
9
|
def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
|
10
10
|
"""Parse JDBC URL into connection parameters
|
11
11
|
|
12
12
|
Args:
|
13
|
-
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/
|
13
|
+
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/sqlite.db or jdbc:sqlite:/path/to/sqlite.db)
|
14
14
|
|
15
15
|
Returns:
|
16
16
|
Dictionary of connection parameters
|
@@ -40,7 +40,7 @@ def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
|
|
40
40
|
params[key] = values[0]
|
41
41
|
|
42
42
|
if not path:
|
43
|
-
raise ValueError("
|
43
|
+
raise ValueError("SQLite file path must be specified in URL")
|
44
44
|
|
45
45
|
return {
|
46
46
|
'path': path,
|
@@ -48,22 +48,22 @@ def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
|
|
48
48
|
}
|
49
49
|
|
50
50
|
@dataclass
|
51
|
-
class
|
51
|
+
class SQLiteConfig(ConnectionConfig):
|
52
52
|
path: str
|
53
53
|
password: Optional[str] = None
|
54
54
|
uri: bool = True # Enable URI mode to support parameters like password
|
55
55
|
type: Literal['sqlite'] = 'sqlite'
|
56
56
|
|
57
57
|
@classmethod
|
58
|
-
def from_jdbc_url(cls, jdbc_url: str, password: Optional[str] = None) -> '
|
58
|
+
def from_jdbc_url(cls, jdbc_url: str, password: Optional[str] = None) -> 'SQLiteConfig':
|
59
59
|
"""Create configuration from JDBC URL
|
60
60
|
|
61
61
|
Args:
|
62
|
-
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/
|
62
|
+
jdbc_url: JDBC URL (e.g. jdbc:sqlite:file:/path/to/sqlite.db)
|
63
63
|
password: Optional password for database encryption
|
64
64
|
|
65
65
|
Returns:
|
66
|
-
|
66
|
+
SQLiteConfig instance
|
67
67
|
|
68
68
|
Raises:
|
69
69
|
ValueError: If URL format is invalid
|
@@ -80,7 +80,7 @@ class SqliteConfig(DatabaseConfig):
|
|
80
80
|
|
81
81
|
@property
|
82
82
|
def absolute_path(self) -> str:
|
83
|
-
"""Return absolute path to database file"""
|
83
|
+
"""Return absolute path to SQLite database file"""
|
84
84
|
return str(Path(self.path).expanduser().resolve())
|
85
85
|
|
86
86
|
def get_connection_params(self) -> Dict[str, Any]:
|
@@ -109,23 +109,23 @@ class SqliteConfig(DatabaseConfig):
|
|
109
109
|
return info
|
110
110
|
|
111
111
|
@classmethod
|
112
|
-
def from_yaml(cls, yaml_path: str, db_name: str, **kwargs) -> '
|
112
|
+
def from_yaml(cls, yaml_path: str, db_name: str, **kwargs) -> 'SQLiteConfig':
|
113
113
|
"""Create SQLite configuration from YAML
|
114
114
|
|
115
115
|
Args:
|
116
116
|
yaml_path: Path to YAML configuration file
|
117
|
-
db_name:
|
117
|
+
db_name: Connection configuration name
|
118
118
|
"""
|
119
119
|
configs = cls.load_yaml_config(yaml_path)
|
120
120
|
|
121
121
|
if db_name not in configs:
|
122
122
|
available_dbs = list(configs.keys())
|
123
|
-
raise ValueError(f"
|
123
|
+
raise ValueError(f"Connection configuration not found: {db_name}. Available configurations: {available_dbs}")
|
124
124
|
|
125
125
|
db_config = configs[db_name]
|
126
126
|
|
127
127
|
if 'type' not in db_config:
|
128
|
-
raise ValueError("
|
128
|
+
raise ValueError("Connection configuration must include 'type' field")
|
129
129
|
if db_config['type'] != 'sqlite':
|
130
130
|
raise ValueError(f"Configuration is not SQLite type: {db_config['type']}")
|
131
131
|
|