mcpField
D
db-gateway

db-gateway

本地服务
github.com/GuardBee安全11 天前更新

关于 db-gateway

db-gateway 是置于大模型与数据库之间的 MCP 服务,面向需要管控数据库访问的团队。它提供自动个人信息遮蔽、基于角色的规则、限流和查询审计。

主要功能

  1. 01自动遮蔽个人信息
  2. 02按角色限制表和字段访问
  3. 03全局及按表限流
  4. 04审计输出与查询历史
  5. 05支持 Prisma、SQL 数据库和 MongoDB
  6. 06可选的受控写入操作

适用场景

  1. 01遮蔽数据库结果中的敏感字段
  2. 02将 Agent 限制为访问获准的表
  3. 03查看被拒绝或已完成的数据库查询
  4. 04受控地插入、更新和删除数据

MCP 相关

向 MCP 客户端提供数据库查询、表结构、状态和审计日志工具。启用写入后,还可提供插入、更新和删除工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"4hmetuyar-db-gateway": {
"command": "npx",
"args": [
"-y",
"@guardbee/mcp-db-gateway"
]
}
}
}
启动命令npx -y @guardbee/mcp-db-gateway

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@guardbee/mcp-db-gateway0.3.5

工具

5

收录的工具列表 · 更新于 2 周前

参数类型必填说明
limitinteger否应用网关限制前要获取的最大行数(默认 50)
tablestring是要查询的表名
filterobject否键值筛选条件对,例如 { status: 'active' }

详细介绍

@guardbee/mcp-db-gateway

🇬🇧 英语 | 🇹🇷 土耳其语 | 🇨🇳 中文 | 🇯🇵 日语 | 🇰🇷 韩语 | 🇧🇷 葡萄牙语 | 🇪🇸 西班牙语 | 🇩🇪 德语

npm version npm downloads License: MIT Smithery

一个符合 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、pg Pool、mysql2 Pool、better-sqlite3 Database 或 MongoDB Db
  • 写入支持(可选) — 支持 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.json Linux: ~/.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
hashSHA-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

规则优先级(从高到低):

  1. 全局 tableRules 拒绝规则
  2. 角色 denyTables
  3. 角色 allowTables(白名单——若已设置,表必须在此列表中)
  4. 角色 fieldRules → 全局 fieldRules

写入支持(写入/插入/更新/删除)

默认情况下,网关为完全只读。允许大模型修改数据需要主动解锁几道限制:

  1. writesEnabled: true —— 全局紧急开关。设为 false(默认值)时,Claude 永远看不到 insert_row/update_row/delete_row。
  2. 表权限 —— tableRules[].write.{insert,update,delete} —— 每张表单独设置,默认全部关闭。
  3. 角色权限(启用角色时)—— 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 对你有帮助,可以在代码仓库点个 ⭐,帮助其他人找到它。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。