Appearance
MCP 谁可以调用什么:Authorization、OAuth 与 Enterprise Managed Auth
前置:MCP 无状态请求如何路由和缓存:Header Routing 与 Cacheable Lists
核心问题:用户完成 OAuth 后,为什么模型仍不能任意调用所有 Tool,authentication 与 per-action authorization 怎样衔接?
模型“选择了一个 Tool”并不代表 Client 就有权调用它。真正的访问权来自用户/服务身份、token scope、audience、resource identity 与 Server policy;credential 应停留在 Client/Secret boundary,不进入 Prompt、Tool Schema 或 Trace payload。
Authorization
MCP Authorization 解决 Client 如何以某个 principal 访问受保护 Server/resource。Server 要验证 issuer、resource/audience、scope 与 token 生命周期;而 Host 仍要继续执行自己的 Tool policy,因此“OAuth 成功”不等于“模型可以执行任意 Tool”。
🔍 源码对照:MCP Auth 为什么需要 Durable-ish Wait Identity
精确定位
- 文件:
source/host/mcp-auth/mcp-auth-wait-registry.ts - Symbol:
McpAuthWaitRegistry.register()/take()/prune() - 文件:
source/host/mcp-auth/host-mcp-auth-completion.ts - Symbol:
registerConnectCard()/resolve()/resolveDesktop() - 深入索引:Code Atlas:12-auth-lifecycle
ts
register({ agentId, connector, serverId })
→ waitKey = normalize(connector) || serverId
→ store { agentId, serverId, expiresAtMs }
auth completion
→ waits.take({ serverId, serverName })
→ choose requesting/watch/waiting agent
→ resumeAfterMcpAuth(agentId, serverName, accountKey)对照理解
- Authorization 不只是在 HTTP header 里塞 token;对 Agent Runtime 来说,还要解决“哪个 Run/Agent 正在等哪一个授权完成事件”。
- Registry 有 TTL,并优先按 server id/name 匹配,避免过期的 auth wait 永久占住 Runtime state。
resolveDesktop()会先尝试 restart MCP management,再进入统一resolve();这体现 transport/connector lifecycle 与 Agent resume 的衔接,但不是 MCP Authorization 规范本身。
CIMD / OAuth 相关变化
2026-07-28 revision 正式把 Dynamic Client Registration 放入弃用路径,并转向 Client ID Metadata Documents(CIMD)。这属于协议 Adapter 的 metadata discovery 细节:业务 Runtime 只消费已经解析好的 identity/authorization context,不复制某一代 OAuth 注册流程。
Enterprise Managed Authorization
Enterprise Managed Authorization 关注的是组织级治理:管理员希望统一决定哪些用户/服务能访问哪些 MCP Server、哪些 scope 可以被委托,以及授权如何撤销和审计。
它和单个用户完成一次 OAuth flow 的层次不同。Runtime 最终应该拿到的是已经解析好的 principal + granted scopes/capabilities,而不是把企业身份规则或长期 credential 直接塞进模型 Context。
把“用户身份”和“Agent 能力”分开
假设用户要求 Agent 读取企业文档。即使 MCP Server 支持 OAuth,Runtime 仍需要区分:
text
谁登录了 → authentication
这个用户授权了哪些 scope → delegated authorization
当前 Agent/Run 是否允许用它 → local capability policy
这个具体 Tool 是否需要审批 → action-level policyOAuth token 只能证明某个授权关系,不能自动意味着“模型可以调用 token 能访问的一切”。Harness 应把 credential 保存在 credential/secret boundary 中,Tool executor 在真正执行时注入;不要把 bearer token 放进普通 model context、trace 或 tool result。
当 scope 不足时,正确结果应是结构化的 authorization failure 或重新授权流程,而不是让模型尝试寻找其他未批准的凭据。
Failure Modes
Credential 或 OAuth Token 进入模型 Context / Trace
症状: bearer token、client secret 或 API key 被写进 Tool 参数、Prompt、完整 Trace。
为什么危险: 模型/provider/日志获得了不需要的 secret,扩大泄漏面。
处理: 模型只拿 credential reference;执行边界通过 Secret Broker 注入最小 scope credential。
如何验证
| Case | 断言 |
|---|---|
| 注入测试 secret 执行一次受保护 MCP Tool | Context、ToolCall、Trace dump 中均不存在 secret 原值;无 scope 时调用被拒绝。 |
关联源码
自测
- MCP Server 知道“这个 Tool 存在”与调用者被授权执行它,是哪两个不同问题?OAuth 解决的是哪一个?
- 企业 Managed Auth 不能只把一个共享 token 注入所有用户请求。为什么 delegated identity/scope 必须被保留?