@frontmcp/plugin-approval

中文文档
1.12.2发布于 2026/11/15Apache-2.0TypeScriptESM + CJS构建来源已验证近 30 天下载 5228

FrontMCP 的批准插件: 拒绝运行标记工具, 直至为调用者记录批准

中文使用文档
文档目录16 节
  1. 安装
  2. 使用量
  3. 客户所看到的
  4. 这个 approval 打开 @Tool
  5. this.approval
  6. 范围
  7. 选项
  8. 审计线索
  9. 代表团链
  10. Webhook 模式
  11. 重新检查模式
  12. 启动检查
  13. 文档
  14. 相关软件包
  15. 捐款
  16. 许可证

@ frontmcp/plugin - 核准

一个FrontMCP插件,拒绝运行一个工具,直到为呼叫者录制一个批准. 用来做工具 不应该在没有别人说的情况下跑,比如删除机票或者退款。 标记工具为 approval, (中文(简体) ), 直到你的代码批准之前 每一个电话都被拒绝 this.approval 或插件的商店. 每一次批准批准的记录,可以撤销或者规定失效. 你决定一个人如何答应: 窗体从 this.elicit(),或采用外部审批系统,插件会向您询问 网点 或通过 投票。 。 。 .

npm License

安装

npm install @frontmcp/plugin-approval @frontmcp/sdk

需要节点.js 24或后. 保留 @frontmcp/sdk 上。 核准 保存在内存中,除非您配置 storage重新获得支持ioredis)包括;对于其他商店,安装 他们自己的客户:

  • @vercel/kv,可选的同伴依赖,用于 storage: { type: 'vercel-kv' }。 。 。 .
  • @upstash/redis 页:1 storage: { type: 'upstash' }。 。 。 .

此插件不属于 @frontmcp/plugins 元包装.

使用量

注册插件于 @FrontMcp打开每个应用程序的工具,或者打开一个 @App标出需要批准的工具, 并给用户一个批准的方法:

import { ApprovalPlugin, userGrantor } from '@frontmcp/plugin-approval';
import { App, FrontMcp, Tool, ToolContext, z } from '@frontmcp/sdk';

@Tool({
  name: 'delete_ticket',
  description: 'Delete a support ticket for good. Needs approval: call approve_delete first.',
  inputSchema: { id: z.string() },
  approval: {
    approvalMessage: 'delete_ticket needs the user to approve it. Call approve_delete first.',
    category: 'delete',
    riskLevel: 'high',
  },
})
class DeleteTicket extends ToolContext {
  async execute({ id }: { id: string }) {
    return { id, deleted: true };
  }
}

@Tool({ name: 'approve_delete', description: 'Ask the user to allow delete_ticket for this session', inputSchema: {} })
class ApproveDelete extends ToolContext {
  async execute() {
    // The user answers this form in the client. The model can't.
    const answer = await this.elicit(
      'Allow the assistant to delete tickets in this session?',
      z.object({ allow: z.boolean() }),
    );
    if (answer.status !== 'accept' || !answer.content?.allow) return { approved: false };

    await this.approval.grantSessionApproval('help-desk:delete_ticket', {
      grantedBy: userGrantor(this.auth.user.sub),
    });
    return { approved: true };
  }
}

@App({ id: 'help-desk', name: 'Help Desk', tools: [DeleteTicket, ApproveDelete] })
class HelpDeskApp {}

@FrontMcp({
  info: { name: 'help-desk', version: '1.0.0' },
  apps: [HelpDeskApp],
  elicitation: { enabled: true },
  plugins: [ApprovalPlugin.init()],
})
export default class Server {}
  • 工具标识是 <app id>:<tool name>一样 help-desk:delete_ticket给予 'delete_ticket' 记录 a 批准什么也没有检查.
  • 批准属于打电话者,因此使用已签名用户的插件。 匿名电话可以换个新的 然后就永远不适用于他们的下一次通话.
  • 别让模特儿批准自己的电话. 一个不问人就提供工具 就能保护大门 没什么 如上所述,在表格后给予用户答复,或给予更多权利的呼叫者答复.
  • 字段需要它的插件 . 一个工具所在的服务器 approval 没有 ApprovalPlugin 到达它不会开始: 失败 UnenforcedMetadataError。 。 。 .
  • ApprovalPlugin.init() 没有争论 plugins: [ApprovalPlugin],使用默认值.

客户所看到的

