mcp-dbutils 0.16.1__py3-none-any.whl → 0.17.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.
@@ -31,6 +31,35 @@ class MySQLHandler(ConnectionHandler):
31
31
  self.log("debug", f"Configuring connection with parameters: {masked_params}")
32
32
  self.pool = None
33
33
 
34
+ async def _check_table_exists(self, cursor, table_name: str) -> None:
35
+ """检查表是否存在
36
+
37
+ Args:
38
+ cursor: 数据库游标
39
+ table_name: 表名
40
+
41
+ Raises:
42
+ ConnectionHandlerError: 如果表不存在
43
+ """
44
+ cursor.execute("""
45
+ SELECT COUNT(*) as count
46
+ FROM information_schema.tables
47
+ WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s
48
+ """, (self.config.database, table_name))
49
+ table_exists = cursor.fetchone()
50
+
51
+ # Handle different formats of cursor results (dict or tuple)
52
+ if not table_exists:
53
+ raise ConnectionHandlerError(f"Table '{self.config.database}.{table_name}' doesn't exist")
54
+
55
+ # If fetchone returns a dictionary (dictionary=True was used)
56
+ if isinstance(table_exists, dict) and 'count' in table_exists and table_exists['count'] == 0:
57
+ raise ConnectionHandlerError(f"Table '{self.config.database}.{table_name}' doesn't exist")
58
+
59
+ # If fetchone returns a tuple
60
+ if isinstance(table_exists, tuple) and table_exists[0] == 0:
61
+ raise ConnectionHandlerError(f"Table '{self.config.database}.{table_name}' doesn't exist")
62
+
34
63
  async def get_tables(self) -> list[types.Resource]:
35
64
  """Get all table resources"""
36
65
  try:
@@ -119,24 +148,30 @@ class MySQLHandler(ConnectionHandler):
119
148
  self.log("debug", f"Executing query: {sql}")
120
149
 
121
150
  with conn.cursor(dictionary=True) as cur: # NOSONAR
122
- # Start read-only transaction
123
- cur.execute("SET TRANSACTION READ ONLY")
151
+ # Check if the query is a SELECT statement
152
+ sql_upper = sql.strip().upper()
153
+ is_select = sql_upper.startswith("SELECT")
154
+
155
+ # Only set read-only transaction for SELECT statements
156
+ if is_select:
157
+ cur.execute("SET TRANSACTION READ ONLY")
124
158
  try:
125
159
  cur.execute(sql)
126
- results = cur.fetchall()
160
+ if not is_select:
161
+ conn.commit()
162
+ results = cur.fetchall() if is_select else []
163
+ if cur.description is None: # DDL statements
164
+ return "Query executed successfully"
127
165
  columns = [desc[0] for desc in cur.description]
