关于 db-gateway
db-gateway 是位于大模型与数据库之间的 MCP 服务安全层。它会屏蔽敏感字段、按角色控制表访问、限制请求频率并记录查询;适配器支持 Prisma、Postgres、MySQL、SQLite 和 MongoDB。
主要功能
- 01屏蔽或隐藏敏感字段
- 02按角色控制表和字段访问
- 03设置全局及单表速率限制
- 04记录并查询审计历史
- 05支持 Prisma、Postgres、MySQL、SQLite 和 MongoDB 适配器
- 06可选写入并执行权限检查
适用场景
- 01让大模型在 PII 屏蔽下查询数据
- 02按角色和数据表限制数据库访问
- 03查看查询历史和被拒操作
- 04为大模型启用受控数据库写入
MCP 相关
提供数据库读取工具,用于查询数据表、列出和描述表、查看网关状态及审计历史。启用写入后,还会提供插入、更新和删除工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 1 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | integer | 否 | 应用网关限制前要获取的最大行数(默认 50) |
| table | string | 是 | 要查询的表名 |
| filter | object | 否 | 键值筛选条件对,例如 { status: 'active' } |
详细介绍
@guardbee/mcp-db-gateway
🇬🇧 英语 | 🇹🇷 土耳其语 | 🇨🇳 中文 | 🇯🇵 日语 | 🇰🇷 韩语 | 🇧🇷 葡萄牙语 | 🇪🇸 西班牙语 | 🇩🇪 德语
一个符合 KVKK / GDPR 要求的 MCP(模型上下文协议)服务——在大模型与数据库之间增加一道安全防护层。
Claude(或其他任何大模型)不会直接查询数据库,而是通过此网关进行查询。敏感字段会自动屏蔽,表访问权限由角色控制,每次查询都会写入审计日志。
默认情况下,遥测处于关闭状态。设置
GUARDBEE_TELEMETRY=1后,它会发送服务、工具、成功状态、耗时和较短的参数值(例如文件路径);较长的字符串以及content、token或password等字段会替换为占位符。请参阅@guardbee/mcp-telemetry。
Claude ──► MCP Gateway ──► Database
│
├─ PII masking (tcKimlik → [REDACTED])
├─ Role check (ai-agent can only reach the products table)
├─ Rate limiting (max 100 queries/minute)
└─ Audit log (every query is recorded)
功能
- PII 屏蔽 — 自动屏蔽土耳其身份证号码、IBAN、电子邮件、电话号码、密码哈希等
- 基于角色的访问控制(RBAC) — 按角色设置表允许/拒绝列表和字段规则
- 速率限制 — 全局和按表设置请求时间窗口
- 审计日志 — 可写入控制台、文件或 HTTP webhook
- Prisma / Postgres / MySQL / SQLite / MongoDB 适配器 — 可直接接入现有的
PrismaClient、pgPool、mysql2Pool、better-sqlite3Database或 MongoDBDb - 写入支持(可选) — 支持 insert/update/delete,默认关闭;由按表和按角色设置的权限控制,具有受保护字段防护机制,以及“禁止操作整张表”的安全保护
- 195 项单元测试 — 涵盖屏蔽、处理流水线、RBAC、速率限制器、审计日志以及所有适配器(读取和写入)
近期变更(2026-09-15)
此软件包 AI Gateway 增长工作中的三个新增步骤:
1. query_audit_log 工具
此前,网关的审计日志是只写的——它会写入 console/file/http sink 之一,但 Claude 本身无法回看并询问“刚才发生了什么,哪些调用被拒绝了”。现在它可以了:
// Tool called from the Claude side:
query_audit_log({ table: "orders", deniedOnly: true, limit: 20 })
- 工作原理:
AuditLogger新增了一个始终启用的内存环形缓冲区,它完全独立于已配置的 sink(audit.bufferSize,默认值为 200)。即使 sink 是"http"(一种即发即弃的 webhook),也仍可通过此缓冲区查询历史记录。进程重启时缓冲区会重置,并且在audit.enabled: false时不会写入任何内容。 - 筛选条件:
table、tool(MCP 工具名称,例如"delete_row")、operation(read/insert/update/delete)、deniedOnly(仅限被拒绝的调用)、since(ISO 8601 时间戳)、limit(默认值为 50,最大值为 200)。结果按时间从新到旧排列。 - 顺带修复的一个实际 Bug:
registerDbTools(读取工具)和registerWriteTools(写入工具)之前各自创建了独立的GatewayPipeline/AuditLogger实例。这意味着insert_row/update_row/delete_row调用产生的审计事件永远不会出现在query_audit_log读取的缓冲区中——它们有两个互不知晓的独立缓冲区。现在server.ts会创建单个GatewayPipeline实例,并在两组工具之间共享。 - 测试:
src/__tests__/audit-logger.test.ts(10 个测试——缓冲区容量上限、每种筛选条件、环形缓冲区行为),以及在pipeline.test.ts中新增的 3 个测试(验证读取和写入会进入同一个缓冲区、deniedOnly与table筛选条件可以组合使用,以及audit.enabled: false时缓冲区保持为空)。
2. SQLite 适配器
现在,除了 Prisma/Postgres/MySQL,网关也支持 better-sqlite3:
import Database from "better-sqlite3";
import { createServer, createSqliteAdapter } from "@guardbee/mcp-db-gateway";
const db = new Database("./app.db");
const server = createServer({}, createSqliteAdapter(db));
- 安全模式与 pg/mysql 相同: 表名和列名无法参数化,因此会在嵌入 SQL 前进行验证——这里使用 SQLite 的
PRAGMA table_info(table),而不是information_schema。格式检查未通过(例如users"; DROP TABLE users;--)或不在实时架构中的名称,会在任何查询到达 SQLite 之前被拒绝。 better-sqlite3只是可选的peerDependency——它未被添加到包自身的devDependencies中,因为它需要原生绑定;测试(与 pg/mysql 测试类似)使用符合SqliteQueryable接口的简单模拟对象编写,无需导入实际的包。- 插入时使用
lastInsertRowid,而不是RETURNING: 由于 SQLite 对RETURNING的支持取决于版本,因此这里采用与 MySQL 适配器的insertId相同的方式——如果表有id列,且该列尚未包含在data中,就会将lastInsertRowid附加到插入的行。 - 额外修复:
AuditEvent/AuditQueryFilter类型此前完全没有从包根目录(index.ts)导出——现已添加,供希望为自己调用GatewayPipeline.queryAuditLog()编写类型的使用者使用。 - 测试:
src/__tests__/sqlite-adapter.test.ts(21 个测试)——与pg-adapter.test.ts使用相同的场景集:表列表、带筛选和不带筛选的查询、限制、拒绝未知表/列、拒绝注入尝试、插入(包括分配 id)、更新、删除。
3. MongoDB 适配器
现在,网关也支持使用 mongodb 驱动中的 Db 实例:
import { MongoClient } from "mongodb";
import { createServer, createMongoAdapter } from "@guardbee/mcp-db-gateway";
const client = new MongoClient(process.env.DATABASE_URL!);
await client.connect();
const server = createServer({}, createMongoAdapter(client.db("mydb")));
- **风险类别不同——不是 SQL 注入,而是操作符注入:**对于 pg/mysql/sqlite,风险在于表名/列名被嵌入 SQL 中。MongoDB 不会嵌入标识符,但如果
filter/data中的键是$where/$ne这样的 Mongo 操作符,或者值是{ $ne: null }这样的操作符对象,工具声明的“简单键值相等筛选条件”就可能在不知情的情况下变成任意查询。此适配器会提前拒绝以下所有情况:- 任意以
$开头的键(在filter和data中) - 任意包含
.的键——即带点号的路径(例如"passwordHash.reset")既有类似操作符的风险,也可以绕过findProtectedWriteFields的精确名称匹配(其 glob 支持范围之外) - 任意非标量(对象/数组)筛选值——包括尝试通过操作符对象绕过的情况,例如
{ status: { $ne: "active" } }
- 任意以
mongodb只是可选的peerDependency——与 SQLite 适配器相同,测试针对的是符合MongoDatabase接口的模拟对象编写的,无需导入真实软件包。- “表”即集合。
tables()映射到listCollections();插入时会将_id(驱动的insertedId)附加到返回的行;更新会使用{ $set: data }执行updateMany;删除会执行deleteMany。 - 测试:
src/__tests__/mongo-adapter.test.ts(19 项测试)——集合列表、带筛选/无筛选查询、限制、三种操作符注入场景(以$开头的键、带点号的键、操作符对象值)、插入/更新/删除。
快速开始
1. 通过 Smithery 一键连接
你可以通过 Smithery 一键将其添加到 Claude Desktop。
2. 通过全局安装连接到 Claude Desktop
npm install -g @guardbee/mcp-db-gateway
将以下内容添加到 ~/Library/Application Support/Claude/claude_desktop_config.json(macOS):
{
"mcpServers": {
"guardbee-db-gateway": {
"command": "guardbee-gateway",
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json
重启 Claude Desktop。演示数据库会自动加载,并启用 PII 遮蔽。
3. 在项目中使用(Prisma)
npm install @guardbee/mcp-db-gateway
import { PrismaClient } from "@prisma/client";
import { createServer, createPrismaAdapter } from "@guardbee/mcp-db-gateway";
const prisma = new PrismaClient();
const server = createServer(
{
audit: { enabled: true, sink: "file", filePath: "./audit.jsonl" },
},
createPrismaAdapter(prisma)
);
如果你不使用 Prisma,也可以直接传入原始的 pg 或 mysql2 连接:
// Postgres
import { Pool } from "pg";
import { createServer, createPgAdapter } from "@guardbee/mcp-db-gateway";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const server = createServer({}, createPgAdapter(pool /*, { schema: "public" } */));
// MySQL
import mysql from "mysql2/promise";
import { createServer, createMysqlAdapter } from "@guardbee/mcp-db-gateway";
const pool = mysql.createPool(process.env.DATABASE_URL!);
const server = createServer({}, createMysqlAdapter(pool /*, { database: "shop" } */));
// SQLite
import Database from "better-sqlite3";
import { createServer, createSqliteAdapter } from "@guardbee/mcp-db-gateway";
const db = new Database("./app.db");
const server = createServer({}, createSqliteAdapter(db));
// MongoDB
import { MongoClient } from "mongodb";
import { createServer, createMongoAdapter } from "@guardbee/mcp-db-gateway";
const client = new MongoClient(process.env.DATABASE_URL!);
await client.connect();
const server = createServer({}, createMongoAdapter(client.db("mydb")));
**安全说明:**与 Prisma 适配器不同,
pg/mysql2/better-sqlite3适配器会生成原始 SQL。由于表名和列名无法参数化,因此每次查询都会根据实时架构对其进行验证(information_schema,SQLite 则使用PRAGMA table_info)——不在架构中的表名/列名(例如,注入尝试)会在进入 SQL 之前被拒绝。MongoDB 适配器会防范另一种风险(“操作符注入”而非 SQL 注入)——请参阅近期更改。
MCP 工具
网关始终会向 Claude 暴露以下 5 个只读工具:
| 工具 | 描述 |
|---|---|
query_table | 查询表中的行(自动遮蔽 PII) |
list_tables | 列出可访问的表(应用角色限制) |
describe_table | 显示表的架构和遮蔽策略 |
query_audit_log | 查询网关自身的审计历史(可按表/工具/操作/deniedOnly/since 筛选)——存储在内存中,受 audit.bufferSize 限制,进程重启后会重置 |
gateway_status | 显示当前配置、角色和速率限制状态 |
当 writesEnabled: true 时(参见写入支持),还会添加另外 3 个写入工具:
| 工具 | 描述 |
|---|---|
insert_row | 插入新行 |
update_row | 更新匹配筛选条件的行(不接受空筛选条件) |
delete_row | 删除匹配筛选条件的行(不接受空筛选条件) |
配置
createServer({
// PII field rules (the first match applies)
fieldRules: [
{ field: "tcKimlik", strategy: "redact" }, // [REDACTED]
{ field: "iban", strategy: "mask" }, // TR32***890
{ field: "email", strategy: "mask" }, // ah***@example.com
{ field: "passwordHash", strategy: "redact" },
{ field: "*Token*", strategy: "redact" }, // glob pattern
],
// Table access rules
tableRules: [
{ table: "audit_logs", access: "deny" },
{ table: "users", access: "allow", maxRows: 25 },
// write: if unspecified, that table has no write permission at all (off by default)
{ table: "orders", access: "allow", write: { insert: true, update: true, delete: false } },
],
// Turn on the write tools (insert_row/update_row/delete_row) — false by default.
// While false, these tools are never visible to Claude.
writesEnabled: true,
// How many rows update_row/delete_row may affect via one filter.
// If exceeded, the operation is rejected before touching the database ("narrow your filter").
maxAffectedRowsPerWrite: 10,
// Default max rows
defaultMaxRows: 50,
// Rate limiting
rateLimit: {
enabled: true,
windowMs: 60_000, // 1 minute
maxRequests: 100, // global limit
maxRequestsPerTable: 20, // per table
},
// Audit log
audit: {
enabled: true,
sink: "file", // "console" | "file" | "http"
filePath: "./audit.jsonl",
// webhookUrl: "https://..." (for sink: "http")
bufferSize: 200, // size of the in-memory history the `query_audit_log` tool reads
},
// Roles
roles: [
{
name: "ai-agent",
allowTables: ["products", "orders"], // only these tables
maxRows: 10,
// If a role doesn't define write (undefined), write is COMPLETELY off for that role,
// even if the table allows it. To permit writes, state it explicitly on the role too:
write: { insert: true, update: true, delete: false },
},
{
name: "analyst",
denyTables: ["audit_logs"], // this table is blocked
fieldRules: [
{ field: "email", strategy: "allow" }, // email unmasked
],
// write not defined → analyst can write nothing
},
],
// Active role (can also be set via the GATEWAY_ROLE env var)
activeRole: "ai-agent",
});
脱敏策略
| 策略 | 说明 | 示例 |
|---|---|---|
redact | 完全移除此字段 | [REDACTED] |
mask | 将值的中间部分替换为星号 | ah***@example.com / 530***67 |
hash | SHA-256(前 16 个字符) | a665a45920422f9d |
allow | 原样传递 | [email protected] |
支持 Glob 模式:*Password*、*Token*、*Secret*
文本列中的 PII(piiScan)
字段规则按列名匹配,因此自由文本列(notes、description)仍可能包含电子邮件、TC kimlik 或 API 密钥。默认关闭的 piiScan 会按值屏蔽所有未被字段规则覆盖的文本字段中的此类内容,包括嵌套对象和数组;allow 规则会对相应字段关闭此功能。使用 preset / enable / disable 选择检测器,预设与安全代理相同:ALL(默认)、KVKK、IDENTITY、IDENTIFIERS、SECRETS。
createServer({ piiScan: { enabled: true, preset: "KVKK", disable: ["address"] } }, adapter);
通过 CLI 设置:GATEWAY_PII_SCAN=true(启用所有默认检测器),或 GATEWAY_PII_SCAN=KVKK(使用某个预设)。未知预设或检测器名称会导致启动失败。
piiScan.blockPatterns 和 piiScan.allowPatterns 可添加自定义模式,与安全代理中的用法相同:block 会将你的 ID 屏蔽为 <CATEGORY_n>;allow 则会保留整个文本与模式匹配的检测值。若 block 模式运行超过 100 ms 时限,查询会失败,而不会返回可能未完成屏蔽的行。
createServer({ piiScan: { enabled: true, blockPatterns: [{ pattern: "CL-\\d{6}", category: "CLIENT_NO" }], allowPatterns: ["destek@acme\\.com\\.tr"] } }, adapter);
基于角色的访问控制(RBAC)
服务器启动时通过 GATEWAY_ROLE 环境变量或 config.activeRole 设置角色。
每个 Claude Desktop 配置文件或部署都可以使用不同的角色运行。
GATEWAY_ROLE=analyst node dist/cli.js
规则优先级(从高到低):
- 全局
tableRules拒绝规则 - 角色
denyTables - 角色
allowTables(白名单——若已设置,表必须在此列表中) - 角色
fieldRules→ 全局fieldRules
写入支持(写入/插入/更新/删除)
默认情况下,网关为完全只读。允许大模型修改数据需要主动解锁几道限制:
writesEnabled: true—— 全局紧急开关。设为false(默认值)时,Claude 永远看不到insert_row/update_row/delete_row。- 表权限 ——
tableRules[].write.{insert,update,delete}—— 每张表单独设置,默认全部关闭。 - 角色权限(启用角色时)——
roles[].write.{insert,update,delete}。若角色从未定义write(未定义),则该角色完全不能写入,即使表允许写入也不例外。要允许写入,表和角色都必须明确设为true(AND 逻辑——比脱敏规则使用的“角色覆盖”逻辑更严格)。
除这三道限制外,还有两项无法禁用的保护:
- 受保护字段防护 —— 大模型永远不能向
fieldRules中标记为redact/mask/hash的字段写入值(例如tcKimlik、passwordHash);如果insert_row/update_row在data中发现此类字段,则会拒绝整个请求。 maxAffectedRowsPerWrite—— 在执行update_row/delete_row之前,会先将筛选条件作为读取操作尝试;若匹配的行数超过此限制(默认值为 10),则会在执行任何更改前拒绝该操作。update_row/delete_row还会始终拒绝空筛选条件——无法通过此网关“更新/删除整张表”。
每次写入尝试(无论接受还是拒绝)都会记录到审计日志中;日志只记录写入了哪些字段,从不记录 data 本身(因此审计日志本身不会成为 PII 泄漏点)。
createServer({
writesEnabled: true,
maxAffectedRowsPerWrite: 10,
tableRules: [
{ table: "orders", access: "allow", write: { insert: true, update: true, delete: false } },
],
roles: [
{ name: "ai-agent", allowTables: ["orders"], write: { insert: true, update: true, delete: false } },
],
activeRole: "ai-agent",
});
Prisma 适配器
直接传入 PrismaClient——表名到模型的映射会自动完成:
| 查询表 | Prisma 模型 |
|---|---|
"users" | prisma.user |
"audit_logs" | prisma.auditLog |
"orders" | prisma.order |
"orderItems" | prisma.orderItem |
开发
npm run dev # dev mode via tsx
npm run build # TypeScript compile
npm test # 195 unit tests
npm run test:watch # watch mode
npm run type-check # type-check only
许可证
MIT — GuardBee
反馈
有问题、想法或遇到误报?请在 GitHub 讨论区中发起讨论,或提交问题。如果 GuardBee 对你有帮助,可以在代码仓库点个 ⭐,帮助其他人找到它。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。