拒绝调用是模型可以读取的工具错误: isError: true, (中文(简体) ), _meta.code: 'APPROVAL_REQUIRED'文本时 该工具的 approvalMessage在生产过程中(批准错误扩大) PublicMcpError) (中文(简体) ). 默认 留言, Tool "<id>" requires approval to execute. Allow?不要告诉模特儿下一步该做什么,所以写你 拥有的。 这个工具 category, (中文(简体) ), riskLevel 其他字段留在服务器上.

这个 approval 打开 @Tool

approval: true 表示 { required: true, defaultScope: ApprovalScope.SESSION }。任何物体都需要批准,除非 它说 required: false。 。 。 .

外地它会做什么
requiredfalse 把门关了 默认 true。 。 。 .
skipApprovaltrue 把门也关了.
alwaysPrompt每次批准都允许一个呼叫运行,并被使用.
approvalMessage拒书之文.
allowedScopes批准这一工具的范围。 补助金通过 this.approval 其他范围抛出 ApprovalScopeNotAllowedError。 。 。 .
maxTtlMs这个工具的核准时间最长.
preApprovedContexts让一个电话在未经批准的情况下运行 authInfo.extra.approvalContext 火柴。 比赛。 FrontMCP的认证模式没有设置,所以你自己的代码必须设置.
category, (中文(简体) ), riskLevel, (中文(简体) ), defaultScope锁定错误 details客户看不到的.

this.approval

该插件添加 this.approval 给它注册的应用程序的工具、资源和提示。 它的作用为 当前调用器,并在 <app id>:<tool name> 形式.

方法它会做什么
isApproved(toolId, context?)门是否让呼叫者运行工具 .
grantSessionApproval(toolId, options?)批准召会.
grantUserApproval(toolId, options?)批准在每届会议上召集人.
grantTimeLimitedApproval(toolId, ttlMs, options?)核准: ttlMs 毫秒长.
grantContextApproval(toolId, context, options?)在一个方面核准, { type, identifier } (见 preApprovedContexts) (中文(简体) ).
revokeApproval(toolId, options?)删去呼叫人对工具的认可,保留副本24小时.
getRevocations(toolId)调用者对该工具的认可在后24小时内被撤销.
getApproval, (中文(简体) ), getSessionApprovals, (中文(简体) ), getUserApprovals, (中文(简体) ), queryApprovals, (中文(简体) ), clearSessionApprovals读取或清取来电所录.

赠款 options 为: { grantedBy?, reason?, metadata? }选项 { revokedBy?, reason? }没有 grantedBy,一个赠款记录了与 method: 'implicit'上面写着没人问: { source: 'user', identifier: <user id>, method: 'implicit' },或 { source: 'user', method: 'implicit' } 没有 注册用户。 在用户同意后,通过一个设保人说:

import { policyGrantor, userGrantor } from '@frontmcp/plugin-approval';

// { source: 'user', identifier: 'user-123', displayName: 'Jane Doe', method: 'interactive' }
await this.approval.grantUserApproval('ops:deploy', { grantedBy: userGrantor('user-123', 'Jane Doe') });

// { source: 'policy', identifier: 'safe-list', method: 'implicit' }
await this.approval.grantSessionApproval('ops:deploy', { grantedBy: policyGrantor('safe-list') });

