mcpField
A
approval-gate-mcp

approval-gate-mcp

本地服务
github.com/itcustomsolution安全03 天前更新

关于 approval-gate-mcp

approval-gate-mcp 是用于 AI Agent 高影响操作的 MCP 审批门,要求人工批准后才能继续发送、删除、发布、付款或部署等操作。它采用默认拒绝、限时且单次使用并绑定具体操作的批准机制,记录审计日志,但本身不执行操作。

主要功能

  1. 01申请并等待人工审批
  2. 02命令行查看、批准、拒绝或撤销
  3. 03批准绑定操作且限时单次使用
  4. 04默认拒绝并记录审计日志
  5. 05支持可信代码在执行边界消费批准

适用场景

  1. 01发送邮件前要求人工确认
  2. 02删除或发布数据前设置审批步骤
  3. 03付款或部署前核对具体操作和负载
  4. 04审查并撤销尚未消费的批准

MCP 相关

作为 MCP 服务,提供申请、查询、等待、拒绝和消费批准等工具;默认不允许通过 MCP 批准,以免 Agent 自行批准。人工可在终端 CLI 中作出决定,服务只记录意图与决定,不执行副作用。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"itcustomsolution-approval-gate": {
"command": "npx",
"args": [
"-y",
"@itcustomsolution/approval-gate-mcp"
]
}
}
}
启动命令npx -y @itcustomsolution/approval-gate-mcp

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

安装包
npm@itcustomsolution/approval-gate-mcp0.3.0

工具

6

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

参数类型必填说明
actionstring是拟执行操作的准确描述,例如 'send email to [email protected] with subject X'
reasonstring是此时需要执行此操作的原因
ttl_secondsinteger否请求过期前保持待处理状态的时长(默认 1 小时)
requested_bystring否请求方 Agent 或工作流的标识符

详细介绍

approval-gate-mcp

一个 MCP 服务,可让你的 AI Agent 在发送、删除、发布、付款或部署前停下来征求人工确认。

npm

license: MIT

原因

Agent 擅长执行任务。难点在于确保那些影响重大的操作 (发送电子邮件、删除数据、发布内容、花钱)只有在人类明确同意后才会发生。这个服务就是这样一道关卡,它源自我多年来在生产环境中运行的一个系统:一次自动化事故让我付出了惨痛教训,此后我便定下了这条常设规则:任何自动化破坏性操作都必须经过人工把关,绝不例外。这是这条规则的通用、可复用版本。

30 秒快速入门

claude mcp add approval-gate -- npx -y @itcustomsolution/approval-gate-mcp

Claude Desktop(claude_desktop_config.json):

{
  "mcpServers": {
    "approval-gate": {
      "command": "npx",
      "args": ["-y", "@itcustomsolution/approval-gate-mcp"]
    }
  }
}

使用 npx -p @itcustomsolution/approval-gate-mcp approval-gate list,即可在终端中批准或拒绝请求。

示例

  1. 你的 Agent 想给客户发送电子邮件。它调用 request_approval,传入 action: "send email to [email protected], subject: Invoice 1042",并获得 ID a1b2c3d4e5f6。
  2. 它调用 wait_for_approval。此时不会发送任何内容。
  3. 在你自己的终端中执行:approval-gate list,然后执行 approval-gate approve a1b2c3d4e5f6 looks right。
  4. Agent 收到 approved,随后使用同一个 ID 和完全相同的操作文本调用 consume_approval(仅可使用一次),然后才发送邮件。如果你拒绝、忽略请求,或让请求过期,结果都是否定。
  5. ~/.approval-gate/audit.jsonl 现在记录了该请求和你的决定。

