MCP工具暴露从90个到平台化需统一身份、计量、审计;命名规范{分类}.{动作}_{对象},每个工具独立权限测试。
通过 Model Context Protocol 暴露一个内部工具是周末就能搞定的事。但要把九十多个工具同时暴露给人类用户和 AI Agent,同时不让 MCP 端点变成凭证泄漏的重灾区、审计日志变成消防演练,就是一个平台级别的问题了。
本文是关于如何设计这个平台的简版方案。一句话总结:把 MCP server 视为与 LLM 网关同族系的工具代理。相同的身份规则(无静态密钥)、相同的计量口径(每次调用都是一条事件)、相同的审计规范。另外加上 LLM 路径不需要的两样东西:第三方凭证的存储包装,以及回传数据的 PII 脱敏环节。
在 90+ 工具的规模下,首要失败是组织层面的,而非性能。几条规则让目录保持健康:
命名即契约:{category}.{verb}_{object},例如 issues.create_ticket、mail.send_draft、ci.get_pipeline_logs。重命名一个工具是破坏性变更,需要有弃用窗口期,而非静默修改。
一个工具 = 一个权限单元 + 一个审计单元 + 一个测试单元。一个"搞定工单追踪器一切"的工具,是你无法安全授予的权限、无法阅读的审计行、无法编写的测试。要拆分到每个工具同时满足这三者。
在 schema 中版本化,而非名称(issues.create_ticket@v2),并在弃用窗口期内同时服务所有活跃版本。
授予工具组,而非全集。服务端几乎不关心 94 个 schema,客户端则不然:一个加载了所有 schema 的 Agent 仅工具定义就能烧掉 30k+ token 的上下文。客户端请求一个组(如 ci、mail),组就是授权单元。
一个值得审视的工具定义大致长这样:
{
"id": "issues.create_ticket@v2",
"input_schema": {
"type": "object",
"required": ["project", "summary"],
"properties": {
"project": { "type": "string" },
"summary": { "type": "string", "maxLength": 256 }
}
},
"errors": ["permission_denied", "validation_failed", "upstream_error", "rate_limited"],
"pii": { "inputs": ["description"], "policy": "mask-on-store" },
"permissions": { "min_role": "contributor", "write": true, "idempotency": "client_key" }
}
背后的模式:
幂等键用于可重试的写操作。带相同 key 的重试创建返回同一张工单,而非重复工单。将其作为写工具的注册时 lint 检查。
错误是契约的一部分。将上游失败规范化为小规模分类,并附带 retryable 标志(有的话加上 Retry-After)。Agent 根据标志重试;人类阅读错误码。
读廉价,写响亮。写操作需要最小角色和作用域(project、space、folder)。写的审计事件携带完整(脱敏后的)输入;读的审计事件只记录形状。
所有列表都要设上限。默认 limit 50,服务端上限 200,cursor 分页。无上限的列表工具是上下文窗口的炸弹。
返回所请求的,而非你所知道的。"关联工单"这类 enrichment 是 Agent 可选择调用的第二个工具。过度返回的工具是 Agent 无法规划的。
Happy path 很简单:客户端针对你的身份平台运行授权码流程 + PKCE,获取短期访问令牌,在 MCP 端点上出示它。服务端验证令牌、解析主体、检查工具授权。真正安全的关键在于:
所有客户端类型都启用 PKCE,包括 IDE 和 Agent 运行时。MCP 客户端通常无法持有密钥。
Resource indicators。令牌的受众是 MCP server。为其他服务签发的有效令牌会被拒绝。
动态客户端注册不是授权。DCR 保持"带上你自己的 Agent 运行时"这种模式可行(重定向 URI 白名单、认证方法检查),但工具访问仍需通过显式授权流转。
组织内统一一种令牌时效策略。例如:交互式客户端用 15 分钟访问令牌,工作负载用 1 小时,Agent 不使用刷新令牌(它们通过工作负载身份重新认证)。
每个工具组一个 scope(mcp:ci:read、mcp:issues:write),让consent 屏幕可读,而非 94 个字符串的堆砌。
客户端的令牌永不发送到 SaaS。代理用自身凭证调用下游系统,并在带内携带主体用于审计和计量。当下游系统有清晰的用户级委托模型时,优先使用它;这是更强的设计。
代理必须为背后的系统存储凭证。用标准信封机制来做:
KMS key-encryption key(永不离开 KMS,自动轮换)
└── 每个类别一个 data-encryption key(AES-256,由 KEK 包装)
└── 秘密值:AES-256-GCM(DEK, value),每次写入fresh 96-bit nonce
每个类别独立的 DEK 将爆炸半径控制在一个类别内。每个用户的令牌在类别 DEK 下再套一层用户专属 DEK。
轮换设计上很便宜:KMS 轮换 KEK;定时任务重新包装 DEK。两者都不触碰密文。
但静态加密只回答了"数据库泄露不等于凭证泄露"。真正的控制是:没有任何角色(break-glass 除外)能读取秘钥。代理在进程内使用它完成一次调用后将缓冲区清零,审计事件记录调用和主体,绝不记录凭证。门户应显示"凭证存在,上次轮换于 N 天前",不多不少。
LLM 网关可以保持对载荷无感知。MCP 路径不行:工具结果就是产品。因此结果在到达客户端之前、存储之前会经过一道 PII 脱敏步骤,目录中每个工具的 pii 字段标明了哪些输入输出需要处理。这也解释了为什么写操作的审计存储脱敏后的输入,而非原始输入。
这是我的书《The AI Gateway Playbook》第五章的精简版,该章还涵盖了 LLM 网关本身:模型注册表、无密钥认证和 RBAC、配额与预算、RAG 团队助手以及运营手册。Leanpub 页面有免费样章。
声明:本文及本书由 AI 辅助基于我设计并运行此类平台的经验起草。所有示例均为通用示例。