sql-agent-bridge
v0.2.2
Published
A lightweight, portable, security-hardened MCP server for multi-dialect database access
Maintainers
Readme
sql-agent-bridge
一个轻量级、可移植、安全加固的 MCP(Model Context Protocol)服务器,为 AI 编程助手提供多方言数据库、默认只读且可选受控写模式的访问能力(MySQL / SQLite / Oracle / SQLServer)。当前版本 v0.2.0。
特性
| 方面 | 能力 |
|------|------|
| 🔗 多方言 + 多数据源 | 单一配置文件管理多个数据库实例,支持 MySQL / SQLite / Oracle / SQLServer |
| 🔒 企业级安全 | 默认只读(可选受控写模式)、参数化查询防注入、危险操作拦截、行数/超时限制、审计日志 |
| 🖥️ 跨平台运行 | 纯 Node.js 实现,支持 Windows / macOS / Linux |
| 🤖 Agent 无关 | 严格遵循 MCP 标准(STDIO / SSE),兼容 Reasonix、Cursor、Claude Desktop 等任意客户端 |
| 🐳 容器化友好 | 提供 Dockerfile,支持一键部署 |
| 📦 可选驱动安装 | 仅需 Node.js(v18+) 运行时;方言驱动按需安装(better-sqlite3 / oracledb / mssql) |
| 📋 配置精简 | Template 继承减少重复,MCP_ALLOWED_CONNECTIONS 白名单精细化暴露 |
快速开始
安装
npm install -g sql-agent-bridge首次启动会自动在 ~/.sql-agent-bridge/ 创建配置文件模板。
配置
编辑 ~/.sql-agent-bridge/mcp-config.json,填入你的数据库信息:
{
"connections": [
{
"name": "my-db",
"description": "我的 MySQL 数据库",
"url": "mysql://readonly_user:${DB_PASSWORD}@127.0.0.1:3306/my_database"
},
{
"name": "local-sqlite",
"description": "本地 SQLite 文件",
"url": "sqlite:///path/to/data.db"
}
]
}密码用 ${DB_PASSWORD} 占位符,运行时从环境变量读取。URL 格式为 dialect://[user[:pass]@]host[:port][/db][?opts]。
启动
# 设置密码环境变量
# Linux/macOS: export DB_PASSWORD="your-password"
# Windows: $env:DB_PASSWORD="your-password"
sql-agent-bridge # STDIO 模式(默认)
# SSE 模式
MCP_TRANSPORT=sse MCP_PORT=3000 sql-agent-bridge配置详解
连接配置字段 (connections[])
| 字段 | 类型 | 必需 | 说明 |
|------|------|------|------|
| name | string | ✅ | 连接别名,AI 通过此名称引用数据库 |
| description | string | ✅ | 连接描述,对 AI 可见,建议写明用途和环境 |
| url | string | ✅ | 数据库连接 URL,格式:dialect://[user[:pass]@]host[:port][/db][?opts],支持 ${ENV_VAR} 注入 |
| mode | string | | "readonly"(默认)或 "readwrite"(需配合全局开关) |
| poolSize | number | | 连接池大小,默认 10(SQLite 忽略此字段) |
| charset | string | | 字符集,默认继承全局 defaults.charset(utf8mb4) |
| timezone | string | | 时区,默认继承全局 defaults.timezone("Z" 即 UTC) |
| extends | string | | 指向 templates 中的一个 key,该 template 的字段作为默认值(单层继承) |
| healthCheck | boolean | | 连接级覆盖 defaults.healthCheck |
URL 方言参考
| 方言 | URL 示例 |
|------|---------|
| MySQL | mysql://user:pass@localhost:3306/mydb?ssl=true |
| SQLite | sqlite:///absolute/path/to/db.sqlite 或 sqlite:relative.db |
| Oracle | oracle://scott:tiger@dbhost:1521/XEPDB1 |
| SQLServer | sqlserver://sa:pass@dbhost:1433/mydb?encrypt=true |
Template 继承 (templates)
{
"templates": {
"base": {
"mode": "readonly",
"poolSize": 5,
"charset": "utf8mb4"
}
},
"connections": [
{
"name": "prod-db",
"description": "生产数据库(只读)",
"url": "mysql://reader:${PROD_PASS}@prod-host:3306/mydb",
"extends": "base"
}
]
}全局默认配置 (defaults)
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| maxRows | number | 1000 | 绝对最大返回行数,查询 LIMIT 不会超过此值 |
| queryTimeout | number | 30000 | 查询超时时间(毫秒),超时返回 E003 |
| defaultLimit | number | 100 | 未指定 limit 参数时自动追加的 LIMIT 值 |
| charset | string | "utf8mb4" | 全局默认字符集 |
| timezone | string | "Z" | 全局默认时区 |
| healthCheck | boolean | false | 启动时是否对每个连接执行 SELECT 1 预检 |
| enableKeepAlive | boolean | true | 是否启用 TCP keep-alive |
| keepAliveInitialDelay | number | 10000 | keep-alive 初始延迟(毫秒) |
环境变量
| 变量名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| MCP_CONFIG_PATH | string | ~/.sql-agent-bridge/mcp-config.json | 配置文件路径(默认在用户全局目录) |
| MCP_TRANSPORT | stdio | sse | stdio | 传输模式 |
| MCP_PORT | number | 3000 | SSE 模式监听端口 |
| MCP_ALLOW_WRITE | true | false | false | 全局写操作开关 |
| MCP_ALLOWED_CONNECTIONS | string | — | 逗号分隔的连接白名单;设置后只暴露列表中指定的数据库 |
| MCP_LOG_LEVEL | string | info | 日志级别:debug / info / warn / error |
| MCP_LOG_FILE | string | — | 日志文件路径,不设置则输出到 stderr |
| MCP_FEEDBACK | true | false | false | 是否启用反馈收集工具 submit_feedback |
| MCP_FEEDBACK_DIR | string | ~/.sql-agent-bridge/ | 反馈文件存储目录 |
提供的工具
| 工具 | 描述 | 参数 |
|------|------|------|
| list_connections | 列出所有可用数据库别名 | 无 |
| query | 执行 SQL 查询(默认只读) | connectionName, sql, limit?, confirm? |
| get_schema | 获取指定表结构,包括表注释(tableComment)和每列的列注释(comment)、类型、主键、默认值、是否可空等 | connectionName, table |
| explain_query | 获取 SELECT 执行计划 | connectionName, sql |
get_schema 输出示例
{
"tableName": "users",
"tableComment": "用户表",
"columns": [
{
"name": "id",
"type": "int",
"key": "PRI",
"nullable": "NO",
"default": "",
"extra": "auto_increment",
"comment": "主键ID",
"position": 1,
"table_comment": "用户表"
},
{
"name": "email",
"type": "varchar(255)",
"key": "",
"nullable": "NO",
"default": "",
"extra": "",
"comment": "邮箱地址",
"position": 2,
"table_comment": "用户表"
}
],
"columnCount": 2
}MySQL/Oracle/SQLServer 返回真实注释;SQLite 返回空串但字段始终存在。
默认情况下所有工具均为只读。写操作需同时满足
MCP_ALLOW_WRITE=true、连接mode="readwrite"以及confirm="I understand the risk"。
AI 客户端接入
全局安装后(npm install -g sql-agent-bridge),所有客户端只需一行 "command": "sql-agent-bridge":
Reasonix
{
"mcpServers": {
"sql-agent-bridge": {
"type": "stdio",
"command": "sql-agent-bridge",
"enabled": true
}
}
}Cursor
{
"mcpServers": {
"sql-agent-bridge": {
"command": "sql-agent-bridge"
}
}
}Claude Desktop
{
"mcpServers": {
"sql-agent-bridge": {
"command": "sql-agent-bridge"
}
}
}OpenCode
{
"mcp": {
"sql-agent-bridge": {
"type": "local",
"command": ["npx", "-y", "sql-agent-bridge"]
}
}
}密码通过系统环境变量传入(如
export DB_PASSWORD=xxx),不出现任何配置文件中。首次运行自动创建~/.sql-agent-bridge/mcp-config.json。
Docker 部署
docker build -t sql-agent-bridge .
docker run -e DB_PASSWORD="xxx" sql-agent-bridgeSSE 模式:docker run -e DB_PASSWORD=xxx -e MCP_TRANSPORT=sse -p 3000:3000 sql-agent-bridge
v0.1 → v0.2 迁移指南
v0.2.0 将连接配置从扁平字段(host/port/user/password/database)改为单一 url 字段。提供自动迁移脚本:
npx tsx scripts/migrate-config-v0.2.ts ~/.sql-agent-bridge/mcp-config.json脚本会输出新版配置到 stdout,不覆盖原文件。检查无误后手动替换。
手动迁移对照
{
"connections": [
{
"name": "my-db",
- "host": "127.0.0.1",
- "port": 3306,
- "user": "root",
- "password": "${DB_PASS}",
- "database": "mydb",
+ "url": "mysql://root:${DB_PASS}@127.0.0.1:3306/mydb"
}
]
}安全设计
纵深防御体系
配置层 → 凭证不硬编码,通过 ${ENV_VAR} 注入,缺失即退出
连接层 → 建议使用只读数据库账户 + 强制 SSL/TLS
工具层 → 默认只暴露只读工具,全局 + 连接级双重写开关
SQL 层 → node-sql-parser 解析校验 + 参数化查询防注入
审计层 → 结构化日志记录所有操作,SQL 脱敏后输出写操作授权(可选)
若需允许写操作,须同时满足三个条件:
- 启动时设置
MCP_ALLOW_WRITE=true - 配置文件中对应连接的
mode设为"readwrite" - 调用
query时传入confirm: "I understand the risk"
SQL 注入防护
- 所有动态值通过参数化查询(Prepared Statement)的
?占位符传递,绝不拼接用户输入 - 启动时强制
SET NAMES utf8mb4,防止编码攻击 - SQL 解析失败 → 直接拒绝(E007),不以原始 SQL 透传
日志脱敏
查询日志中 SQL 的字面量(字符串、数字)被替换为 ?:
原始 SQL: SELECT * FROM users WHERE id = 123 AND name = 'Alice'
脱敏后: SELECT * FROM users WHERE id = ? AND name = ?密码等敏感字段绝不出现在任何日志中。
错误码参考
| 错误码 | 含义 |
|--------|------|
| E001 | 连接别名不存在 |
| E002 | SQL 语句不合法或非只读操作(INSERT/UPDATE/DELETE/DROP 等) |
| E003 | 查询超时(超过 queryTimeout 毫秒) |
| E004 | 结果集超过最大行数限制(保留) |
| E005 | 数据库连接失败 |
| E006 | 参数化查询执行失败 |
| E007 | SQL 解析失败(不支持的语法或方言) |
| E008 | 表不存在或无列(get_schema 专用) |
v0.2.2 已知修复
- SHOW ... LIKE/WHERE 误判为写操作:根因是 query.ts 不区分 E007/E002 错误码导致解析失败被错误路由到写授权检测,加上 node-sql-parser 不支持 SHOW 后缀子句。修复方案:query.ts 按 error.code 分流(E007 直接返回、E002 多语句直接拒绝),sql-parser.ts 加 SIMPLE_SHOW_RE 正则白名单兜底。
已知限制
- node-sql-parser 仍不支持 SHOW 后跟复杂 WHERE/LIKE 子句——超出 SIMPLE_SHOW_RE 白名单范围的会返回 E007。建议使用
list_tables工具替代SHOW TABLES LIKE。 SHOW TABLES LIKE 'x';(带尾部分号的 LIKE 查询)会被 regex 拒绝 → 回落 parser → E007。LIKE 后缀不支持尾部分号。- list_tables pattern 不做 LIKE 转义(
_%保持原义传递)。
开发指南
项目结构
src/
├── index.ts # 入口:启动流程、STDIO/SSE 传输、优雅关闭
├── server.ts # MCP Server 初始化 + 工具注册
├── logger.ts # pino 日志实例(全部输出到 stderr)
├── errors.ts # BridgeError 统一错误类型
├── config/
│ ├── types.ts # 类型定义(AppConfig / EnvConfig / ErrorCode)
│ ├── loader.ts # 配置加载 + ${ENV_VAR} 解析 + template 继承 + 白名单过滤 + fail-fast
│ └── url-parser.ts # URL 解析器(四方言:mysql/oracle/sqlserver/sqlite)
├── db/
│ ├── dialect.ts # Dialect / ConnectionConfig / ParsedUrl 类型
│ ├── pool-manager.ts # 多连接池管理器(driver-agnostic)
│ ├── client.ts # [遗留] mysql2 查询封装
│ └── dialects/
│ ├── driver.ts # DialectDriver 接口定义
│ ├── mysql.ts # MySQL 驱动实现
│ ├── sqlite.ts # SQLite 驱动实现(better-sqlite3)
│ ├── oracle.ts # Oracle 驱动实现(oracledb thin)
│ └── sqlserver.ts # SQLServer 驱动实现(mssql)
├── security/
│ ├── sql-parser.ts # SQL 只读校验(多方言)
│ ├── sanitizer.ts # SQL 日志脱敏
│ └── guard.ts # 写权限双层校验
├── tools/
│ ├── list-connections.ts
│ ├── query.ts
│ ├── get-schema.ts
│ ├── explain-query.ts
│ ├── list-tables.ts
│ └── submit-feedback.ts
├── transport/
│ └── sse.ts # SSE HTTP 服务器(多客户端)
└── types/
└── optional-deps.d.ts # 可选依赖类型声明命令
| 命令 | 说明 |
|------|------|
| npm run build | 编译 TypeScript → dist/ |
| npm start | 运行编译产物(STDIO 模式) |
| npm run dev | 开发模式(tsx 直接运行) |
| npm run lint | 类型检查(不生成文件) |
| npm test | 运行单元测试(vitest) |
开发约束(硬性要求)
- 日志全部走 pino + stderr:stdout 仅供 MCP 协议通信,严禁
console.log - Fail-fast:启动时检测所有
${ENV_VAR},未定义 →process.exit(1) - SQL 安全:先解析校验只读 → 再执行,解析失败直接拒绝(E007)
- 参数化查询:所有动态值通过
?占位符传递 - 工具描述:每个工具必须包含详尽的 AI 可见
description字段
发布前检查清单
- [x]
npm run build零错误 - [x]
npm run lint零错误 - [x]
npm test全部通过(79 passed / 7 skipped) - [x] 通过 MCP 手工测试:SELECT / JOIN / GROUP BY / INSERT 拦截
- [x]
.gitignore排除敏感文件
路线图
| 阶段 | 内容 | 状态 | |------|------|------| | 第1-5阶段 | 项目骨架 / 安全加固 / 功能补全 / SSE / Docker | ✅ 完成 | | v0.1.0 | 写模式落地、多语句护栏、统一错误对象、测试补全 | ✅ 完成 | | v0.2.0 | 多方言支持(MySQL/SQLite/Oracle/SQLServer)、URL 配置、template 继承、连接白名单 | ✅ 完成 | | v0.2.2 | SHOW…LIKE/WHERE 误判修复、list_tables 跨方言工具、MCP_FEEDBACK 可选反馈 | ✅ 完成 |
技术栈
| 组件 | 选型 | 说明 |
|------|------|------|
| 运行时 | Node.js 18+ | 轻量、跨平台 |
| MCP SDK | @modelcontextprotocol/sdk v1.29 | 官方标准实现 |
| MySQL 驱动 | mysql2 v3.14 | 支持预处理语句、连接池、SSL |
| SQLite 驱动 | better-sqlite3 v11 | 同步 API,单文件零配置(可选安装) |
| Oracle 驱动 | oracledb v6 | Thin 模式,无需 Oracle Instant Client(可选安装) |
| SQLServer 驱动 | mssql v11 | 支持 Windows Auth + SQL Auth(可选安装) |
| SQL 解析 | node-sql-parser v5.3 | AST 解析 + 类型识别 |
| 日志 | pino v9.5 | 结构化 JSON 日志 |
| 参数校验 | zod v3.24 | 工具输入 Schema 校验 |
| 单元测试 | vitest v2.1 | 快速 ESM 原生测试框架 |
| 集成测试 | vitest + vi.mock | 纯 mock 方案;真环境集成测试见 test-data/setup-pg.cjs(本地 PG 初始化) |
License
MIT