设计原则

  • 默认拒绝。 只有成功消费才能授权操作: consume_approval 返回 ok: true(仅一次,且必须对应完全相同的已批准操作),或者在可信代码中调用 executeApproved。待处理、已批准但未消费、已拒绝、已过期、超时、出错:这些都不构成授权;它们的意思全都是不允许。
  • 关卡绝不执行任何操作。 它只记录意图和决定。你的 Agent 先发起请求,然后只有在获批后才执行。将决定与执行分开,能让关卡保持简单且可审计。
  • 决定在 Agent 上下文之外作出。 人类通过自己终端中的 CLI 批准或拒绝,而不是在 Agent 的上下文中操作。MCP 工具无法批准请求。CLI 要求使用交互式终端并重新输入确认内容,这只是增加了一道操作门槛,并非身份验证:以你的 OS 用户身份运行的任何程序(shell、PTY 或文件访问)仍能绕过它。请参阅威胁模型。
  • 只有消费才能授权。 status: "approved" 仅供参考。状态响应会携带 authorizesExecution: false 和 effectiveStatus(如 approved_unconsumed、approved_expired、consumed、revoked 等)。执行副作用的可信代码应调用 executeApproved(请参阅可信执行边界),由它自行消费批准。通过 MCP、文件或 Agent 收到的消费结果,无论格式多么完整,都不能作为批准的证明。
  • 所有内容都会被审计。 每个请求、决定、过期事件和消费操作都会追加写入 ~/.approval-gate/audit.jsonl(这是一种只追加的约定,并非防篡改日志)。
  • 请求会过期。 默认 TTL 为 1 小时;过期的批准不算批准。
  • 批准仅可使用一次,并与操作绑定。 consume_approval 仅能成功一次,且只适用于完全相同的已批准操作文本(如果请求中包含操作名称和规范化 JSON 负载,也必须完全相同),并且只有在所有持久化操作完成后读取的时钟仍不晚于请求的过期时间时才会成功。

安装

需要 Node.js 22 或更高版本(Node 18 和 20 已停止生命周期支持;MCP SDK 的 @hono/node-server 依赖需要 Node 20 或更高版本)。

从 npm 安装:

npm install -g @itcustomsolution/approval-gate-mcp   # installs `approval-gate` and `approval-gate-mcp`

从源码安装:

npm install
npm run build
npm link        # makes `approval-gate` and `approval-gate-mcp` available on PATH

将其接入 MCP 客户端

Claude Code:

claude mcp add approval-gate -- node /path/to/approval-gate-mcp/dist/index.js

或者在任何 MCP 客户端配置中:

{
  "mcpServers": {
    "approval-gate": {
      "command": "node",
      "args": ["/path/to/approval-gate-mcp/dist/index.js"]
    }
  }
}

工具

工具用途
request_approval注册计划执行的操作(action、reason、可选的 ttl_seconds、可选的 operation 和要绑定的 JSON payload)。返回一个 ID(以及 payloadDigest)。
check_approval轮询请求状态:pending / approved / denied / expired。
wait_for_approval最多阻塞 timeout_seconds,等待决策。超时 = 仍处于待处理状态 = 未获批准。
list_pending按时间从早到晚列出等待人工处理的请求,每次一页(limit 为 1-100,默认值为 50;将 nextCursor 作为 cursor 传回)。
consume_approval在执行操作前立即调用。仅当请求已获批准、未过期,且 action(以及请求中若包含 operation/payload,则它们也)完全匹配时,才会成功一次。其他任何情况:都不得继续。其 JSON 结果不是可转让的凭证。
decide_approval拒绝待处理的请求,或撤销尚未使用的已批准请求。默认拒绝通过 MCP 批准请求,以防 Agent 批准自己的请求;批准应来自 CLI。操作员可以选择启用 APPROVAL_GATE_ALLOW_MCP_APPROVE=1(不建议)。

人工操作

approval-gate list                # pending requests
approval-gate show a1b2c3d4e5f6
approval-gate approve a1b2c3d4e5f6 looks right, ship it
approval-gate deny a1b2c3d4e5f6 wrong recipient
approval-gate revoke a1b2c3d4e5f6 changed my mind   # approved but not yet consumed
approval-gate list approved --limit 20 --after a1b2c3d4e5f6   # page through history
approval-gate archive --older-than-days 30          # retention: move finished requests aside

当请求绑定了操作时,show 和 approve 确认步骤会打印操作和完整的规范化载荷(已转义): 执行器获准运行的内容正是这些。

建议的 Agent 策略

在 Agent 的系统提示词或策略文件中加入类似内容:

执行任何对外或破坏性操作(发送、发布、删除、购买、部署)之前, 调用 request_approval,然后调用 wait_for_approval。当它返回 approved 时,使用相同的 ID 和完全一致的操作文本调用 consume_approval,并且只有在它返回 ok: true 时才继续。绝不要对自己的请求调用 decide_approval。

可信执行边界

当由你自己的代码(而非 Agent)执行副作用时,让审批门在该边界消费批准,并准确运行获批的载荷:

import { executeApproved } from "@itcustomsolution/approval-gate-mcp/dist/store.js";

// id comes from the agent; the action, operation and payload are what your code is about to run.
await executeApproved(id, { action, operation: "smtp.send", payload: { to, subject, body } }, async (ctx) => {
  ctx.assertActive();          // re-check expiry immediately before the side effect
  await smtp.send(ctx.payload); // ctx.payload is a frozen copy of what the human approved
});

executeApproved 不接受回执。它会自行执行一次性消费(绑定操作文本,以及请求中若包含 operation 和 payload,则也绑定这两项),写入 execution_started 审计记录(fsync),再次检查是否过期,然后才调用你的函数。如果无法写入开始记录,或者批准在写入期间过期,则不会运行任何内容。在任何发生于副作用之前的 await 之后,再次调用 ctx.assertActive()。

assertConsumed(result, id, action, binding?) 仍供自行调用 consumeApproval 的代码使用:它只接受由 consumeApproval 在同一进程中签发的授权对象,且仅限于在过期前针对相同的 ID/操作/绑定使用一次。复制对象、进行 JSON 往返转换(MCP 响应、文件、Agent 消息),或手动构造的对象,一律会被拒绝。0.3.0 会接受任何格式正确的对象;详见更新日志。

崩溃和重放语义。 消费操作至多执行一次:一旦 .used 被标记,批准即告用尽,无论之后发生什么,包括标记后拒绝(持久化期间过期、审计失败)。如果在 execution_started 之后、execution_finished 之前发生崩溃,结果将无法确定:请检查外部系统,并在必要时创建并批准新请求。系统不会自动重试;此审批门的文件与回调所操作的外部系统之间也不保证原子性。详情:docs/authorization-states.md。

限制

限制值
action、reason、决策原因每项 8192 个 UTF-8 字节
requested_by256 字节(由请求方自行报告,未经身份验证)
operation1-128 个字符,仅限 A-Z a-z 0-9 . _ : / -
payload仅限 JSON;规范化后为 32 KiB(RFC 8785),深度 32,10000 个值;不得包含 "__proto__" 键
TTLMCP:整数,10 秒至 7 天;库:最多 7 天的任意有限数值(零或负值会生成已过期的请求)
等待决策的请求APPROVAL_GATE_MAX_PENDING,默认值为 100,范围为 1 至 10000。并发创建者最多可能使数量超出各自的请求数。无效值会安全失败。
列表页1-100(默认值为 50)
状态目录requests/ 中达到 100,000 个条目时拒绝新请求;超过 200,000 个条目时拒绝列出。读取、决策、消费和 archive 在任何规模下均可执行。

队列已满时,request_approval 会失败并提示 approval queue full,直到有人类用户决策请求或请求过期。已完成的请求会保留在磁盘上,直到运行 approval-gate archive;此命令只会移动至少 8 天前创建且已过期的已完成请求,因此归档的请求不会再被使用。MCP 客户端可以在没有人类用户参与的情况下创建并拒绝自己的请求,因此它可以将 requests/ 填满至创建上限;这会阻止新请求(属于可用性限制,类似队列上限),但不会妨碍人类用户进行决策。每个列表页都会读取每个请求文件,因此随着历史记录增多,列出请求会变慢,直到执行归档。

状态