设保人的其他帮助者是: adminGrantor, (中文(简体) ), systemGrantor, (中文(简体) ), agentGrantor, (中文(简体) ), apiGrantor, (中文(简体) ), oauthGrantor, (中文(简体) ), testGrantor 财务报告和审定财务报表 customGrantor,与匹配的撤销器助手(userRevoker, (中文(简体) ), adminRevoker, (中文(简体) ), policyRevoker,以及 页:1.

要批准或撤销除打电话者以外的人,如批准队友的领先者,使用商店: this.get(ApprovalStoreToken) 返回,使用 grantApproval({ toolId, scope, userId?, sessionId?, ttlMs?, grantedBy?, ... }) 财务报告和审定财务报表 revokeApproval(...)。 。 。 .

范围

ApprovalScope 是字符串音符 :

范围数值它会做什么
SESSION'session'核准本届会议.
USER'user'在每届会议上为用户核准.
TIME_LIMITED'time_limited'需要a ttlMs,并批准至到期.
CONTEXT_SPECIFIC'context_specific'仅核准发出这方面的呼吁,因为 authInfo.extra.approvalContext 携带.
TOOL_SPECIFIC'tool_specific'标签 插件中没有任何内容授予它.

根据MCP协议2026-07-28,客户端没有会话,因此每个用户都会保持会话批准,并持续到完成 撤销或过期。 在有需要时,给予有时限的批准.

选项

ApprovalPlugin.init({
  storage: { type: 'redis', redis: { url: process.env.REDIS_URL } },
});
选项默认说明
storage{ type: 'auto' }在保留批准的情况下: 'memory', (中文(简体) ), 'redis', (中文(简体) ), 'vercel-kv', (中文(简体) ), 'upstash', (中文(简体) ), 'cloudflare-kv' 或者说 'auto'。 。 。 . 'auto' 从其环境变量中选择 Upstash, Vercel KV 或 Redis, 否则则选择内存 .
storageInstance一个你做的存储器 createStorage() 从 @frontmcp/utils胜利结束 storage。 。 。 .
namespace'approval'插件密钥的前缀 .
cleanupIntervalSeconds60过期核准被删除的频率如何. 0 关掉.
mode'recheck''recheck' 民意调查 recheck.url 当它被设置的时候; 'webhook' 登记a ChallengeService 并向 webhook.url 当它被设置。 没有 URL , 既不接触外部系统 .
recheck见 重新检查模式。 。 。 .
webhook见 Webhook 模式。 。 。 . webhook.challengeTtl (默认) 300)是挑战持续多久,以秒计.
enableAuditfalse日志 a 审计线索。 。 。 .
maxDelegationDepth3最深处 授权链 授权批准可以指名,0至64个.

在浏览器构建中( I)create() 在网页上,WebMCP, storage: { type: 'redis' } 或者说 { type: 'sqlite' } 掷出 BrowserStoreUnavailableError 从 init()名称 storage: { type: 'memory' }: 一页没有 TCP 套接字, 没有 文档. { type: 'auto' } 在那里选择记忆.

审计线索

与 enableAudit: true,插件日志 1 info 线条( 浏览器) ApprovalAudit每个事件。 讯息是 <event> <toolId>,第一个论点是:

活动何时
approval.requested一通电话没有得到任何认可 它可以使用. state数字 : pending, (中文(简体) ), expired, (中文(简体) ), denied,或 approved 由复检系统进行; via数字 : webhook 或者说 recheck。 。 。 .
approval.granted核准记录如下: this.approval,商店,一个webhook召回或重新检查答案.
approval.denied一份否认书被记录在案.
approval.revoked一项批准被撤销(revokedBy, (中文(简体) ), reason),或会议批准被批准(count) (中文(简体) ).
approval.expired清理删除了过期的审批或拒绝.

字段是工具、范围、用户编号、上下文、设保人或撤销人、理由和 expiresAt。一条线从不携带 会话 id、 符号或记录 metadata。一个过期的后端,例如Redis,可以删除已过期的 记录在清理之前; approval.granted 携带其 expiresAt。 。 。 .

默认关闭:最多1.11.2项选项无所作为(其记录的默认为 true,并打开它添加 线条,在日志中带有用户编号。 以木质运输方式追踪他们.

代表团链

授权批准姓名,授权: grantedBy.delegationContext, (中文(简体) ), { delegatorId, delegateId, purpose?, constraints?, chain? }代表可进一步授权: chain 代表团名单 授权人的第一个,和 extendDelegation() 建立下个.

import { agentGrantor, extendDelegation } from '@frontmcp/plugin-approval';

const toPlanner = { delegatorId: 'user-123', delegateId: 'planner-agent', purpose: 'release' };
const toDeployer = extendDelegation(toPlanner, 'deploy-agent', { purpose: 'deploy step' });
// { delegatorId: 'planner-agent', delegateId: 'deploy-agent', purpose: 'deploy step',
//   chain: [{ delegatorId: 'user-123', delegateId: 'planner-agent', purpose: 'release' }] }

await this.approval.grantSessionApproval('ops:deploy', { grantedBy: agentGrantor('deploy-agent', toDeployer) });

maxDelegationDepth (默认) 3限制链条,计算代表团本身: toPlanner 一个深, toDeployer 2. 其链条更深、断裂(不是前一名代表的委托人)或当事人名称的赠款 两次抛出 ApprovalOperationError,而存储的批准 这样不允许一个电话通过。 拒绝保留 无论他们的链子是什么. maxDelegationDepth: 1 仅允许直接授权,以及 0 没有授权核准. delegationDepth(context) 财务报告和审定财务报表 delegationProblem(context, maxDepth) 自己检查一个上下文。 最多1.11.2选项 什么都没做.

Webhook 模式

与 mode: 'webhook' 财务报告和审定财务报表 webhook.url电话需要批准 它不会发出签名的批准请求 并被拒绝作为 pending. 系统 由 POSTING 返回到 webhook.callbackPath,而打电话者的下一个呼叫运行,或被拒绝作为 denied。 。 。 .

ApprovalPlugin.init({
  storage: { type: 'redis', redis: { url: process.env.REDIS_URL } },
  mode: 'webhook',
  webhook: {
    url: 'https://approvals.example.com/webhook',
    secret: process.env.APPROVAL_WEBHOOK_SECRET, // required with url, at least 24 bytes
    challengeTtl: 300,
    callbackPath: '/approval/callback',
  },
});
选项默认说明
webhook.url请求被搁置的地方. https://· ; http://localhost, (中文(简体) ), 127.0.0.1 或者说 [::1] 仅在 NODE_ENV 没有 production没有证书.
webhook.secret签名每个请求并验证每个召回。 要求为 urlA级 whsec_ secret (Standard Webhooks) 是前缀后的基础64.
webhook.includeJwtfalse在请求中添加调用符 jwt. 只有当审批制度必须充当调用人时才设置.
webhook.challengeTtl300驳回请求等待裁决 驳回请求没有 ttlMs 持续时间.
webhook.callbackPath'/approval/callback'服务器作出决定的路径( T)POST) (中文(简体) ).