128
-
129
- result_text = str({
130
- 'type': self.db_type,
131
- 'columns': columns,
132
- 'rows': results,
133
- 'row_count': len(results)
166
+ return str({
167
+ "columns": columns,
168
+ "rows": results
134
169
  })
135
-
136
- self.log("debug", f"Query completed, returned {len(results)} rows")
137
- return result_text
170
+ except mysql.connector.Error as e:
171
+ self.log("error", f"Query error: {str(e)}")
172
+ raise ConnectionHandlerError(str(e))
138
173
  finally:
139
- cur.execute("ROLLBACK")
174
+ cur.close()
140
175
  except mysql.connector.Error as e:
141
176
  error_msg = f"[{self.db_type}] Query execution failed: {str(e)}"
142
177
  raise ConnectionHandlerError(error_msg)
@@ -151,6 +186,9 @@ class MySQLHandler(ConnectionHandler):
151
186
  conn_params = self.config.get_connection_params()
152
187
  conn = mysql.connector.connect(**conn_params)
153
188
  with conn.cursor(dictionary=True) as cur: # NOSONAR
189
+ # Check if table exists
190
+ await self._check_table_exists(cur, table_name)
191
+
154
192
  # Get table information and comment
155
193
  cur.execute("""
156
194
  SELECT
@@ -243,6 +281,9 @@ class MySQLHandler(ConnectionHandler):
243
281
  conn_params = self.config.get_connection_params()
244
282
  conn = mysql.connector.connect(**conn_params)
245
283
  with conn.cursor(dictionary=True) as cur: # NOSONAR
284
+ # Check if table exists
285
+ await self._check_table_exists(cur, table_name)
286
+
246
287
  # Get index information
247
288
  cur.execute("""
248
289
  SELECT
@@ -302,6 +343,9 @@ class MySQLHandler(ConnectionHandler):
302
343
  conn_params = self.config.get_connection_params()
303
344
  conn = mysql.connector.connect(**conn_params)
304
345
  with conn.cursor(dictionary=True) as cur: # NOSONAR
346
+ # Check if table exists
347
+ await self._check_table_exists(cur, table_name)
348
+
305
349
  # Get table statistics
306
350
  cur.execute("""
307
351
  SELECT
@@ -367,6 +411,9 @@ class MySQLHandler(ConnectionHandler):
367
411
  conn_params = self.config.get_connection_params()
368
412
  conn = mysql.connector.connect(**conn_params)
369
413
  with conn.cursor(dictionary=True) as cur: # NOSONAR
414
+ # Check if table exists
415
+ await self._check_table_exists(cur, table_name)
416
+
370
417
  # Get constraint information
371
418
  cur.execute("""
372
419
  SELECT
@@ -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
- # Only allow SELECT statements
82
- if not sql.strip().upper().startswith("SELECT"):
83
- raise ConnectionHandlerError("cannot execute DELETE statement")
84
-
85
- with sqlite3.connect(self.config.path) as conn:
86
- conn.row_factory = sqlite3.Row
87
- cur = conn.cursor()
88
- self.log("debug", f"Executing query: {sql}")
89
-
90
- cur.execute(sql)
91
- results = cur.fetchall()
92
- rows = [dict(row) for row in results]
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")
93
87
 
94
- result_text = str({
95
- 'type': self.db_type,
96
- 'columns': list(rows[0].keys()) if rows else [],
97
- 'rows': rows,
98
- 'row_count': len(rows)
99
- })
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()
100
93
 
101
- self.log("debug", f"Query completed, returned {len(rows)} rows")
102
- return result_text
94
+ try:
95
+ start_time = time.time()
96
+ cur.execute(sql)
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)
113
+
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)
@@ -173,6 +197,12 @@ 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()
@@ -221,6 +251,11 @@ class SQLiteHandler(ConnectionHandler):
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()
@@ -382,6 +417,13 @@ class SQLiteHandler(ConnectionHandler):
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()
@@ -0,0 +1,308 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-dbutils
3
+ Version: 0.17.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 Database Utilities
27
+
28
+ <!-- 项目状态徽章 -->
29
+ [![Build Status](https://img.shields.io/github/workflow/status/donghao1393/mcp-dbutils/Quality%20Assurance?label=tests)](https://github.com/donghao1393/mcp-dbutils/actions)
30
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/donghao1393/bdd0a63ec2a816539ff8c136ceb41e48/raw/coverage.json)](https://github.com/donghao1393/mcp-dbutils/actions)
31
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=donghao1393_mcp-dbutils&metric=alert_status)](https://sonarcloud.io/dashboard?id=donghao1393_mcp-dbutils)
32
+
33
+ <!-- 版本和安装徽章 -->
34
+ [![PyPI version](https://img.shields.io/pypi/v/mcp-dbutils)](https://pypi.org/project/mcp-dbutils/)
35
+ [![PyPI downloads](https://img.shields.io/pypi/dm/mcp-dbutils)](https://pypi.org/project/mcp-dbutils/)
36
+ [![Smithery](https://smithery.ai/badge/@donghao1393/mcp-dbutils)](https://smithery.ai/server/@donghao1393/mcp-dbutils)
37
+
38
+ <!-- 技术规格徽章 -->
39
+ [![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
40
+ [![License](https://img.shields.io/github/license/donghao1393/mcp-dbutils)](LICENSE)
41
+ [![GitHub Stars](https://img.shields.io/github/stars/donghao1393/mcp-dbutils?style=social)](https://github.com/donghao1393/mcp-dbutils/stargazers)
42
+
43
+ [中文文档](README_CN.md) | [Technical Guide](docs/technical-guide.md)
44
+
45
+ ## What is MCP Database Utilities?
46
+
47
+ MCP Database Utilities is an all-in-one MCP service that enables your AI to do data analysis by accessing versatile types of database (SQLite, MySQL, PostgreSQL, and more) within a unified connection configuration in a safe way.
48
+
49
+ Think of it as a secure bridge between AI systems and your databases, allowing AI to read and analyze your data without direct database access or risking data modifications.
50
+
51
+ ## Security and Privacy: Our Top Priority
52
+
53
+ MCP Database Utilities is built with a **security-first architecture**, making it ideal for businesses, startups, and individuals who value data protection. Our comprehensive security measures include:
54
+
55
+ ### Data Protection
56
+
57
+ - **Strictly Read-Only**: All operations are limited to SELECT queries only - data cannot be modified
58
+ - **No Direct Database Access**: AI interacts with your database through our secure service, never directly
59
+ - **Isolated Connections**: Each database connection is managed separately and strictly isolated
60
+ - **On-Demand Connectivity**: Connects only when needed and disconnects immediately after task completion
61
+ - **Automatic Timeouts**: Long-running operations are automatically terminated to prevent resource abuse
62
+
63
+ ### Privacy Safeguards
64
+
65
+ - **Local Processing**: All data processing occurs on your local machine - no data sent to external servers
66
+ - **Minimal Data Exposure**: Only requested data is returned, limiting exposure scope
67
+ - **Credential Protection**: Connection credentials are never exposed to the AI model
68
+ - **Sensitive Data Masking**: Passwords and connection details are automatically hidden in logs
69
+
70
+ ### Enterprise-Ready Security
71
+
72
+ - **SSL/TLS Support**: Encrypted connections to remote databases
73
+ - **Configuration Separation**: YAML configuration files eliminate interpretation risks
74
+ - **User-Controlled Access**: You decide which databases are accessible
75
+ - **Secure Default Settings**: Secure by default with no additional configuration needed
76
+
77
+ For technical details about our security architecture, see the [Technical Guide](docs/technical-guide.md#通信模式与安全架构).
78
+
79
+ ## Why Use MCP Database Utilities?
80
+
81
+ - **Universal AI Support**: Works with any AI system that supports the MCP protocol
82
+ - **Multiple Database Support**: Connect to SQLite, MySQL, PostgreSQL with the same interface
83
+ - **Simple Configuration**: Single YAML file for all your database connections
84
+ - **Advanced Capabilities**: Table exploration, schema analysis, and query execution
85
+
86
+ ## System Requirements
87
+
88
+ - Python 3.10 or higher
89
+ - One of the following:
90
+ - **For uvx installation**: uv package manager
91
+ - **For Docker installation**: Docker Desktop
92
+ - **For Smithery installation**: Node.js 14+
93
+ - Supported databases:
94
+ - SQLite 3.x
95
+ - PostgreSQL 12+
96
+ - MySQL 8+
97
+ - Supported AI clients:
98
+ - Claude Desktop
99
+ - Cursor
100
+ - Any MCP-compatible client
101
+
102
+ ## Getting Started
103
+
104
+ ### 1. Installation Guide
105
+
106
+ Choose **ONE** of the following methods to install:
107
+
108
+ #### Option A: Using uvx (Recommended)
109
+
110
+ This method uses `uvx`, which is part of the Python package manager tool called "uv". Here's how to set it up:
111
+
112
+ 1. **Install uv and uvx first:**
113
+
114
+ **On macOS or Linux:**
115
+ ```bash
116
+ curl -LsSf https://astral.sh/uv/install.sh | sh
117
+ ```
118
+
119
+ **On Windows:**
120
+ ```powershell
121
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
122
+ ```
123
+
124
+ After installation, verify that uv is installed correctly:
125
+ ```bash
126
+ uv --version
127
+ # Should display something like: uv 0.5.5 (Homebrew 2024-11-27)
128
+ ```
129
+
130
+ 2. **Create a configuration file** named `config.yaml` with your database connection details:
131
+
132
+ ```yaml
133
+ connections:
134
+ my-sqlite:
135
+ type: sqlite
136
+ path: /path/to/my-database.db
137
+
138
+ my-postgres:
139
+ type: postgres
140
+ host: localhost
141
+ port: 5432
142
+ dbname: my_database
143
+ user: my_user
144
+ password: my_password
145
+ ```
146
+
147
+ > For advanced configuration options (SSL connections, connection pooling, etc.),
148
+ > please refer to the [Configuration System Details](docs/technical-guide.md#configuration-system-details) section in our technical guide.
149
+
150
+ 3. **Add this configuration to your AI client:**
151
+
152
+ **For JSON-based MCP clients:**
153
+ - Locate and edit your client's MCP configuration file:
154
+ - **Claude Desktop (Mac)**: `~/Library/Application Support/Claude/claude_desktop_config.json`
155
+ - **Cline (Mac)**: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
156
+ - **Other clients**: Refer to your client's documentation for the MCP configuration file location
157
+ - Add the following configuration to the JSON file:
158
+
159
+ ```json
160
+ "dbutils": {
161
+ "command": "uvx",
162
+ "args": [
163
+ "mcp-dbutils",
164
+ "--config",
165
+ "/full/path/to/your/config.yaml"
166
+ ]
167
+ }
168
+ ```
169
+
170
+ **For Cursor:**
171
+ - Open Cursor
172
+ - Go to Settings → MCP
173
+ - Click "Add MCP Server" and fill in:
174
+ - Name: `Database Utility MCP`
175
+ - Type: `Command` (default)
176
+ - Command: `uvx mcp-dbutils --config /full/path/to/your/config.yaml`
177
+
178
+ > **Important Notes for uvx Setup:**
179
+ > - Replace `/full/path/to/your/config.yaml` with the actual full path to your config file
180
+ > - If you get an error about uvx not being found, make sure step 1 was completed successfully
181
+ > - You can verify uvx is installed by typing `uvx --version` in your terminal
182
+
183
+ #### Option B: Manual Installation with Docker
184
+
185
+ 1. Install Docker from [docker.com](https://www.docker.com/products/docker-desktop/) if you don't have it
186
+
187
+ 2. Create a configuration file (see next section for details)
188
+
189
+ 3. Add this configuration to your AI client:
190
+
191
+ **For JSON-based MCP clients:**
192
+ - Locate and edit your client's MCP configuration file:
193
+ - **Claude Desktop (Mac)**: `~/Library/Application Support/Claude/claude_desktop_config.json`
194
+ - **Cline (Mac)**: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
195
+ - **Other clients**: Refer to your client's documentation for the MCP configuration file location
196
+ - Add the following configuration to the JSON file:
197
+
198
+ ```json
199
+ "dbutils": {
200
+ "command": "docker",
201
+ "args": [
202
+ "run",
203
+ "-i",
204
+ "--rm",
205
+ "-v",
206
+ "/full/path/to/your/config.yaml:/app/config.yaml",
207
+ "-v",
208
+ "/full/path/to/your/sqlite.db:/app/sqlite.db", // Only needed for SQLite
209
+ "mcp/dbutils",
210
+ "--config",
211
+ "/app/config.yaml"
212
+ ]
213
+ }
214
+ ```
215
+
216
+ **For Cursor:**
217
+ - Open Cursor
218
+ - Go to Settings → MCP
219
+ - Click "Add MCP Server" and fill in:
220
+ - Name: `Database Utility MCP`
221
+ - Type: `Command` (default)
222
+ - Command: `docker run -i --rm -v /full/path/to/your/config.yaml:/app/config.yaml -v /full/path/to/your/sqlite.db:/app/sqlite.db mcp/dbutils --config /app/config.yaml`
223
+
224
+ > **Important Notes for Docker:**
225
+ > - Replace `/full/path/to/your/config.yaml` with the actual full path to your config file
226
+ > - For SQLite databases, also replace the sqlite.db path with your actual database path
227
+ > - For other database types, remove the SQLite volume line entirely
228
+
229
+ #### Option C: Using Smithery (One-Click for Claude)
230
+
231
+ This method automatically installs AND configures the service for Claude:
232
+
233
+ ```bash
234
+ npx -y @smithery/cli install @donghao1393/mcp-dbutils --client claude
235
+ ```
236
+
237
+ After installation completes, skip to the "Using the Service" section.
238
+
239
+ ### 4. Using the Service
240
+
241
+ Once installed and configured properly, your AI can now:
242
+ - List tables in your database
243
+ - View table structures
244
+ - Execute SQL queries safely
245
+ - Analyze data across multiple databases
246
+
247
+ **To verify everything is working:**
248
+
249
+ 1. Ask your AI something like: "Can you check if you're able to connect to my database?"
250
+ 2. If properly configured, the AI should reply that it can connect to the database specified in your config file
251
+ 3. Try a simple command like: "List the tables in my database"
252
+
253
+ If you encounter any issues, check:
254
+ - Your configuration file syntax is correct
255
+ - The database connection details are accurate
256
+ - Your AI client has the MCP server properly configured
257
+ - Your database is accessible from your computer
258
+
259
+ ## Example Interactions
260
+
261
+ **You**: "Can you list all the tables in my my-postgres database?"
262
+
263
+ **AI**: "I'll check that for you. Here are the tables in your my-postgres database:
264
+ - customers
265
+ - products
266
+ - orders
267
+ - inventory
268
+ - employees"
269
+
270
+ **You**: "What does the customers table look like?"
271
+
272
+ **AI**: "The customers table has the following structure:
273
+ - id (integer, primary key)
274
+ - name (text)
275
+ - email (text)
276
+ - registration_date (date)
277
+ - last_purchase (date)
278
+ - total_spent (numeric)"
279
+
280
+ **You**: "How many customers made purchases in the last month?"
281
+
282
+ **AI**: "Let me run a query to find out... According to the data, 128 customers made purchases in the last month. The total value of these purchases was $25,437.82."
283
+
284
+ ## Available Tools
285
+
286
+ MCP Database Utilities provides several tools that your AI can use:
287
+
288
+ - **dbutils-list-tables**: Lists all tables in a database
289
+ - **dbutils-run-query**: Executes a SQL query (SELECT only)
290
+ - **dbutils-get-stats**: Gets statistics about a table
291
+ - **dbutils-list-constraints**: Lists table constraints
292
+ - **dbutils-explain-query**: Gets query execution plan
293
+ - **dbutils-get-performance**: Gets database performance metrics
294
+ - **dbutils-analyze-query**: Analyzes queries for optimization
295
+
296
+ ## Need More Help?
297
+
298
+ - [Technical Documentation](docs/technical-guide.md) - For developers and advanced users
299
+ - [GitHub Issues](https://github.com/donghao1393/mcp-dbutils/issues) - Report bugs or request features
300
+ - [Smithery](https://smithery.ai/server/@donghao1393/mcp-dbutils) - Simplified installation and updates
301
+
302
+ ## Star History
303
+
304
+ [![Star History Chart](https://api.star-history.com/svg?repos=donghao1393/mcp-dbutils&type=Date)](https://star-history.com/#donghao1393/mcp-dbutils&Date)
305
+
306
+ ## License
307
+
308
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -5,7 +5,7 @@ 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=knBoFVYmdse5hsjr4GPi4fZhEaYOPRBPGR2d3w8qqzw,19837
8
+ mcp_dbutils/mysql/handler.py,sha256=fVmOq49ZDsB24NOkHCMu5e4WwMFEV8hBkElkNA8FFJo,21962
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
@@ -13,10 +13,10 @@ mcp_dbutils/postgres/handler.py,sha256=ppltSKtSk-BlPpp3iEVJlmoyl4AmqKcQHx_0zHlz0
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=25zqoQpMhRNKeO3MH2a0E5dO3-4A8sPb7q87cn7Cs0E,17746
16
+ mcp_dbutils/sqlite/handler.py,sha256=iojyVhr_cgNOATKESXdSFYV58mdZQhywwDQVd4jXtvM,19740
17
17
  mcp_dbutils/sqlite/server.py,sha256=jqpE8d9vJETMs5xYGB7P0tvNDPes6Yn5ZM_iCCF7Tv4,7181
18
- mcp_dbutils-0.16.1.dist-info/METADATA,sha256=mjE8oqdIRiIdiRlyWufFfxWGoHT8aGIoSaVTEuFlkJs,16714
19
- mcp_dbutils-0.16.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- mcp_dbutils-0.16.1.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
21
- mcp_dbutils-0.16.1.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
22
- mcp_dbutils-0.16.1.dist-info/RECORD,,
18
+ mcp_dbutils-0.17.0.dist-info/METADATA,sha256=ZuJS8qq9D-Ru7TpXq18fQeTCB83hif0qR6luEMaAi1M,12075
19
+ mcp_dbutils-0.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ mcp_dbutils-0.17.0.dist-info/entry_points.txt,sha256=XTjt0QmYRgKOJQT6skR9bp1EMUfIrgpHeZJPZ3CJffs,49
21
+ mcp_dbutils-0.17.0.dist-info/licenses/LICENSE,sha256=1A_CwpWVlbjrKdVEYO77vYfnXlW7oxcilZ8FpA_BzCI,1065
22
+ mcp_dbutils-0.17.0.dist-info/RECORD,,
@@ -1,572 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: mcp-dbutils
3
- Version: 0.16.1
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 Database Utilities
27
-
28
- ![GitHub Repo stars](https://img.shields.io/github/stars/donghao1393/mcp-dbutils)
29
- ![PyPI version](https://img.shields.io/pypi/v/mcp-dbutils)
30
- [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/donghao1393/bdd0a63ec2a816539ff8c136ceb41e48/raw/coverage.json)](https://github.com/donghao1393/mcp-dbutils/actions)
31
- ![Python](https://img.shields.io/badge/Python-3.10%2B-blue)
32
- ![License](https://img.shields.io/github/license/donghao1393/mcp-dbutils)
33
- [![smithery badge](https://smithery.ai/badge/@donghao1393/mcp-dbutils)](https://smithery.ai/server/@donghao1393/mcp-dbutils)
34
-
35
- [中文文档](README_CN.md)
36
-
37
- ## Overview
38
- MCP Database Utilities is a unified database access service that supports multiple database types (PostgreSQL, SQLite, and MySQL). Through its abstraction layer design, it provides a simple and unified database operation interface for MCP servers.
39
-
40
- ## Features
41
- - Unified database access interface
42
- - Support for multiple database configurations
43
- - Secure read-only query execution
44
- - Table structure and schema information retrieval
45
- - Database tables listing via MCP tools
46
- - Intelligent connection management and resource cleanup
47
- - Debug mode support
48
- - SSL/TLS connection support for PostgreSQL and MySQL
49
-
50
- ## Installation and Configuration
51
-
52
- ### Installation Methods
53
- #### Installing via Smithery
54
-
55
- To install Database Utilities for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@donghao1393/mcp-dbutils):
56
-
57
- ```bash
58
- npx -y @smithery/cli install @donghao1393/mcp-dbutils --client claude
59
- ```
60
-
61
- #### Using uvx (Recommended)
62
- No installation required, run directly using `uvx`:
63
- ```bash
64
- uvx mcp-dbutils --config /path/to/config.yaml
65
- ```
66
-
67
- Add to Claude configuration:
68
- ```json
69
- "mcpServers": {
70
- "dbutils": {
71
- "command": "uvx",
72
- "args": [
73
- "mcp-dbutils",
74
- "--config",
75
- "/path/to/config.yaml"
76
- ],
77
- "env": {
78
- "MCP_DEBUG": "1" // Optional: Enable debug mode
79
- }
80
- }
81
- }
82
- ```
83
-
84
- #### Using pip
85
- ```bash
86
- pip install mcp-dbutils
87
- ```
88
-
89
- Add to Claude configuration:
90
- ```json
91
- "mcpServers": {
92
- "dbutils": {
93
- "command": "python",
94
- "args": [
95
- "-m",
96
- "mcp_dbutils",
97
- "--config",
98
- "/path/to/config.yaml"
99
- ],
100
- "env": {
101
- "MCP_DEBUG": "1" // Optional: Enable debug mode
102
- }
103
- }
104
- }
105
- ```
106
-
107
- #### Using Docker
108
- ```bash
109
- docker run -i --rm \
110
- -v /path/to/config.yaml:/app/config.yaml \
111
- -v /path/to/sqlite.db:/app/sqlite.db \ # Optional: for SQLite database
112
- -e MCP_DEBUG=1 \ # Optional: Enable debug mode
113
- mcp/dbutils --config /app/config.yaml
114
- ```
115
-
116
- Add to Claude configuration:
117
- ```json
118
- "mcpServers": {
119
- "dbutils": {
120
- "command": "docker",
121
- "args": [
122
- "run",
123
- "-i",
124
- "--rm",
125
- "-v",
126
- "/path/to/config.yaml:/app/config.yaml",
127
- "-v",
128
- "/path/to/sqlite.db:/app/sqlite.db", // Optional: for SQLite database
129
- "mcp/dbutils",
130
- "--config",
131
- "/app/config.yaml"
132
- ],
133
- "env": {
134
- "MCP_DEBUG": "1" // Optional: Enable debug mode
135
- }
136
- }
137
- }
138
- ```
139
-
140
- > **Note for Docker database connections:**
141
- > - For SQLite: Mount your database file using `-v /path/to/sqlite.db:/app/sqlite.db`
142
- > - For PostgreSQL running on host:
143
- > - On Mac/Windows: Use `host.docker.internal` as host in config
144
- > - On Linux: Use `172.17.0.1` (docker0 IP) or run with `--network="host"`
145
-
146
- ### Requirements
147
- - Python 3.10+
148
- - PostgreSQL (optional)
149
- - SQLite3 (optional)
150
- - MySQL (optional)
151
-
152
- ### Configuration File
153
- The project requires a YAML configuration file, specified via the `--config` parameter. Configuration examples:
154
-
155
- ```yaml
156
- connections:
157
- # SQLite configuration examples
158
- dev-db:
159
- type: sqlite
160
- path: /path/to/dev.db
161
- # Password is optional
162
- password:
163
-
164
- # PostgreSQL standard configuration
165
- test-db:
166
- type: postgres
167
- host: postgres.example.com
168
- port: 5432
169
- dbname: test_db
170
- user: test_user
171
- password: test_pass
172
-
173
- # PostgreSQL URL configuration with SSL
174
- prod-db:
175
- type: postgres
176
- url: postgresql://postgres.example.com:5432/prod-db?sslmode=verify-full
177
- user: prod_user
178
- password: prod_pass
179
-
180
- # PostgreSQL full SSL configuration example
181
- secure-db:
182
- type: postgres
183
- host: secure-db.example.com
184
- port: 5432
185
- dbname: secure_db
186
- user: secure_user
187
- password: secure_pass
188
- ssl:
189
- mode: verify-full # disable/require/verify-ca/verify-full
190
- cert: /path/to/client-cert.pem
191
- key: /path/to/client-key.pem
192
- root: /path/to/root.crt
193
-
194
- # MySQL standard configuration
195
- sandbox-mysql:
196
- type: mysql
197
- host: localhost
198
- port: 3306
199
- database: sandbox_db
200
- user: sandbox_user
201
- password: sandbox_pass
202
- charset: utf8mb4
203
-
204
- # MySQL URL configuration
205
- integration-mysql:
206
- type: mysql
207
- url: mysql://mysql.example.com:3306/integration_db?charset=utf8mb4
208
- user: integration_user
209
- password: integration_pass
210
-
211
- # MySQL with SSL configuration
212
- secure-mysql:
213
- type: mysql
214
- host: secure-mysql.example.com
215
- port: 3306
216
- database: secure_db
217
- user: secure_user
218
- password: secure_pass
219
- charset: utf8mb4
220
- ssl:
221
- mode: verify_identity
222
- ca: /path/to/ca.pem
223
- cert: /path/to/client-cert.pem
224
- key: /path/to/client-key.pem
225
- ```
226
-
227
- Database SSL Configuration Options:
228
-
229
- PostgreSQL SSL Configuration:
230
- 1. Using URL parameters:
231
- ```
232
- postgresql://host:port/dbname?sslmode=verify-full&sslcert=/path/to/cert.pem
233
- ```
234
- 2. Using dedicated SSL configuration section:
235
- ```yaml
236
- ssl:
237
- mode: verify-full # SSL verification mode
238
- cert: /path/to/cert.pem # Client certificate
239
- key: /path/to/key.pem # Client private key
240
- root: /path/to/root.crt # CA certificate
241
- ```
242
-
243
- PostgreSQL SSL Modes:
244
- - disable: No SSL
245
- - require: Use SSL but no certificate verification
246
- - verify-ca: Verify server certificate is signed by trusted CA
247
- - verify-full: Verify server certificate and hostname match
248
-
249
- MySQL SSL Configuration:
250
- 1. Using URL parameters:
251
- ```
252
- mysql://host:port/dbname?ssl-mode=verify_identity&ssl-ca=/path/to/ca.pem
253
- ```
254
- 2. Using dedicated SSL configuration section:
255
- ```yaml
256
- ssl:
257
- mode: verify_identity # SSL verification mode
258
- ca: /path/to/ca.pem # CA certificate
259
- cert: /path/to/cert.pem # Client certificate
260
- key: /path/to/key.pem # Client private key
261
- ```
262
-
263
- MySQL SSL Modes:
264
- - disabled: No SSL
265
- - preferred: Use SSL if available, but allow unencrypted connection
266
- - required: Always use SSL, but don't verify server certificate
267
- - verify_ca: Verify server certificate is signed by trusted CA
268
- - verify_identity: Verify server certificate and hostname match
269
-
270
- SQLite Configuration Options:
271
- 1. Basic configuration with path:
272
- ```yaml
273
- type: sqlite
274
- path: /path/to/db.sqlite
275
- password: optional_password # Optional encryption
276
- ```
277
- 2. Using URI parameters:
278
- ```yaml
279
- type: sqlite
280
- path: /path/to/db.sqlite?mode=ro&cache=shared
281
- ```
282
-
283
- ### Debug Mode
284
- Set environment variable `MCP_DEBUG=1` to enable debug mode for detailed logging output.
285
-
286
- ## Architecture Design
287
-
288
- ### Core Concept: Abstraction Layer
289
-
290
- ```mermaid
291
- graph TD
292
- Client[Client] --> DatabaseServer[Database Server]
293
- subgraph MCP Server
294
- DatabaseServer
295
- DatabaseHandler[Database Handler]
296
- PostgresHandler[PostgreSQL Handler]
297
- SQLiteHandler[SQLite Handler]
298
- MySQLHandler[MySQL Handler]
299
- DatabaseServer --> DatabaseHandler
300
- DatabaseHandler --> PostgresHandler
301
- DatabaseHandler --> SQLiteHandler
302
- DatabaseHandler --> MySQLHandler
303
- end
304
- PostgresHandler --> PostgreSQL[(PostgreSQL)]
305
- SQLiteHandler --> SQLite[(SQLite)]
306
- MySQLHandler --> MySQL[(MySQL)]
307
- ```
308
-
309
- The abstraction layer design is the core architectural concept in MCP Database Utilities. Just like a universal remote control that works with different devices, users only need to know the basic operations without understanding the underlying complexities.
310
-
311
- #### 1. Simplified User Interaction
312
- - Users only need to know the database configuration name (e.g., "my_postgres")
313
- - No need to deal with connection parameters and implementation details
314
- - MCP server automatically handles database connections and queries
315
-
316
- #### 2. Unified Interface Design
317
- - DatabaseHandler abstract class defines unified operation interfaces
318
- - All specific database implementations (PostgreSQL/SQLite/MySQL) follow the same interface
319
- - Users interact with different databases in the same way
320
-
321
- #### 3. Configuration and Implementation Separation
322
- - Complex database configuration parameters are encapsulated in configuration files
323
- - Runtime access through simple database names
324
- - Easy management and modification of database configurations without affecting business code
325
-
326
- ### System Components
327
- 1. DatabaseServer
328
- - Core component of the MCP server
329
- - Handles resource and tool requests
330
- - Manages database connection lifecycle
331
-
332
- 2. DatabaseHandler
333
- - Abstract base class defining unified interface
334
- - Includes get_tables(), get_schema(), execute_query(), etc.
335
- - Implemented by PostgreSQL, SQLite, and MySQL handlers
336
-
337
- 3. Configuration System
338
- - YAML-based configuration file
339
- - Support for multiple database configurations
340
- - Type-safe configuration validation
341
-
342
- 4. Error Handling and Logging
343
- - Unified error handling mechanism
344
- - Detailed logging output
345
- - Sensitive information masking
346
-
347
- ## Usage Examples
348
-
349
- ### Basic Query
350
- ```python
351
- # Access through connection name
352
- async with server.get_handler("my_postgres") as handler:
353
- # Execute SQL query
354
- result = await handler.execute_query("SELECT * FROM users")
355
- ```
356
-
357
- ### View Table Structure
358
- ```python
359
- # Get all tables
360
- tables = await handler.get_tables()
361
-
362
- # Get specific table schema
363
- schema = await handler.get_schema("users")
364
- ```
365
-
366
- ### Error Handling
367
- ```python
368
- try:
369
- async with server.get_handler("my_connection") as handler:
370
- result = await handler.execute_query("SELECT * FROM users")
371
- except ValueError as e:
372
- print(f"Configuration error: {e}")
373
- except Exception as e:
374
- print(f"Query error: {e}")
375
- ```
376
-
377
- ## Security Notes
378
- - Supports SELECT queries only to protect database security
379
- - Automatically masks sensitive information (like passwords) in logs
380
- - Executes queries in read-only transactions
381
-
382
- ## API Documentation
383
-
384
- ### DatabaseServer
385
- Core server class providing:
386
- - Resource list retrieval
387
- - Tool call handling (list_tables, query)
388
- - Database handler management
389
-
390
- ### MCP Tools
391
-
392
- #### dbutils-list-tables
393
- Lists all tables in the specified database.
394
- - Parameters:
395
- * connection: Database connection name
396
- - Returns: Text content with a list of table names
397
-
398
- #### dbutils-run-query
399
- Executes a SQL query on the specified database.
400
- - Parameters:
401
- * connection: Database connection name
402
- * sql: SQL query to execute (SELECT only)
403
- - Returns: Query results in a formatted text
404
-
405
- #### dbutils-get-stats
406
- Get table statistics information.
407
- - Parameters:
408
- * connection: Database connection name
409
- * table: Table name
410
- - Returns: Statistics including row count, size, column stats
411
-
412
- #### dbutils-list-constraints
413
- List table constraints (primary key, foreign keys, etc).
414
- - Parameters:
415
- * connection: Database connection name
416
- * table: Table name
417
- - Returns: Detailed constraint information
418
-
419
- #### dbutils-explain-query
420
- Get query execution plan with cost estimates.
421
- - Parameters:
422
- * connection: Database connection name
423
- * sql: SQL query to explain
424
- - Returns: Formatted execution plan
425
-
426
- #### dbutils-get-performance
427
- Get database performance statistics.
428
- - Parameters:
429
- * connection: Database connection name
430
- - Returns: Detailed performance statistics including query times, query types, error rates, and resource usage
431
-
432
- #### dbutils-analyze-query
433
- Analyze a SQL query for performance and provide optimization suggestions.
434
- - Parameters:
435
- * connection: Database connection name
436
- * sql: SQL query to analyze
437
- - Returns: Query analysis with execution plan, timing information, and optimization suggestions
438
-
439
- ### DatabaseHandler
440
- Abstract base class defining interfaces:
441
- - get_tables(): Get table resource list
442
- - get_schema(): Get table structure
443
- - execute_query(): Execute SQL query
444
- - cleanup(): Resource cleanup
445
-
446
- ### PostgreSQL Implementation
447
- Provides PostgreSQL-specific features:
448
- - Remote connection support
449
- - Table description information
450
- - Constraint queries
451
-
452
- ### SQLite Implementation
453
- Provides SQLite-specific features:
454
- - File path handling
455
- - URI scheme support
456
- - Password protection support (optional)
457
-
458
- ### MySQL Implementation
459
- Provides MySQL-specific features:
460
- - Remote connection support
461
- - Character set configuration
462
- - SSL/TLS secure connection
463
- - URL and standard connection methods
464
-
465
- ## Code Quality
466
-
467
- ### Quality Gates
468
- We use SonarCloud to maintain high code quality standards. All pull requests must pass the following quality gates:
469
-
470
- - Code Coverage: ≥ 80%
471
- - Code Quality:
472
- * No blocker or critical issues
473
- * Less than 10 major issues
474
- * Code duplication < 3%
475
- - Security:
476
- * No security vulnerabilities
477
- * No security hotspots
478
-
479
- ### Automated Checks
480
- Our CI/CD pipeline automatically performs:
481
- 1. Full test suite execution
482
- 2. Code coverage analysis
483
- 3. SonarCloud static code analysis
484
- 4. Quality gate validation
485
-
486
- Pull requests that don't meet these standards will be automatically blocked from merging.
487
-
488
- ### Code Style
489
- We use Ruff for code style checking and formatting:
490
-
491
- [![Code Style](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
492
-
493
- All code must follow our style guide:
494
- - Line length: 88 characters
495
- - Indentation: 4 spaces
496
- - Quotes: Double quotes
497
- - Naming: PEP8 conventions
498
-
499
- For detailed guidelines, see [STYLE_GUIDE.md](docs/STYLE_GUIDE.md).
500
-
501
- ### Local Development
502
- To check code quality locally:
503
- 1. Run tests with coverage:
504
- ```bash
505
- pytest --cov=src/mcp_dbutils --cov-report=xml:coverage.xml tests/
506
- ```
507
- 2. Use SonarLint in your IDE to catch issues early
508
- 3. Review SonarCloud analysis results in PR comments
509
- 4. Run Ruff for code style checking:
510
- ```bash
511
- # Install Ruff
512
- uv pip install ruff
513
-
514
- # Check code style
515
- ruff check .
516
-
517
- # Format code
518
- ruff format .
519
- ```
520
- 5. Use pre-commit hooks for automatic checks:
521
- ```bash
522
- # Install pre-commit
523
- uv pip install pre-commit
524
- pre-commit install
525
-
526
- # Run all checks
527
- pre-commit run --all-files
528
- ```
529
-
530
- ### SonarCloud AI Integration
531
- We've implemented an AI-assisted workflow for fixing SonarCloud issues:
532
-
533
- 1. Our CI/CD pipeline automatically extracts SonarCloud analysis results
534
- 2. Results are formatted into both JSON and Markdown formats
535
- 3. These reports can be downloaded using the provided Fish function
536
- 4. The reports can then be provided to AI tools for analysis and fix suggestions
537
-
538
- For detailed instructions, see [SonarCloud AI Integration Guide](docs/sonarcloud-ai-integration.md).
539
-
540
- ```bash
541
- # Load the function
542
- source scripts/sonar-ai-fix.fish
543
-
544
- # Download the latest SonarCloud analysis reports
545
- sonar-ai-fix
546
- ```
547
-
548
- ## Contributing
549
- Contributions are welcome! Here's how you can help:
550
-
551
- 1. 🐛 Report bugs: Open an issue describing the bug and how to reproduce it
552
- 2. 💡 Suggest features: Open an issue to propose new features
553
- 3. 🛠️ Submit PRs: Fork the repo and create a pull request with your changes
554
-
555
- ### Development Setup
556
- 1. Clone the repository
557
- 2. Create a virtual environment using `uv venv`
558
- 3. Install dependencies with `uv sync --all-extras`
559
- 4. Run tests with `pytest`
560
-
561
- For detailed guidelines, see [CONTRIBUTING.md](.github/CONTRIBUTING.md)
562
-
563
- ## Acknowledgments
564
- - [MCP Servers](https://github.com/modelcontextprotocol/servers) for inspiration and demonstration
565
- - AI Editors:
566
- * [Claude Desktop](https://claude.ai/download)
567
- * [Cline](https://cline.bot)
568
- - [Model Context Protocol](https://modelcontextprotocol.io/) for comprehensive interfaces
569
-
570
- ## Star History
571
-
572
- [![Star History Chart](https://api.star-history.com/svg?repos=donghao1393/mcp-dbutils&type=Date)](https://star-history.com/#donghao1393/mcp-dbutils&Date)