所有内容都存储在 ~/.approval-gate/ 中(可通过 APPROVAL_GATE_DIR 覆盖): requests/*.json 用于保存当前状态,requests/*.final / *.used 用于保存一次性决策和消费标记,audit.jsonl 用于保存历史记录,archive/ 用于存放归档的请求。目录权限设为 0700,文件权限设为 0600。

隐私: 操作文本、原因、requested_by 以及任何绑定的 payload 都会以明文形式存储在这些文件中;操作文本和原因也会复制到 audit.jsonl 中。请勿在其中放入凭据或机密信息。

威胁模型

该门禁可以防范:

  • 只能通过 MCP 访问此服务的 Agent 批准自己的请求(MCP 无法批准)。
  • Agent 在非交互式 Shell 中驱动 CLI(approve 要求有 TTY,并输入确认信息)。
  • 将一次批准用于第二个或不同的操作(consume_approval 只能使用一次,且绑定到特定操作)。
  • 在请求过期后使用批准、批准/拒绝/过期之间的竞态、猜测 ID(128 位随机数)、没有匹配决策记录的格式错误或经过手动编辑的状态文件,以及请求文本中试图伪装你所批准内容的终端转义序列、双向文本覆盖符和不可见字符(CLI 会将它们显示为 \u 转义序列)。其他文字系统中的普通形似字母(例如主机名中的西里尔字母「а」)会照原样显示:请仔细阅读请求文本,不要草草浏览。

该门禁无法防范:

  • **这是信任限制,并非已修复的 Bug:拥有 Shell、PTY 或你这个 OS 用户文件访问权限的 Agent。**它可以驱动伪终端、编辑 ~/.approval-gate/,或者直接绕过门禁执行操作。该门禁是一种协作协议,加上审计记录,并非沙箱。若要强制执行,请将 Agent 作为单独的 OS 用户运行(或放入容器),使其无法读写门禁目录,并由你自己使用门禁的 CLI。
  • 实际负载。consume_approval 匹配的是操作文本。它无法证明 Agent 随后执行的邮件、SQL 或部署内容与你读到的内容一致。这种绑定依赖协作。有关强制执行的设计(仅为提案),请参阅 docs/design-trusted-executor.md。
  • **无人值守的重大操作。**如果没有 OS 级隔离(见上文),请勿将此门禁视为无人值守 Agent 执行不可逆操作时的唯一控制措施。确保这类路径在故障时默认拒绝,且不自动执行。
  • 从不调用门禁的 Agent。是否强制要求 Agent 发起请求,取决于你的 Agent 策略和工具。
  • 拥有 audit.jsonl 写入权限的任何人篡改该文件。

executeApproved 和负载绑定是本地防护措施:当你的受信任代码使用它们时,它们可以确保执行的负载与获批负载一致。但它们不会验证人的身份,也无法阻止同一用户下的 Agent 绕过门禁。仍需另行设置受信任的授权方/执行器:

  1. 执行器作为 Agent 无法控制的另一个 OS 用户、容器或服务账户运行,并持有执行副作用所需的唯一凭据。
  2. Agent 用户无法读写门禁状态目录。
  3. 人通过 Agent 无法获取的凭据进行批准(例如硬件密钥签名,或提示另一用户操作的 OS 提示),而非通过 TTY。
  4. Agent 仅通过 ID 引用请求执行器运行已获批的操作,绝不再次发送负载。
  5. 使用防篡改(哈希链式或存储在外部主机上的)审计日志。

此软件包不会实现上述任何内容;这需要负责人作出决定并提供新凭据。请参阅 docs/design-trusted-executor.md。

设置 APPROVAL_GATE_ALLOW_MCP_APPROVE=1 会重新启用通过 MCP 批准,并移除主要防护。只有在人是唯一操控 MCP 客户端的一方时才使用此设置。

docs/authorization-states.md 说明了状态转换、撤销和崩溃行为。approval-gate revoke <id>(或对已批准的请求执行 deny)会取消尚未使用的批准。

为什么要做得这么小?

因为模式比底层实现更重要。在我的生产系统中,同样的模式(请求、带外人工决策、故障时默认拒绝、审计记录)用于邮件、发布和部署之前,并已消除整类自动化事故。从小处着手,为高风险操作设置门禁,再根据事故复盘决定下一步要为哪些操作设置门禁。

贡献

欢迎提交问题和拉取请求。请保持改动小,并默认拒绝:任何可能将“否”变为“是”的改动都需要有测试。提交 PR 前请运行 npm test。 发布历史请参阅 CHANGELOG.md。

许可证

MIT,详见 LICENSE。


由 Olufela "Lu" Fagbure 构建 · itcustomsolution.com · MIT 许可证

评论

支持 Markdown

第一次接触 MCP?

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

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