2026-09-28 17:41:59 +08:00
|
|
|
|
# mini-db-mcp
|
|
|
|
|
|
|
|
|
|
|
|
一个面向关系型数据库的轻量级 MCP(Model Context Protocol)服务。项目基于 Python MCP SDK 和 SQLAlchemy,当前支持 MySQL,并通过**渐进式披露**减少一次性注入模型上下文的工具定义数量。
|
|
|
|
|
|
|
|
|
|
|
|
## 特性
|
|
|
|
|
|
|
|
|
|
|
|
- 支持 `stdio` 与无状态 Streamable HTTP 两种传输方式
|
|
|
|
|
|
- 采用“领域工具 -> 子工具”的两阶段发现与执行流程
|
|
|
|
|
|
- 提供数据库对象浏览、表结构查看和字段采样能力
|
|
|
|
|
|
- 查询默认只读,并提供行数、响应大小、单元格长度和超时限制
|
|
|
|
|
|
- 同时返回 `structuredContent` 与兼容旧客户端的 JSON 文本内容
|
|
|
|
|
|
- 支持工具列表和元数据结果分页
|
|
|
|
|
|
|
|
|
|
|
|
## 工作原理
|
|
|
|
|
|
|
|
|
|
|
|
项目不会在初始化时把所有子工具定义一次性暴露给模型,而是按需加载:
|
|
|
|
|
|
|
|
|
|
|
|
1. MCP 客户端通过 `tools/list` 获取顶层领域工具,例如 `metadata`。
|
|
|
|
|
|
2. 第一次调用领域工具时传入空参数,服务返回该领域的子工具清单及其输入、输出 Schema。
|
|
|
|
|
|
3. 模型选择子工具后,再通过 `child_tool` 和 `child_arguments` 发起第二次调用。
|
|
|
|
|
|
4. 服务校验参数并将请求路由到对应处理器,最终返回结构化结果。
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
|
participant Client as MCP Client
|
|
|
|
|
|
participant Server as mini-db-mcp
|
|
|
|
|
|
participant DB as MySQL
|
|
|
|
|
|
Client->>Server: tools/list
|
|
|
|
|
|
Server-->>Client: metadata
|
|
|
|
|
|
Client->>Server: tools/call metadata, arguments={}
|
|
|
|
|
|
Server-->>Client: child tool manifest
|
|
|
|
|
|
Client->>Server: tools/call metadata, child_tool + child_arguments
|
|
|
|
|
|
Server->>DB: execute metadata query
|
|
|
|
|
|
DB-->>Server: result
|
|
|
|
|
|
Server-->>Client: structuredContent + JSON text
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
当前提供以下领域和子工具:
|
|
|
|
|
|
|
|
|
|
|
|
| 领域 | 子工具 | 用途 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| `metadata` | `list_tables` | 分页列出当前数据库中的表和视图 |
|
|
|
|
|
|
| `metadata` | `describe_table` | 分页查看指定表或视图的字段结构 |
|
|
|
|
|
|
| `metadata` | `sample_values` | 获取指定字段的去重样例值 |
|
|
|
|
|
|
| `query` | `execute_query` | 执行单条 SQL;根据 `QUERY_ALLOW_WRITE` 校验读写权限,并受查询超时和结果大小限制 |
|
|
|
|
|
|
|
|
|
|
|
|
主要入口位于 [`src/mini_db_mcp/server.py`](src/mini_db_mcp/server.py),渐进式工具发现与调用逻辑位于 [`src/mini_db_mcp/tools.py`](src/mini_db_mcp/tools.py)。
|
|
|
|
|
|
|
|
|
|
|
|
## 环境要求
|
|
|
|
|
|
|
|
|
|
|
|
- Python 3.12+
|
|
|
|
|
|
- 可访问的 MySQL 数据库
|
|
|
|
|
|
- 推荐使用 [uv](https://docs.astral.sh/uv/) 管理依赖
|
|
|
|
|
|
|
|
|
|
|
|
## 快速开始
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 安装依赖
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
uv sync
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 创建本地配置
|
|
|
|
|
|
|
|
|
|
|
|
启动前必须先将示例配置复制为 `.env`。
|
|
|
|
|
|
|
|
|
|
|
|
Windows PowerShell:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
Copy-Item .env.example .env
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
macOS / Linux:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cp .env.example .env
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
随后编辑 `.env`,至少填写数据库连接信息:
|
|
|
|
|
|
|
|
|
|
|
|
```dotenv
|
|
|
|
|
|
DB_HOST=127.0.0.1
|
|
|
|
|
|
DB_PORT=3306
|
|
|
|
|
|
DB_USER=root
|
|
|
|
|
|
DB_PASSWORD=your_password
|
|
|
|
|
|
DB_SCHEMA=your_database
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不要提交 `.env`。仓库仅跟踪不含真实凭据的 `.env.example`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 启动服务
|
|
|
|
|
|
|
|
|
|
|
|
默认使用 `stdio`,适合由本地 MCP 客户端拉起:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
uv run mcp
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
也可以通过模块入口启动:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
uv run python -m mini_db_mcp
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如需使用 Streamable HTTP,将 `.env` 中的传输方式改为:
|
|
|
|
|
|
|
|
|
|
|
|
```dotenv
|
|
|
|
|
|
MCP_TRANSPORT=http
|
|
|
|
|
|
MCP_HOST=127.0.0.1
|
|
|
|
|
|
MCP_PORT=8000
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
然后运行:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
uv run mcp
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
MCP 端点为 `http://127.0.0.1:8000/mcp`。
|
|
|
|
|
|
|
2026-10-09 10:36:17 +08:00
|
|
|
|
### Docker Compose
|
|
|
|
|
|
|
|
|
|
|
|
先按上文创建并填写 `.env`,然后启动服务:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
docker compose up --build -d
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Compose 会强制使用 HTTP 传输,并将 MCP 端点暴露为 `http://127.0.0.1:8000/mcp`。
|
|
|
|
|
|
|
|
|
|
|
|
停止服务:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
docker compose down
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-28 17:41:59 +08:00
|
|
|
|
## MCP 客户端配置
|
|
|
|
|
|
|
|
|
|
|
|
使用 `stdio` 的客户端可参考以下配置。请将项目路径替换为本机的绝对路径:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"mcpServers": {
|
|
|
|
|
|
"mini-db-mcp": {
|
|
|
|
|
|
"command": "uv",
|
|
|
|
|
|
"args": [
|
|
|
|
|
|
"--directory",
|
|
|
|
|
|
"C:\\path\\to\\mini-db-mcp",
|
|
|
|
|
|
"run",
|
|
|
|
|
|
"mcp"
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
服务从项目工作目录读取 `.env`,因此客户端配置必须将工作目录指向仓库根目录。
|
|
|
|
|
|
|
|
|
|
|
|
## CallTool 示例
|
|
|
|
|
|
|
|
|
|
|
|
第一次调用 `metadata` 时不传业务参数,用于获取当前可用的子工具定义:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "metadata",
|
|
|
|
|
|
"arguments": {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
返回结果的 `mode` 为 `manifest`,其中 `children` 包含子工具名称、说明及 JSON Schema。选择子工具后进行第二次调用:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "metadata",
|
|
|
|
|
|
"arguments": {
|
|
|
|
|
|
"child_tool": "list_tables",
|
|
|
|
|
|
"child_arguments": {
|
|
|
|
|
|
"keyword": "user",
|
|
|
|
|
|
"limit": 20
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
成功结果的 `mode` 为 `result`;参数错误、未知子工具或执行失败时,`mode` 为 `error`,同时 MCP 响应的 `isError` 会被设置为 `true`。
|
|
|
|
|
|
|
|
|
|
|
|
`query` 领域同样采用两阶段调用。先发现 SQL 执行子工具:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "query",
|
|
|
|
|
|
"arguments": {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
然后选择 `execute_query` 并提供 SQL:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "query",
|
|
|
|
|
|
"arguments": {
|
|
|
|
|
|
"child_tool": "execute_query",
|
|
|
|
|
|
"child_arguments": {
|
|
|
|
|
|
"sql": "SELECT id, name FROM users"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 配置
|
|
|
|
|
|
|
2026-09-28 17:51:55 +08:00
|
|
|
|
配置从项目工作目录下的 `.env` 加载。下表列出 [config.py](src/mini_db_mcp/config.py) 中的代码默认值;[.env.example](.env.example) 包含全部受支持的配置项,但示例值(如数据库连接信息)可以与代码默认值不同。
|
2026-09-28 17:41:59 +08:00
|
|
|
|
|
|
|
|
|
|
| 配置项 | 默认值 | 说明 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| `DB_HOST` | `127.0.0.1` | 数据库主机 |
|
|
|
|
|
|
| `DB_PORT` | `3306` | 数据库端口 |
|
|
|
|
|
|
| `DB_USER` | `root` | 数据库用户 |
|
|
|
|
|
|
| `DB_PASSWORD` | 空 | 数据库密码 |
|
|
|
|
|
|
| `DB_SCHEMA` | 空 | 默认数据库 Schema |
|
2026-09-28 17:51:55 +08:00
|
|
|
|
| `DB_CONNECT_TIMEOUT` | `30` | 建立数据库连接的超时时间(秒) |
|
|
|
|
|
|
| `DB_POOL_SIZE` | `5` | 数据库连接池常驻连接数 |
|
|
|
|
|
|
| `DB_MAX_OVERFLOW` | `10` | 连接池允许超出的连接数 |
|
|
|
|
|
|
| `DB_POOL_TIMEOUT` | `30` | 等待连接池可用连接的超时时间(秒) |
|
|
|
|
|
|
| `DB_POOL_RECYCLE` | `1800` | 连接回收时间(秒) |
|
|
|
|
|
|
| `DB_POOL_PRE_PING` | `true` | 从连接池取出连接前是否检查连接有效性 |
|
2026-09-28 17:41:59 +08:00
|
|
|
|
| `QUERY_MAX_ROWS` | `1000` | 单次查询最大返回行数 |
|
|
|
|
|
|
| `QUERY_MAX_BYTES` | `262144` | 单次响应最大字节数 |
|
|
|
|
|
|
| `QUERY_MAX_CELL_CHARS` | `4096` | 单个字段值最大字符数 |
|
|
|
|
|
|
| `QUERY_TIMEOUT_SECONDS` | `30` | 查询超时时间(秒) |
|
|
|
|
|
|
| `QUERY_ALLOW_WRITE` | `false` | 是否允许写操作,建议保持关闭 |
|
|
|
|
|
|
| `MCP_TRANSPORT` | `stdio` | 传输方式:`stdio` 或 `http` |
|
|
|
|
|
|
| `MCP_HOST` | `127.0.0.1` | HTTP 监听地址 |
|
|
|
|
|
|
| `MCP_PORT` | `8000` | HTTP 监听端口 |
|
|
|
|
|
|
| `LOG_LEVEL` | `INFO` | 日志级别 |
|
|
|
|
|
|
|
|
|
|
|
|
## 开发与测试
|
|
|
|
|
|
|
|
|
|
|
|
安装开发依赖并运行测试:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
uv sync --dev
|
|
|
|
|
|
uv run pytest
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
使用 MCP Inspector 调试 `stdio` 服务:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
npx @modelcontextprotocol/inspector uv run mcp
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
src/mini_db_mcp/
|
2026-09-28 17:51:55 +08:00
|
|
|
|
|-- __init__.py # 包版本信息
|
|
|
|
|
|
|-- __main__.py # python -m mini_db_mcp 入口
|
|
|
|
|
|
|-- main.py # 命令行入口、日志与传输方式选择
|
|
|
|
|
|
|-- config.py # 数据库、查询和服务配置
|
|
|
|
|
|
|-- db.py # 异步数据库访问、元数据读取和查询执行
|
|
|
|
|
|
|-- security.py # SQL 解析、安全校验、写入策略和行数限制
|
|
|
|
|
|
|-- prompts.py # MCP Prompt 定义与处理
|
|
|
|
|
|
|-- server.py # MCP Server 组装与 stdio / HTTP 启动
|
|
|
|
|
|
|-- tools.py # 渐进式工具发现、参数校验、路由和结果封装
|
|
|
|
|
|
|-- domain/
|
|
|
|
|
|
| |-- domain_catalog.py # 领域和子工具的输入/输出 Schema
|
|
|
|
|
|
| |-- domain_manifest.py # 将领域定义转换为 MCP 顶层工具
|
|
|
|
|
|
| |-- domain_model.py # 领域、请求、错误模型及 JSON 序列化
|
|
|
|
|
|
| |-- domain_handler.py # Handler 协议与领域处理器注册表
|
|
|
|
|
|
| |-- domain_handler_factory.py # 创建 runtime、handler 并组装注册表
|
|
|
|
|
|
| |-- runtime/
|
|
|
|
|
|
| | |-- metadata_runtime.py # 元数据参数校验、store 调用和结果整理
|
|
|
|
|
|
| | `-- query_runtime.py # SQL 校验、安全处理、store 调用和结果补充
|
|
|
|
|
|
| `-- handler/
|
|
|
|
|
|
| |-- metadata_handler.py # 元数据子工具到 runtime 的映射
|
|
|
|
|
|
| `-- query_handler.py # execute_query 到 query runtime 的映射
|
|
|
|
|
|
`-- utils/
|
|
|
|
|
|
|-- json_util.py # JSON 值转换与校验
|
|
|
|
|
|
`-- pagination_util.py # 游标分页
|
2026-09-28 17:41:59 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-28 17:51:55 +08:00
|
|
|
|
|
2026-09-28 17:41:59 +08:00
|
|
|
|
## 安全建议
|
|
|
|
|
|
|
|
|
|
|
|
- 使用权限最小化的专用数据库账号。
|
|
|
|
|
|
- 保持 `QUERY_ALLOW_WRITE=false`,除非明确需要写入能力并已完成额外审计。
|
|
|
|
|
|
- 不要在 MCP 客户端配置或版本库中直接保存数据库密码。
|
|
|
|
|
|
- 对 HTTP 部署增加反向代理、身份认证、TLS 和网络访问控制;当前服务默认仅监听本机地址。
|