DB-MCP
by saopack
README.md
# DB-MCP 数据库SQL验证服务
## 系统概述
DB-MCP 是一个基于 Model Context Protocol (MCP) 的数据库 SQL 验证服务,为智能答疑平台提供高效、灵活的多数据库 SQL 执行与结果验证能力。
系统作为智能 Agent 与验证环境之间的桥梁,通过 Docker 容器化技术快速启动数据库实例,支持事务级回滚或容器销毁来保证数据零残留。
## 核心功能
- 支持多种数据库:Vastbase、金仓(Kingbase)、PostgreSQL、Oracle、MySQL、SQL Server
- 多版本支持:同一数据库的不同版本可同时配置和运行
- Docker 容器化:带容器命名和幂等启动(已运行则复用),端口自动映射
- 容器预热池:启动时预热配置容器,空闲容器可复用
- 容器健康监控:后台线程定期检查端口可达性,异常容器自动重建
- ephemeral 版本:不在 yaml 中的版本自动从 Nexus 下载二进制,Docker 挂载运行
- 事务级回滚:DML 操作通过显式事务实现毫秒级数据回退
- DDL 兜底策略:不支持 DDL 事务的数据库通过反向 DDL 清理或容器销毁
- 多 SQL 语句执行:支持分号分隔的多条 SQL,智能拆分(处理字符串、注释、各类引号、PL/SQL 块)
- EXPLAIN 模式:支持 REST API 和 MCP 接口查看执行计划
- 并发控制:BoundedSemaphore + PooledDB 连接池,单容器最多 10 个并发执行
- MCP JSON-RPC 协议:支持 `initialize`、`tools/list`、`tools/call`、SSE
- OAuth 2.0 授权:DCR 动态客户端注册 + Authorization Code 流程
- API Key 认证:客户端注册、管理、轮换
- 结构化审计日志:记录每次 SQL 执行的客户端、数据库、查询预览、状态
- RESTful API:基于 FastAPI 提供 HTTP 接口,附带 Swagger/ReDoc 文档
## 技术栈
- Python 3.8+
- FastAPI + uvicorn
- Docker SDK
- psycopg2(PostgreSQL / Vastbase / 金仓)
- pymysql(MySQL)
- oracledb(Oracle)
- pymssql(SQL Server)
- PyYAML
## 项目结构
```
db-mcp/
├── config/
│ ├── databases.yaml # 数据库配置
│ └── dockerfile_templates/ # Dockerfile 模板
│ └── vastbase/
├── src/
│ ├── __init__.py
│ ├── main.py # 服务入口
│ ├── api.py # FastAPI 应用创建 + 路由挂载
│ ├── config_manager.py # 配置加载(YAML → Pydantic 校验 + 线程锁)
│ ├── container_pool.py # 容器池(单例 + 连接池 + 信号量 + 健康监控)
│ ├── executor.py # 执行引擎(SQL 拆分、DDL/DML 路由)
│ ├── exceptions.py # 异常体系(精确 HTTP 状态码映射)
│ ├── dependencies.py # 惰性单例依赖注入
│ ├── client_registry.py # 客户端注册表(API Key + OAuth,线程安全)
│ ├── nexus_client.py # Nexus 仓库客户端
│ ├── package_manager.py # 包管理器(下载/解压/缓存)
│ ├── adapters/
│ │ ├── __init__.py # 导出 ADAPTER_REGISTRY + 适配器类
│ │ ├── base.py # 抽象基类 DBAdapter + 自动注册装饰器
│ │ ├── vastbase.py # Vastbase 适配器
│ │ ├── kingbase.py # 金仓适配器
│ │ ├── postgresql.py # PostgreSQL 适配器
│ │ ├── mysql.py # MySQL 适配器
│ │ ├── oracle.py # Oracle 适配器
│ │ └── mssql.py # SQL Server 适配器
│ ├── mcp/
│ │ ├── __init__.py
│ │ └── dify_mcp.py # MCP 协议处理器(工具定义 + 调用)
│ └── routes/
│ ├── __init__.py # 路由聚合导出
│ ├── execute_routes.py # SQL 执行 + 数据库信息 + 健康检查
│ ├── mcp_routes.py # MCP JSON-RPC + SSE 端点
│ ├── oauth_routes.py # OAuth DCR / 授权 / Token 交换
│ └── client_routes.py # 客户端管理 + Dify MCP 集成
├── tests/
│ ├── test_adapters.py
│ ├── test_api.py
│ ├── test_config_manager.py
│ ├── test_executor.py
│ └── test_container_pool.py
├── requirements.txt
└── README.md
```
## 安装与运行
### 安装依赖
```bash
pip install -r requirements.txt
```
### 启动服务
```bash
python -m src.main
```
服务将在 http://localhost:8000 启动。
### API 文档
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
## API 接口
### 数据库信息
```
GET /api/databases # 获取支持的数据库类型列表
GET /api/databases/{db_type}/versions # 获取指定数据库的版本列表
```
### SQL 执行
```
POST /api/execute_sql # 执行 SQL(需认证)
POST /api/dify/execute_sql # Dify 专用 SQL 执行接口(需认证)
```
请求示例:
```json
{
"db_type": "postgresql",
"version": "14",
"query": "SELECT 1"
}
```
可选字段:
- `db_compatibility`: 兼容性模式(oracle/pg/mysql/sqlserver 或 A/B/PG/MSSQL),自动转换为目标库格式
- `explain`: 设为 `true` 查看执行计划而非实际执行
- `params`: GUC 参数配置(仅 Vastbase 临时版本)
- `postgresql_conf`: postgresql.conf 内容(仅 Vastbase 临时版本)
- `pg_hba_conf`: pg_hba.conf 内容(仅 Vastbase 临时版本)
- `extra_files`: 额外挂载文件列表(仅 Vastbase 临时版本)
响应示例:
```json
{
"status": "success",
"data": {
"columns": ["?column?"],
"rows": [{"?column?": 1}],
"row_count": 1
}
}
```
多语句响应示例:
```json
{
"status": "success",
"data": [
{"statement": "SELECT 1", "status": "success", "data": {...}},
{"statement": "SELECT 2", "status": "success", "data": {...}}
]
}
```
### 健康检查
```
GET /api/health # 返回 healthy / degraded + config + docker 状态
```
### MCP JSON-RPC
```
POST / # JSON-RPC 入口 (initialize / tools/list / tools/call)
POST /mcp # JSON-RPC 入口(/mcp 路径)
GET /sse # SSE 端点
POST /messages # SSE 消息端点
GET /mcp # MCP 服务信息
GET /mcp/tools # MCP 工具列表
```
### OAuth
```
POST /register # DCR 动态客户端注册
GET /authorize # OAuth 授权端点
POST /token # Token 交换端点
```
### 客户端管理
```
POST /api/clients/register # 注册新客户端
GET /api/clients # 列出所有客户端
DELETE /api/clients/{client_id} # 注销客户端
POST /api/clients/{client_id}/rotate-key # 轮换 API Key
PATCH /api/clients/{client_id} # 更新客户端信息
POST /mcp/call # MCP 工具调用
POST /console/api/mcp/oauth/callback # Dify OAuth 回调
```
## 配置说明
配置文件 `config/databases.yaml` 按数据库类型和版本组织:
```yaml
databases:
<db_type>:
versions:
"<version>":
image: "<docker_image>"
port: <container_port>
adapter: "<AdapterClassName>"
username: "<user>"
password: "<password>"
database: "<database>"
privileged: true/false # 可选:特权模式
env: # 可选:自定义环境变量
KEY: "value"
```
当前已配置的数据库:
| 数据库 | 版本 |
|--------|------|
| Vastbase | 2.2.15, 3.0.8, 3.0.9 |
| 金仓 (Kingbase) | V8, V9 |
| PostgreSQL | 12, 13, 14 |
| Oracle | 11c, 12c, 18c, 19c, 21c |
| MySQL | 5.6, 5.7, 8.0 |
| SQL Server | 2017, 2019 |
## 数据回滚与容器生命周期
每次 SQL 执行的决策逻辑:
```
收到 SQL
├─ 是否为 DDL?
│ ├─ 否(DML/SELECT)→ execute_with_rollback() → 事务回滚,数据干净 → 容器可复用
│ └─ 是 → 适配器 supports_ddl_transaction?
│ ├─ True → execute_with_rollback() → 事务回滚,数据干净 → 容器可复用
│ └─ False → execute() 直接执行 → 数据无法回滚 → 容器必须销毁
└─ 执行结束后 → stop_container()
```
**核心原则:数据能否回滚决定了容器能不能复用。** 如果通过事务回滚了,容器数据是干净的,只需正常 stop,预热池可以继续复用。只有当 DDL 在不支持事务的数据库上直接执行后,数据无法回退,容器才需要销毁重建。
### DML 操作(INSERT / UPDATE / DELETE / SELECT)
1. 连接时设置 `autocommit = False`
2. 将 SQL 包裹在显式事务中执行
3. 无论执行成功与否,强制 `ROLLBACK`
4. 数据零残留,容器可正常 stop 并复用
5. 耗时通常 < 50ms
### DDL 操作(CREATE / ALTER / DROP / TRUNCATE / RENAME)
| 数据库 | DDL 事务支持 | DDL 策略 | 容器处理 |
|--------|------------|---------|---------|
| Vastbase | 支持 | 事务回滚 | 正常 stop,可复用 |
| PostgreSQL | 支持 | 事务回滚 | 正常 stop,可复用 |
| SQL Server | 支持 | 事务回滚 | 正常 stop,可复用 |
| 金仓 | 不支持 | 直接执行 | 需销毁重建 |
| Oracle | 不支持 | 直接执行 | 需销毁重建 |
| MySQL | 不支持 | 直接执行 | 需销毁重建 |
> DDL 在不支持事务的数据库上直接执行后,响应中会包含 `"note": "DDL executed, container will be destroyed"`,随后容器被销毁以确保后续请求拿到干净环境。
## 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `MCP_HOST` | `0.0.0.0` | 服务监听地址 |
| `MCP_PORT` | `8000` | 服务监听端口 |
| `MCP_LOG_DIR` | `./logs/` | 日志目录 |
| `MCP_BASE_URL` | `http://localhost:8000` | 服务对外 URL(OAuth 使用) |
| `MCP_MAX_CONCURRENCY` | `10` | 单容器最大并发执行数 |
| `MCP_LEASE_TIMEOUT` | `30` | 并发槽位等待超时(秒) |
| `MCP_DB_READY_TIMEOUT` | `300` | 数据库就绪等待超时(秒) |
| `MCP_CONTAINER_IDLE_TTL` | `86400` | 空闲容器保留时间(秒),默认 24 小时 |
| `MCP_HEALTH_CHECK_INTERVAL` | `30` | 健康检查间隔(秒) |
| `MCP_STATEMENT_TIMEOUT` | `30` | 单条 SQL 执行超时(秒) |
| `MCP_QUERY_TIMEOUT` | `3600` | HTTP 请求总超时(秒),含镜像构建 |
| `MCP_DOCKER_BUILD_TIMEOUT` | `1800` | Docker 镜像构建超时(秒) |
| `MCP_NEXUS_DOWNLOAD_TIMEOUT` | `600` | Nexus 下载超时(秒) |
| `MCP_PACKAGE_CACHE_DIR` | `./data/packages/` | 二进制包缓存目录 |
| `MCP_RESOURCE_CPU_<TYPE>` | 按数据库类型 | 覆盖 CPU 限制,如 `MCP_RESOURCE_CPU_VASTBASE=4` |
| `MCP_RESOURCE_MEM_<TYPE>` | 按数据库类型 | 覆盖内存限制,如 `MCP_RESOURCE_MEM_VASTBASE=4g` |
## 安全注意事项
1. 本工具仅供内网使用,请勿暴露到公网
2. 所有 SQL 执行需通过 API Key 认证(`Authorization: Bearer <key>`)
3. 容器使用 `--rm` 参数,停止后自动删除
4. DML 操作通过事务回滚保证数据零残留,容器可复用
5. 支持 DDL 事务的数据库(Vastbase、PostgreSQL、SQL Server)DDL 也会回滚,容器保持干净
6. 不支持 DDL 事务的数据库(金仓、Oracle、MySQL)DDL 执行后容器会被销毁重建
7. 建议配置适当的资源限制(CPU、内存)
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessSyncing