mcp-dbutils 0.23.0__py3-none-any.whl → 1.0.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/audit.py +269 -0
- mcp_dbutils/base.py +505 -3
- mcp_dbutils/config.py +103 -1
- mcp_dbutils/mysql/config.py +57 -40
- mcp_dbutils/mysql/handler.py +60 -0
- mcp_dbutils/postgres/config.py +40 -22
- mcp_dbutils/postgres/handler.py +60 -0
- mcp_dbutils/sqlite/config.py +8 -1
- mcp_dbutils/sqlite/handler.py +53 -0
- {mcp_dbutils-0.23.0.dist-info → mcp_dbutils-1.0.0.dist-info}/METADATA +1 -1
- mcp_dbutils-1.0.0.dist-info/RECORD +23 -0
- mcp_dbutils-0.23.0.dist-info/RECORD +0 -22
- {mcp_dbutils-0.23.0.dist-info → mcp_dbutils-1.0.0.dist-info}/WHEEL +0 -0
- {mcp_dbutils-0.23.0.dist-info → mcp_dbutils-1.0.0.dist-info}/entry_points.txt +0 -0
- {mcp_dbutils-0.23.0.dist-info → mcp_dbutils-1.0.0.dist-info}/licenses/LICENSE +0 -0
mcp_dbutils/postgres/handler.py
CHANGED
@@ -154,6 +154,66 @@ class PostgreSQLHandler(ConnectionHandler):
|
|
154
154
|
if conn:
|
155
155
|
conn.close()
|
156
156
|
|
157
|
+
async def _execute_write_query(self, sql: str) -> str:
|
158
|
+
"""Execute SQL write query
|
159
|
+
|
160
|
+
Args:
|
161
|
+
sql: SQL write query (INSERT, UPDATE, DELETE)
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
str: Execution result
|
165
|
+
|
166
|
+
Raises:
|
167
|
+
ConnectionHandlerError: If query execution fails
|
168
|
+
"""
|
169
|
+
conn = None
|
170
|
+
try:
|
171
|
+
# Check if the query is a write operation
|
172
|
+
sql_upper = sql.strip().upper()
|
173
|
+
is_insert = sql_upper.startswith("INSERT")
|
174
|
+
is_update = sql_upper.startswith("UPDATE")
|
175
|
+
is_delete = sql_upper.startswith("DELETE")
|
176
|
+
is_transaction = sql_upper.startswith(("BEGIN", "COMMIT", "ROLLBACK"))
|
177
|
+
|
178
|
+
if not (is_insert or is_update or is_delete or is_transaction):
|
179
|
+
raise ConnectionHandlerError("Only INSERT, UPDATE, DELETE, and transaction statements are allowed for write operations")
|
180
|
+
|
181
|
+
conn_params = self.config.get_connection_params()
|
182
|
+
conn = psycopg2.connect(**conn_params)
|
183
|
+
self.log("debug", f"Executing write operation: {sql}")
|
184
|
+
|
185
|
+
with conn.cursor() as cur:
|
186
|
+
try:
|
187
|
+
# Execute the write operation
|
188
|
+
cur.execute(sql)
|
189
|
+
|
190
|
+
# Get number of affected rows
|
191
|
+
affected_rows = cur.rowcount
|
192
|
+
|
193
|
+
# Commit the transaction if not in a transaction block
|
194
|
+
if not is_transaction:
|
195
|
+
conn.commit()
|
196
|
+
|
197
|
+
self.log("debug", f"Write operation executed successfully, affected {affected_rows} rows")
|
198
|
+
|
199
|
+
# Return result
|
200
|
+
if is_transaction:
|
201
|
+
return f"Transaction operation executed successfully"
|
202
|
+
else:
|
203
|
+
return f"Write operation executed successfully. {affected_rows} row{'s' if affected_rows != 1 else ''} affected."
|
204
|
+
except psycopg2.Error as e:
|
205
|
+
# Rollback on error
|
206
|
+
if not is_transaction:
|
207
|
+
conn.rollback()
|
208
|
+
self.log("error", f"Write operation error: [Code: {e.pgcode}] {e.pgerror or str(e)}")
|
209
|
+
raise ConnectionHandlerError(f"[Code: {e.pgcode}] {e.pgerror or str(e)}")
|
210
|
+
except psycopg2.Error as e:
|
211
|
+
error_msg = f"[{self.db_type}] Write operation failed: [Code: {e.pgcode}] {e.pgerror or str(e)}"
|
212
|
+
raise ConnectionHandlerError(error_msg)
|
213
|
+
finally:
|
214
|
+
if conn:
|
215
|
+
conn.close()
|
216
|
+
|
157
217
|
async def get_table_description(self, table_name: str) -> str:
|
158
218
|
"""Get detailed table description"""
|
159
219
|
conn = None
|
mcp_dbutils/sqlite/config.py
CHANGED
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
5
5
|
from typing import Any, Dict, Literal, Optional
|
6
6
|
from urllib.parse import parse_qs, urlparse
|
7
7
|
|
8
|
-
from ..config import ConnectionConfig
|
8
|
+
from ..config import ConnectionConfig, WritePermissions
|
9
9
|
|
10
10
|
|
11
11
|
def parse_jdbc_url(jdbc_url: str) -> Dict[str, str]:
|
@@ -55,6 +55,8 @@ class SQLiteConfig(ConnectionConfig):
|
|
55
55
|
password: Optional[str] = None
|
56
56
|
uri: bool = True # Enable URI mode to support parameters like password
|
57
57
|
type: Literal['sqlite'] = 'sqlite'
|
58
|
+
writable: bool = False # Whether write operations are allowed
|
59
|
+
write_permissions: Optional[WritePermissions] = None # Write permissions configuration
|
58
60
|
|
59
61
|
@classmethod
|
60
62
|
def from_jdbc_url(cls, jdbc_url: str, password: Optional[str] = None) -> 'SQLiteConfig':
|
@@ -147,5 +149,10 @@ class SQLiteConfig(ConnectionConfig):
|
|
147
149
|
uri=True
|
148
150
|
)
|
149
151
|
|
152
|
+
# Parse write permissions
|
153
|
+
config.writable = db_config.get('writable', False)
|
154
|
+
if config.writable and 'write_permissions' in db_config:
|
155
|
+
config.write_permissions = WritePermissions(db_config['write_permissions'])
|
156
|
+
|
150
157
|
config.debug = cls.get_debug_mode()
|
151
158
|
return config
|
mcp_dbutils/sqlite/handler.py
CHANGED
@@ -128,6 +128,59 @@ class SQLiteHandler(ConnectionHandler):
|
|
128
128
|
error_msg = f"[{self.db_type}] Query execution failed: {str(e)}"
|
129
129
|
raise ConnectionHandlerError(error_msg)
|
130
130
|
|
131
|
+
async def _execute_write_query(self, sql: str) -> str:
|
132
|
+
"""Execute SQL write query
|
133
|
+
|
134
|
+
Args:
|
135
|
+
sql: SQL write query (INSERT, UPDATE, DELETE)
|
136
|
+
|
137
|
+
Returns:
|
138
|
+
str: Execution result
|
139
|
+
|
140
|
+
Raises:
|
141
|
+
ConnectionHandlerError: If query execution fails
|
142
|
+
"""
|
143
|
+
try:
|
144
|
+
# Check if the query is a write operation
|
145
|
+
sql_upper = sql.strip().upper()
|
146
|
+
is_insert = sql_upper.startswith("INSERT")
|
147
|
+
is_update = sql_upper.startswith("UPDATE")
|
148
|
+
is_delete = sql_upper.startswith("DELETE")
|
149
|
+
is_transaction = sql_upper.startswith(("BEGIN", "COMMIT", "ROLLBACK"))
|
150
|
+
|
151
|
+
if not (is_insert or is_update or is_delete or is_transaction):
|
152
|
+
raise ConnectionHandlerError("Only INSERT, UPDATE, DELETE, and transaction statements are allowed for write operations")
|
153
|
+
|
154
|
+
conn = sqlite3.connect(self.config.path)
|
155
|
+
cur = conn.cursor()
|
156
|
+
|
157
|
+
try:
|
158
|
+
start_time = time.time()
|
159
|
+
cur.execute(sql)
|
160
|
+
conn.commit()
|
161
|
+
end_time = time.time()
|
162
|
+
elapsed_ms = (end_time - start_time) * 1000
|
163
|
+
|
164
|
+
# Get number of affected rows
|
165
|
+
affected_rows = cur.rowcount
|
166
|
+
|
167
|
+
self.log("debug", f"Write operation executed in {elapsed_ms:.2f}ms, affected {affected_rows} rows")
|
168
|
+
|
169
|
+
# Return result
|
170
|
+
if is_transaction:
|
171
|
+
return f"Transaction operation executed successfully"
|
172
|
+
else:
|
173
|
+
return f"Write operation executed successfully. {affected_rows} row{'s' if affected_rows != 1 else ''} affected."
|
174
|
+
except sqlite3.Error as e:
|
175
|
+
self.log("error", f"Write operation error: {str(e)}")
|
176
|
+
raise ConnectionHandlerError(str(e))
|
177
|
+
finally:
|
178
|
+
cur.close()
|
179
|
+
conn.close()
|
180
|
+
except sqlite3.Error as e:
|
181
|
+
error_msg = f"[{self.db_type}] Write operation failed: {str(e)}"
|
182
|
+
raise ConnectionHandlerError(error_msg)
|
183
|
+
|
131
184
|
async def get_table_description(self, table_name: str) -> str:
|
132
185
|
"""Get detailed table description"""
|
133
186
|
try:
|
@@ -0,0 +1,23 @@
|
|
1
|
+
mcp_dbutils/__init__.py,sha256=6LLccQv7je2L4IpY_I3OzSJZcK32VUDJv2IY31y6eYg,1900
|
2
|
+
mcp_dbutils/audit.py,sha256=U-Fd511HxOQH4BxfCXgd4KyaBLESEBnGsPkUNviSTwc,7294
|
3
|
+
mcp_dbutils/base.py,sha256=ao2md_bsbK5s_vsO--m-sZLPfiMp6f_MUVL9o0KzKIA,61720
|
4
|
+
mcp_dbutils/config.py,sha256=zwN9yPKv4WvEPG3WIRT6uBVZSRxFniSmN2kEog7KPcI,5921
|
5
|
+
mcp_dbutils/log.py,sha256=mqxi6I_IL-MF1F_pxBtnYZQKOHbGBJ74gsvZHVelr1w,823
|
6
|
+
mcp_dbutils/stats.py,sha256=wMqWPfGnEOg9v5YBtTsARV-1YsFUMM_pKdzitzSU9x4,7137
|
7
|
+
mcp_dbutils/mysql/__init__.py,sha256=gNhoHaxK1qhvMAH5AVl1vfV1rUpcbV9KZWUQb41aaQk,129
|
8
|
+
mcp_dbutils/mysql/config.py,sha256=ZOYnSRc-Cj9u494sk0P2Nn3zmQxMxaQaXGYArNJNo84,10338
|
9
|
+
mcp_dbutils/mysql/handler.py,sha256=B2T9TDwRGUtdvUz0m-iHtSk-ct0Q7a9UVFzscgV7NYo,25274
|
10
|
+
mcp_dbutils/mysql/server.py,sha256=4kWkrZby8fZq-Em65Fobs5KDDPJnrOxbsK1zLK1jzp0,9412
|
11
|
+
mcp_dbutils/postgres/__init__.py,sha256=-2zYuEJEQ2AMvmGhH5Z_umerSvt7S4xOa_XV4wgvGfI,154
|
12
|
+
mcp_dbutils/postgres/config.py,sha256=Z9g-VIZVfiik_u7nayFOc82EigainmPzwjO6zYPHpU8,8431
|
13
|
+
mcp_dbutils/postgres/handler.py,sha256=YLSPeS_D3BPLO7qN5SnYN95ioeTgCqUppIZ7Pt2ytQY,27733
|
14
|
+
mcp_dbutils/postgres/server.py,sha256=qden51sQu5Ht2ibl2jSDyxg7r_N9qDVhg83Pc-q98yc,8887
|
15
|
+
mcp_dbutils/sqlite/__init__.py,sha256=fK_3-WylCBYpBAzwuopi8hlwoIGJm2TPAlwcPWG46I0,134
|
16
|
+
mcp_dbutils/sqlite/config.py,sha256=rsfAE8yaCVZC39ziXssqsi0EXUOEWA-MtKHvrO-6jG4,4933
|
17
|
+
mcp_dbutils/sqlite/handler.py,sha256=vpkyCow26hpBqigNUNW0VGyWhsTz8uFflssM7K-FJi4,21882
|
18
|
+
mcp_dbutils/sqlite/server.py,sha256=EBKNKz_wTvChwg6BZlvZIBA1H5mmE2NiNEMOgu_CMy4,7373
|
19
|
+
mcp_dbutils-1.0.0.dist-info/METADATA,sha256=qwZMwl9JMQAydeUitTQ_zOhmzE2FplxliOYY03O39OQ,8440
|
20
|
+
mcp_dbutils-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
21
|
+
mcp_dbutils-1.0.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
|
22
|
+
mcp_dbutils-1.0.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
|
23
|
+
mcp_dbutils-1.0.0.dist-info/RECORD,,
|
@@ -1,22 +0,0 @@
|
|
1
|
-
mcp_dbutils/__init__.py,sha256=6LLccQv7je2L4IpY_I3OzSJZcK32VUDJv2IY31y6eYg,1900
|
2
|
-
mcp_dbutils/base.py,sha256=A9p79jqKxzCmOErFTwabQtUzIftkl1_qMFQ2rlnFB3g,42147
|
3
|
-
mcp_dbutils/config.py,sha256=bmXpOd1fyYfoyUS75I035ChT6t3wP5AyEnJ06e2ZS2o,1848
|
4
|
-
mcp_dbutils/log.py,sha256=mqxi6I_IL-MF1F_pxBtnYZQKOHbGBJ74gsvZHVelr1w,823
|
5
|
-
mcp_dbutils/stats.py,sha256=wMqWPfGnEOg9v5YBtTsARV-1YsFUMM_pKdzitzSU9x4,7137
|
6
|
-
mcp_dbutils/mysql/__init__.py,sha256=gNhoHaxK1qhvMAH5AVl1vfV1rUpcbV9KZWUQb41aaQk,129
|
7
|
-
mcp_dbutils/mysql/config.py,sha256=BTPPFqlhoTp7EBFIeLJZh8x6bCn3q9NivHYz9yZHziw,9820
|
8
|
-
mcp_dbutils/mysql/handler.py,sha256=CJl6Mcs8kloojtS7i-2yD_-InRSmtU09LfYnHDZDhn0,22783
|
9
|
-
mcp_dbutils/mysql/server.py,sha256=4kWkrZby8fZq-Em65Fobs5KDDPJnrOxbsK1zLK1jzp0,9412
|
10
|
-
mcp_dbutils/postgres/__init__.py,sha256=-2zYuEJEQ2AMvmGhH5Z_umerSvt7S4xOa_XV4wgvGfI,154
|
11
|
-
mcp_dbutils/postgres/config.py,sha256=NyQOVhkXJ1S-JD0w-ePNjTKI1Ja-aZQkDUdHi6U7Vl4,7752
|
12
|
-
mcp_dbutils/postgres/handler.py,sha256=SnzgXjcO3iuvpQ-dzggNOF0bewAKB5BwL3QS11a5qWw,25183
|
13
|
-
mcp_dbutils/postgres/server.py,sha256=qden51sQu5Ht2ibl2jSDyxg7r_N9qDVhg83Pc-q98yc,8887
|
14
|
-
mcp_dbutils/sqlite/__init__.py,sha256=fK_3-WylCBYpBAzwuopi8hlwoIGJm2TPAlwcPWG46I0,134
|
15
|
-
mcp_dbutils/sqlite/config.py,sha256=j67TJ8mQJ2D886MthSa-zYMtvUUYyyxYLMlNxkYoqZE,4509
|
16
|
-
mcp_dbutils/sqlite/handler.py,sha256=jH0xi_0PxiIBIVnXAGkfNoTVazTr7ol_P1XgBVDRkrU,19781
|
17
|
-
mcp_dbutils/sqlite/server.py,sha256=EBKNKz_wTvChwg6BZlvZIBA1H5mmE2NiNEMOgu_CMy4,7373
|
18
|
-
mcp_dbutils-0.23.0.dist-info/METADATA,sha256=8GKU0Z9Z-TQFAVJqjeouk5VRNrZaJfedBktVujVg3b4,8441
|
19
|
-
mcp_dbutils-0.23.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
20
|
-
mcp_dbutils-0.23.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
|
21
|
-
mcp_dbutils-0.23.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
|
22
|
-
mcp_dbutils-0.23.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|