mcp-database 0.1.0__tar.gz
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_database-0.1.0/.gitignore +19 -0
- mcp_database-0.1.0/LICENSE +21 -0
- mcp_database-0.1.0/PKG-INFO +173 -0
- mcp_database-0.1.0/README.md +140 -0
- mcp_database-0.1.0/README_zh.md +158 -0
- mcp_database-0.1.0/pyproject.toml +53 -0
- mcp_database-0.1.0/src/mcp_database/__init__.py +3 -0
- mcp_database-0.1.0/src/mcp_database/__main__.py +5 -0
- mcp_database-0.1.0/src/mcp_database/adapters/__init__.py +6 -0
- mcp_database-0.1.0/src/mcp_database/adapters/base.py +108 -0
- mcp_database-0.1.0/src/mcp_database/adapters/mysql.py +173 -0
- mcp_database-0.1.0/src/mcp_database/adapters/postgres.py +174 -0
- mcp_database-0.1.0/src/mcp_database/adapters/sqlite.py +120 -0
- mcp_database-0.1.0/src/mcp_database/config.py +120 -0
- mcp_database-0.1.0/src/mcp_database/server.py +285 -0
- mcp_database-0.1.0/tests/test_sqlite_adapter.py +174 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mergewall Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-database
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for multi-database access — SQLite, PostgreSQL, MySQL. Query, inspect schema, and manage databases through Claude.
|
|
5
|
+
Author: Mergewall Team
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: claude,database,mcp,model-context-protocol,mysql,postgresql,sqlite
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: mcp>=1.0.0
|
|
20
|
+
Requires-Dist: pyyaml>=6.0
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'all'
|
|
23
|
+
Requires-Dist: pymysql>=1.1.0; extra == 'all'
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
28
|
+
Provides-Extra: mysql
|
|
29
|
+
Requires-Dist: pymysql>=1.1.0; extra == 'mysql'
|
|
30
|
+
Provides-Extra: postgres
|
|
31
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'postgres'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# mcp-database
|
|
35
|
+
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://modelcontextprotocol.io)
|
|
39
|
+
|
|
40
|
+
**MCP server for multi-database access — query, inspect schema, and manage SQLite, PostgreSQL, and MySQL databases through Claude.**
|
|
41
|
+
|
|
42
|
+
## Why mcp-database?
|
|
43
|
+
|
|
44
|
+
| Problem | Solution |
|
|
45
|
+
|---------|----------|
|
|
46
|
+
| Need to query a database from Claude Code / Claude Desktop | One MCP server, multiple database support |
|
|
47
|
+
| Existing database MCP servers are JS/Go only | Pure Python, uses official `mcp` SDK |
|
|
48
|
+
| Worried about accidental writes | Read-only by default, writes opt-in |
|
|
49
|
+
| Don't know the schema | Built-in schema inspection, table info, search |
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Install
|
|
55
|
+
pip install mcp-database
|
|
56
|
+
|
|
57
|
+
# Run with a SQLite database
|
|
58
|
+
MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Claude Code Integration
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Add to Claude Code
|
|
65
|
+
claude mcp add mcp-database -- mcp-database
|
|
66
|
+
|
|
67
|
+
# Or with a specific database
|
|
68
|
+
claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Claude Desktop Integration
|
|
72
|
+
|
|
73
|
+
Add to your `claude_desktop_config.json`:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"database": {
|
|
79
|
+
"command": "mcp-database",
|
|
80
|
+
"env": {
|
|
81
|
+
"MCP_DATABASE_URL": "sqlite:///path/to/your.db"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Supported Databases
|
|
89
|
+
|
|
90
|
+
| Database | Status | Install |
|
|
91
|
+
|----------|--------|---------|
|
|
92
|
+
| **SQLite** | Built-in | `pip install mcp-database` |
|
|
93
|
+
| **PostgreSQL** | Optional | `pip install 'mcp-database[postgres]'` |
|
|
94
|
+
| **MySQL** | Optional | `pip install 'mcp-database[mysql]'` |
|
|
95
|
+
| **All** | Optional | `pip install 'mcp-database[all]'` |
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
### Environment Variables
|
|
100
|
+
|
|
101
|
+
| Variable | Default | Description |
|
|
102
|
+
|----------|---------|-------------|
|
|
103
|
+
| `MCP_DATABASE_URL` | `sqlite:///:memory:` | Database connection URL |
|
|
104
|
+
| `MCP_DATABASE_TYPE` | `sqlite` | Database type: `sqlite`, `postgresql`, `mysql` |
|
|
105
|
+
| `MCP_DATABASE_READ_ONLY` | `true` | Enable read-only mode |
|
|
106
|
+
| `MCP_MAX_ROWS` | `100` | Maximum rows returned per query |
|
|
107
|
+
|
|
108
|
+
### Connection URLs
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# SQLite
|
|
112
|
+
MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
|
|
113
|
+
MCP_DATABASE_URL=sqlite:///:memory:
|
|
114
|
+
|
|
115
|
+
# PostgreSQL
|
|
116
|
+
MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
117
|
+
MCP_DATABASE_TYPE=postgresql
|
|
118
|
+
|
|
119
|
+
# MySQL
|
|
120
|
+
MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
|
|
121
|
+
MCP_DATABASE_TYPE=mysql
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Available Tools
|
|
125
|
+
|
|
126
|
+
Once connected, Claude can use these tools:
|
|
127
|
+
|
|
128
|
+
| Tool | Description |
|
|
129
|
+
|------|-------------|
|
|
130
|
+
| `list_databases` | List all configured database connections |
|
|
131
|
+
| `list_tables` | List all tables in a database |
|
|
132
|
+
| `get_table_info` | Get detailed table info (columns, types, row count) |
|
|
133
|
+
| `get_schema` | Get full database schema (CREATE TABLE statements) |
|
|
134
|
+
| `query` | Execute a read-only SQL query (SELECT, SHOW, DESCRIBE) |
|
|
135
|
+
| `execute` | Execute a write statement (INSERT, UPDATE, DELETE) — opt-in only |
|
|
136
|
+
| `sample_rows` | Get sample rows from a table |
|
|
137
|
+
| `search_tables` | Search for tables or columns by keyword |
|
|
138
|
+
|
|
139
|
+
## Examples
|
|
140
|
+
|
|
141
|
+
Ask Claude things like:
|
|
142
|
+
|
|
143
|
+
- "What tables are in my database?"
|
|
144
|
+
- "Show me the schema for the users table"
|
|
145
|
+
- "Query the top 10 orders by amount"
|
|
146
|
+
- "Find all columns related to 'email'"
|
|
147
|
+
- "Sample some rows from the products table"
|
|
148
|
+
|
|
149
|
+
## Security
|
|
150
|
+
|
|
151
|
+
- **Read-only by default** — queries are safe, no data modification
|
|
152
|
+
- **Write opt-in** — set `allow_writes=True` and `MCP_DATABASE_READ_ONLY=false` to enable
|
|
153
|
+
- **Read-only detection** — write tool rejects SELECT statements (use `query` instead)
|
|
154
|
+
- **Row limits** — configurable max rows to prevent accidental large result sets
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Clone and install
|
|
160
|
+
git clone https://github.com/Jansen003/mcp-database.git
|
|
161
|
+
cd mcp-database
|
|
162
|
+
pip install -e ".[dev]"
|
|
163
|
+
|
|
164
|
+
# Run tests
|
|
165
|
+
pytest
|
|
166
|
+
|
|
167
|
+
# Run with Inspector UI
|
|
168
|
+
mcp dev src/mcp_database/server.py
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# mcp-database
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
|
|
7
|
+
**MCP server for multi-database access — query, inspect schema, and manage SQLite, PostgreSQL, and MySQL databases through Claude.**
|
|
8
|
+
|
|
9
|
+
## Why mcp-database?
|
|
10
|
+
|
|
11
|
+
| Problem | Solution |
|
|
12
|
+
|---------|----------|
|
|
13
|
+
| Need to query a database from Claude Code / Claude Desktop | One MCP server, multiple database support |
|
|
14
|
+
| Existing database MCP servers are JS/Go only | Pure Python, uses official `mcp` SDK |
|
|
15
|
+
| Worried about accidental writes | Read-only by default, writes opt-in |
|
|
16
|
+
| Don't know the schema | Built-in schema inspection, table info, search |
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Install
|
|
22
|
+
pip install mcp-database
|
|
23
|
+
|
|
24
|
+
# Run with a SQLite database
|
|
25
|
+
MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Claude Code Integration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Add to Claude Code
|
|
32
|
+
claude mcp add mcp-database -- mcp-database
|
|
33
|
+
|
|
34
|
+
# Or with a specific database
|
|
35
|
+
claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Claude Desktop Integration
|
|
39
|
+
|
|
40
|
+
Add to your `claude_desktop_config.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"database": {
|
|
46
|
+
"command": "mcp-database",
|
|
47
|
+
"env": {
|
|
48
|
+
"MCP_DATABASE_URL": "sqlite:///path/to/your.db"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Supported Databases
|
|
56
|
+
|
|
57
|
+
| Database | Status | Install |
|
|
58
|
+
|----------|--------|---------|
|
|
59
|
+
| **SQLite** | Built-in | `pip install mcp-database` |
|
|
60
|
+
| **PostgreSQL** | Optional | `pip install 'mcp-database[postgres]'` |
|
|
61
|
+
| **MySQL** | Optional | `pip install 'mcp-database[mysql]'` |
|
|
62
|
+
| **All** | Optional | `pip install 'mcp-database[all]'` |
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
### Environment Variables
|
|
67
|
+
|
|
68
|
+
| Variable | Default | Description |
|
|
69
|
+
|----------|---------|-------------|
|
|
70
|
+
| `MCP_DATABASE_URL` | `sqlite:///:memory:` | Database connection URL |
|
|
71
|
+
| `MCP_DATABASE_TYPE` | `sqlite` | Database type: `sqlite`, `postgresql`, `mysql` |
|
|
72
|
+
| `MCP_DATABASE_READ_ONLY` | `true` | Enable read-only mode |
|
|
73
|
+
| `MCP_MAX_ROWS` | `100` | Maximum rows returned per query |
|
|
74
|
+
|
|
75
|
+
### Connection URLs
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# SQLite
|
|
79
|
+
MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
|
|
80
|
+
MCP_DATABASE_URL=sqlite:///:memory:
|
|
81
|
+
|
|
82
|
+
# PostgreSQL
|
|
83
|
+
MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
84
|
+
MCP_DATABASE_TYPE=postgresql
|
|
85
|
+
|
|
86
|
+
# MySQL
|
|
87
|
+
MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
|
|
88
|
+
MCP_DATABASE_TYPE=mysql
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Available Tools
|
|
92
|
+
|
|
93
|
+
Once connected, Claude can use these tools:
|
|
94
|
+
|
|
95
|
+
| Tool | Description |
|
|
96
|
+
|------|-------------|
|
|
97
|
+
| `list_databases` | List all configured database connections |
|
|
98
|
+
| `list_tables` | List all tables in a database |
|
|
99
|
+
| `get_table_info` | Get detailed table info (columns, types, row count) |
|
|
100
|
+
| `get_schema` | Get full database schema (CREATE TABLE statements) |
|
|
101
|
+
| `query` | Execute a read-only SQL query (SELECT, SHOW, DESCRIBE) |
|
|
102
|
+
| `execute` | Execute a write statement (INSERT, UPDATE, DELETE) — opt-in only |
|
|
103
|
+
| `sample_rows` | Get sample rows from a table |
|
|
104
|
+
| `search_tables` | Search for tables or columns by keyword |
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
Ask Claude things like:
|
|
109
|
+
|
|
110
|
+
- "What tables are in my database?"
|
|
111
|
+
- "Show me the schema for the users table"
|
|
112
|
+
- "Query the top 10 orders by amount"
|
|
113
|
+
- "Find all columns related to 'email'"
|
|
114
|
+
- "Sample some rows from the products table"
|
|
115
|
+
|
|
116
|
+
## Security
|
|
117
|
+
|
|
118
|
+
- **Read-only by default** — queries are safe, no data modification
|
|
119
|
+
- **Write opt-in** — set `allow_writes=True` and `MCP_DATABASE_READ_ONLY=false` to enable
|
|
120
|
+
- **Read-only detection** — write tool rejects SELECT statements (use `query` instead)
|
|
121
|
+
- **Row limits** — configurable max rows to prevent accidental large result sets
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Clone and install
|
|
127
|
+
git clone https://github.com/Jansen003/mcp-database.git
|
|
128
|
+
cd mcp-database
|
|
129
|
+
pip install -e ".[dev]"
|
|
130
|
+
|
|
131
|
+
# Run tests
|
|
132
|
+
pytest
|
|
133
|
+
|
|
134
|
+
# Run with Inspector UI
|
|
135
|
+
mcp dev src/mcp_database/server.py
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# mcp-database
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
|
|
7
|
+
**多数据库 MCP 服务器 —— 让 Claude 直接查询、探索和管理你的 SQLite、PostgreSQL、MySQL 数据库。**
|
|
8
|
+
|
|
9
|
+
[English](README.md) | 中文
|
|
10
|
+
|
|
11
|
+
## 为什么需要 mcp-database?
|
|
12
|
+
|
|
13
|
+
| 你的问题 | mcp-database 的解决方案 |
|
|
14
|
+
|---------|----------------------|
|
|
15
|
+
| 想在 Claude Code 里直接查数据库 | 一个 MCP 服务器,支持多种数据库 |
|
|
16
|
+
| 现有的数据库 MCP 要么是 JS 要么是 Go | 纯 Python,基于官方 `mcp` SDK |
|
|
17
|
+
| 担心 Claude 误操作数据 | 默认只读模式,写入需要显式开启 |
|
|
18
|
+
| 不熟悉数据库结构 | 内置表结构探索、列信息、关键词搜索 |
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 安装
|
|
24
|
+
pip install mcp-database
|
|
25
|
+
|
|
26
|
+
# 连接 SQLite 数据库
|
|
27
|
+
MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 接入 Claude Code
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 一行命令接入
|
|
34
|
+
claude mcp add mcp-database -- mcp-database
|
|
35
|
+
|
|
36
|
+
# 指定数据库文件
|
|
37
|
+
claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 接入 Claude Desktop
|
|
41
|
+
|
|
42
|
+
在 `claude_desktop_config.json` 中添加:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"database": {
|
|
48
|
+
"command": "mcp-database",
|
|
49
|
+
"env": {
|
|
50
|
+
"MCP_DATABASE_URL": "sqlite:///path/to/your.db"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 支持的数据库
|
|
58
|
+
|
|
59
|
+
| 数据库 | 状态 | 安装方式 |
|
|
60
|
+
|--------|------|----------|
|
|
61
|
+
| **SQLite** | 内置 | `pip install mcp-database` |
|
|
62
|
+
| **PostgreSQL** | 可选 | `pip install 'mcp-database[postgres]'` |
|
|
63
|
+
| **MySQL** | 可选 | `pip install 'mcp-database[mysql]'` |
|
|
64
|
+
| **全部** | 可选 | `pip install 'mcp-database[all]'` |
|
|
65
|
+
|
|
66
|
+
## 配置方式
|
|
67
|
+
|
|
68
|
+
### 环境变量
|
|
69
|
+
|
|
70
|
+
| 变量名 | 默认值 | 说明 |
|
|
71
|
+
|--------|--------|------|
|
|
72
|
+
| `MCP_DATABASE_URL` | `sqlite:///:memory:` | 数据库连接地址 |
|
|
73
|
+
| `MCP_DATABASE_TYPE` | `sqlite` | 数据库类型:`sqlite`、`postgresql`、`mysql` |
|
|
74
|
+
| `MCP_DATABASE_READ_ONLY` | `true` | 是否启用只读模式 |
|
|
75
|
+
| `MCP_MAX_ROWS` | `100` | 单次查询最大返回行数 |
|
|
76
|
+
|
|
77
|
+
### 连接地址格式
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# SQLite
|
|
81
|
+
MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
|
|
82
|
+
MCP_DATABASE_URL=sqlite:///:memory:
|
|
83
|
+
|
|
84
|
+
# PostgreSQL
|
|
85
|
+
MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
86
|
+
MCP_DATABASE_TYPE=postgresql
|
|
87
|
+
|
|
88
|
+
# MySQL
|
|
89
|
+
MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
|
|
90
|
+
MCP_DATABASE_TYPE=mysql
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 可用工具
|
|
94
|
+
|
|
95
|
+
接入后,Claude 可以使用以下工具:
|
|
96
|
+
|
|
97
|
+
| 工具名 | 说明 |
|
|
98
|
+
|--------|------|
|
|
99
|
+
| `list_databases` | 列出所有已配置的数据库连接 |
|
|
100
|
+
| `list_tables` | 列出数据库中的所有表 |
|
|
101
|
+
| `get_table_info` | 获取表的详细信息(列名、类型、行数) |
|
|
102
|
+
| `get_schema` | 获取完整的数据库建表语句 |
|
|
103
|
+
| `query` | 执行只读 SQL 查询(SELECT、SHOW、DESCRIBE) |
|
|
104
|
+
| `execute` | 执行写入语句(INSERT、UPDATE、DELETE)—— 需手动开启 |
|
|
105
|
+
| `sample_rows` | 获取表中的示例数据 |
|
|
106
|
+
| `search_tables` | 按关键词搜索表名和列名 |
|
|
107
|
+
|
|
108
|
+
## 使用示例
|
|
109
|
+
|
|
110
|
+
你可以这样问 Claude:
|
|
111
|
+
|
|
112
|
+
- "我的数据库里有哪些表?"
|
|
113
|
+
- "看一下 users 表的结构"
|
|
114
|
+
- "查询金额最大的 10 笔订单"
|
|
115
|
+
- "找一下所有跟 email 相关的字段"
|
|
116
|
+
- "看看 products 表里长什么样"
|
|
117
|
+
|
|
118
|
+
## 安全设计
|
|
119
|
+
|
|
120
|
+
- **默认只读** —— 查询操作不会修改任何数据
|
|
121
|
+
- **写入需授权** —— 必须设置 `allow_writes=True` 和 `MCP_DATABASE_READ_ONLY=false`
|
|
122
|
+
- **语句检测** —— 写入工具会拒绝 SELECT 语句(应该用 `query`)
|
|
123
|
+
- **行数限制** —— 可配置最大返回行数,防止意外拉取全表
|
|
124
|
+
|
|
125
|
+
## 开发
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# 克隆并安装
|
|
129
|
+
git clone https://github.com/Jansen003/mcp-database.git
|
|
130
|
+
cd mcp-database
|
|
131
|
+
pip install -e ".[dev]"
|
|
132
|
+
|
|
133
|
+
# 运行测试
|
|
134
|
+
pytest
|
|
135
|
+
|
|
136
|
+
# 使用 Inspector 调试
|
|
137
|
+
mcp dev src/mcp_database/server.py
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 项目结构
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
src/mcp_database/
|
|
144
|
+
__init__.py # 版本信息
|
|
145
|
+
server.py # MCP 服务器主入口(8 个工具)
|
|
146
|
+
config.py # 配置加载(环境变量、URL 解析)
|
|
147
|
+
adapters/
|
|
148
|
+
base.py # 适配器基类(DatabaseAdapter)
|
|
149
|
+
sqlite.py # SQLite 适配器
|
|
150
|
+
postgres.py # PostgreSQL 适配器
|
|
151
|
+
mysql.py # MySQL 适配器
|
|
152
|
+
tests/
|
|
153
|
+
test_sqlite_adapter.py # SQLite 适配器测试(17 个用例)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 许可证
|
|
157
|
+
|
|
158
|
+
MIT — 详见 [LICENSE](LICENSE)。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mcp-database"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for multi-database access — SQLite, PostgreSQL, MySQL. Query, inspect schema, and manage databases through Claude."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Mergewall Team"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mcp", "database", "sqlite", "postgresql", "mysql", "claude", "model-context-protocol"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Database",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"mcp>=1.0.0",
|
|
29
|
+
"pyyaml>=6.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
postgres = ["psycopg2-binary>=2.9.0"]
|
|
34
|
+
mysql = ["pymysql>=1.1.0"]
|
|
35
|
+
all = ["mcp-database[postgres,mysql]"]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=8.0",
|
|
38
|
+
"pytest-asyncio>=0.23",
|
|
39
|
+
"ruff>=0.4.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
mcp-database = "mcp_database.server:main"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/mcp_database"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
asyncio_mode = "auto"
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
target-version = "py310"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Base adapter interface for database connections."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class QueryResult:
|
|
10
|
+
"""Result of a SQL query."""
|
|
11
|
+
|
|
12
|
+
columns: list[str]
|
|
13
|
+
rows: list[list[Any]]
|
|
14
|
+
row_count: int
|
|
15
|
+
truncated: bool = False
|
|
16
|
+
|
|
17
|
+
def to_table(self, max_rows: int = 100) -> str:
|
|
18
|
+
"""Format result as an aligned text table."""
|
|
19
|
+
if not self.columns:
|
|
20
|
+
return "No results."
|
|
21
|
+
|
|
22
|
+
rows = self.rows[:max_rows]
|
|
23
|
+
# Calculate column widths
|
|
24
|
+
widths = [len(c) for c in self.columns]
|
|
25
|
+
for row in rows:
|
|
26
|
+
for i, val in enumerate(row):
|
|
27
|
+
if i < len(widths):
|
|
28
|
+
widths[i] = max(widths[i], len(str(val)))
|
|
29
|
+
|
|
30
|
+
# Header
|
|
31
|
+
header = " | ".join(c.ljust(widths[i]) for i, c in enumerate(self.columns))
|
|
32
|
+
separator = "-+-".join("-" * w for w in widths)
|
|
33
|
+
lines = [header, separator]
|
|
34
|
+
|
|
35
|
+
# Rows
|
|
36
|
+
for row in rows:
|
|
37
|
+
line = " | ".join(str(row[i]).ljust(widths[i]) if i < len(row) else "" for i in range(len(self.columns)))
|
|
38
|
+
lines.append(line)
|
|
39
|
+
|
|
40
|
+
result = "\n".join(lines)
|
|
41
|
+
if self.truncated:
|
|
42
|
+
result += f"\n... ({self.row_count} total rows, showing first {max_rows})"
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class TableInfo:
|
|
48
|
+
"""Information about a database table."""
|
|
49
|
+
|
|
50
|
+
name: str
|
|
51
|
+
columns: list[dict[str, Any]]
|
|
52
|
+
row_count: int | None = None
|
|
53
|
+
create_sql: str | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DatabaseAdapter(ABC):
|
|
57
|
+
"""Abstract base class for database adapters."""
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def connect(self) -> None:
|
|
61
|
+
"""Establish connection to the database."""
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def disconnect(self) -> None:
|
|
65
|
+
"""Close the database connection."""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def test_connection(self) -> bool:
|
|
69
|
+
"""Test if the connection is alive."""
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def list_databases(self) -> list[str]:
|
|
73
|
+
"""List available databases (if applicable)."""
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def list_tables(self, database: str | None = None) -> list[str]:
|
|
77
|
+
"""List all tables in the database."""
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def get_table_info(self, table: str, database: str | None = None) -> TableInfo:
|
|
81
|
+
"""Get detailed information about a table."""
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def get_schema(self, database: str | None = None) -> str:
|
|
85
|
+
"""Get the full database schema as formatted text."""
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def execute_query(self, sql: str, database: str | None = None, max_rows: int = 100) -> QueryResult:
|
|
89
|
+
"""Execute a read-only SQL query."""
|
|
90
|
+
|
|
91
|
+
def execute_write(self, sql: str, database: str | None = None) -> int:
|
|
92
|
+
"""Execute a write SQL query (INSERT/UPDATE/DELETE). Returns affected rows.
|
|
93
|
+
|
|
94
|
+
Raises NotImplementedError if the adapter is read-only.
|
|
95
|
+
"""
|
|
96
|
+
raise NotImplementedError("This adapter is read-only. Use execute_query() for SELECT statements.")
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def db_type(self) -> str:
|
|
101
|
+
"""Return the database type name (e.g., 'sqlite', 'postgresql', 'mysql')."""
|
|
102
|
+
|
|
103
|
+
def _is_read_only_query(self, sql: str) -> bool:
|
|
104
|
+
"""Check if a query is read-only (SELECT, SHOW, DESCRIBE, EXPLAIN)."""
|
|
105
|
+
normalized = sql.strip().upper()
|
|
106
|
+
# Allow SELECT, SHOW, DESCRIBE, EXPLAIN, WITH (CTE leading to SELECT)
|
|
107
|
+
read_only_prefixes = ("SELECT", "SHOW", "DESCRIBE", "DESC", "EXPLAIN", "WITH", "PRAGMA")
|
|
108
|
+
return any(normalized.startswith(prefix) for prefix in read_only_prefixes)
|