请求,每个呼叫者和工具发送一次,直至决定或 challengeTtl 通行证 :

{
  "type": "approval.requested",
  "codeChallenge": "…",
  "codeVerifier": "…",
  "toolId": "ops:delete_ticket",
  "toolName": "delete_ticket",
  "category": "delete",
  "riskLevel": "high",
  "approvalMessage": "…",
  "requestedScope": "session",
  "userId": "user-123",
  "callbackPath": "/approval/callback",
  "callbackUrl": "https://mcp.example.com/approval/callback",
  "expiresAt": 1700000300000,
  "timestamp": 1700000000000
}

它从来不给会话命名. callbackUrl 有没有当 FRONTMCP_PUBLIC_URL 设置; allowedScopes 财务报告和审定财务报表 maxTtlMs 当工具限制它们时; jwt 仅使用 includeJwt: true保留 codeVerifier 秘密:决定 请求。 失败的请求( 错误状态, 重定向, 10 秒内没有答案) 由下一个调用再次发送 .

这一决定,被选为: callbackPath 并以同一秘密签署:

{
  "codeVerifier": "…",
  "approved": true,
  "scope": "session",
  "ttlMs": 3600000,
  "grantedBy": { "source": "user", "identifier": "lead-7" },
  "reason": "ok"
}
  • 被记录下来的,是给打电话的人的 要求,从来没有一个尸体的名字 allowedScopes 财务报告和审定财务报表 maxTtlMs (单位:千美元)scope 默认为 requestedScope),与 grantedBy 财务报告和审定财务报表 origin: 'webhook'A级 拒绝(approved: false)记录在呼叫者的会话中,并持续 ttlMs,或 challengeTtl 没有它.
  • 每个 codeVerifier 工作过一次,直到 challengeTtl 通过。 重播或迟到的决定毫无作用.
  • 每一个签名的,造型良好的召回 202 { "status": "received" },无论它是否符合请求,所以 路由从未说明是否等待批准。 未签名或错误签名的回调,或一个以上签名的回调 几分钟前,得到 401一个不是决定的身体 400; 1 超过 16 KB 413。 。 。 .

请求和回调与HMAC-SHA256在 标准网络呼号 格式,所以任何标准 Webhooks 库检查它们:页眉 webhook-id, (中文(简体) ), webhook-timestamp (Unix 秒)和 (韩语) webhook-signature: v1,<base64>结束 <webhook-id>.<webhook-timestamp>.<body>检查时间戳在你这边, 并拒绝一个 webhook-id 你已经看到了.

this.get(ChallengeServiceToken) 返回插件 ChallengeService,这些一次性 PKCE (Proof Key) 的存储器 代码交换,RFC 7636挑战: createChallenge(...), (中文(简体) ), consume(codeVerifier) (请求一次,或 undefined和 verifyAndConsume(codeVerifier) (请求一次,或 ChallengeValidationError) (中文(简体) ).

重新检查模式

与 mode: 'recheck' (默认)和 recheck.url需要你同意的电话 没有民调 批准API直到它决定.

ApprovalPlugin.init({
  recheck: {
    url: 'https://approvals.example.com/status',
    auth: 'bearer',
    token: process.env.APPROVALS_API_TOKEN,
    interval: 2000,
    maxAttempts: 5,
  },
});
选项默认说明
recheck.url状态 URL, 规则与 webhook.url。 。 。 .
recheck.auth'none''bearer' 发送 recheck.token· ; 'jwt' 转发呼叫者自己的访问信使(没有传票的呼叫); 'custom' 发送信头 recheck.headers(request) 返回.
recheck.token标志 auth: 'bearer' 发送.
recheck.headers(request) => headers,用于 auth: 'custom'。 。 。 .
recheck.secret在每张民意测验上签名时, 请签名 .
recheck.interval2000民调之间的相差100到60000秒.
recheck.maxAttempts5每通电话1到60. interval × (maxAttempts − 1) 最多两分钟.

每份民意调查 { type: 'approval.status', requestId, attempt, toolId, toolName, userId?, requestedScope, ... } (单位:千美元)requestId 对一个打电话者和工具的每一次民意测验都是一样的,姓名也不相同),并读作: { approved, pending?, scope?, ttlMs?, grantedBy?, denialReason? }数字 :

  • { "approved": true } 记录了批准情况,因为网上核准记录了批准情况,但与 origin: 'recheck',则 呼叫运行。 一个 alwaysPrompt 工具的批准让它单独通过 并且没有存储.
  • { "approved": false, "pending": true } 投票之后 interval; 之后 maxAttempts 民意调查 电话被拒绝 pending。 。 。 .
  • { "approved": false } 拒绝调用为 denied. 无所存: 下一次民调.
  • 一个错误状态,一个方向调整,一个不是这样的答案的身体,超过64 KB,或者10秒内没有答案拒绝 调用为 pending,并登录一个只列出 URL 来源的警告.

有记录的拒绝在任何信息被传票或发出之前,以两种方式拒绝打电话.

启动检查

ApprovalPlugin.init() 掷出 ApprovalConfigurationError (代码) APPROVAL_CONFIGURATION),服务器不 启动, 对于无法工作的选项 : webhook.url 无 webhook.secret,或24字节以下的秘密; webhook.* (不含 challengeTtl页:1 mode: 'webhook' 或无 webhook.url· ; recheck 内 mode: 'webhook' 或者说 无 recheck.url; 不是一个 URL https:// (或外部生产回放),或在其中有资质; auth: 'bearer' 无 token, (中文(简体) ), auth: 'custom' 无 headers,或为另一个设定 auth; 一个 interval 或者说 maxAttempts a 区域外;和 callbackPath 这不是一条平坦的道路,服务器保留了 MCP 端点, /oauth/*, (中文(简体) ), /.well-known/*),或另一个 ApprovalPlugin 服务。 最多为1.11.2项选择 被接受和忽视.

文档

捐款

资料来源: plugins/plugin-approval问题 欢迎抽取请求;见 捐款额。 。 。 .

许可证

阿帕奇2.0 (英语). 见 自由主义。 。 。 .

版本与发布记录

@frontmcp/plugin-approval 共发布 112 个版本,首次发布于 2026/01/09;过去 12 个月发布了 112 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.12.2latest2026/11/151 天前
1.12.12026/11/132 天前
1.12.02026/11/114 天前
1.12.0-rc.1rc2026/11/114 天前
1.11.32026/11/106 天前
1.11.22026/11/096 天前

其他发布通道:beta → 1.1.2-beta.1,rc → 1.12.0-rc.1。

关于 @frontmcp/plugin-approval 的常见问题

@frontmcp/plugin-approval 是什么?

FrontMCP 的批准插件: 拒绝运行标记工具, 直至为调用者记录批准

如何安装 @frontmcp/plugin-approval?

在项目目录运行 npm install @frontmcp/plugin-approval,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@frontmcp/sdk(1.12.2)、@frontmcp/lazy-zod(1.12.2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@frontmcp/plugin-approval 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@frontmcp/plugin-approval 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@frontmcp/plugin-approval 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@frontmcp/plugin-approval 可以商用吗?

@frontmcp/plugin-approval 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@frontmcp/plugin-approval 的下载量有多少?

根据 npm 官方下载统计,@frontmcp/plugin-approval 在 2026/10/16 – 2026/11/14 这 30 天内被下载 5,228 次,日均约 174 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@frontmcp/plugin-approval 还在维护吗?

最新版本 1.12.2 发布于 2026/11/15(1 天前);至今共发布 112 个版本,其中过去 12 个月发布了 112 个;首次发布于 2026/01/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。