把 MCP Server 从本机 stdio 迁到可被编排器共享的 HTTPS 端点后,传输层会话(Mcp-Session-Id、SSE 续传)解决的是「如何可靠收发 JSON-RPC」;鉴权解决的是「谁有权调用 tools/call」。MCP 2025-06-18 在 HTTP 传输上把受保护服务器定义为 OAuth 2.1 资源服务器(Resource Server),客户端为 OAuth Client,并强制通过 RFC 9728 做授权服务器发现。若 Agent 网关仍用长期 API Key 拼在查询串、或把用户 OAuth 令牌原样转给下游 SaaS,会与规范中的 audience 绑定、禁止 token passthrough 等要求直接冲突。本文只讨论 MCP 官方 Authorization 章节与相关 RFC 的落地参数,不涉及某一商业 IDE 的内部凭据存储实现。
问题背景:无鉴权 HTTP MCP 与多租户 Agent 的缺口
典型生产场景里,远程 MCP 具备以下特征:
- 多 Agent、多租户:同一 MCP 路径可能被不同编排 Pod、不同终端用户复用,需要按主体授权而非仅靠网络 ACL。
- 工具副作用:
tools/call可能触发写操作;传输层 401 与业务层 403(scope 不足)必须可区分,便于 Agent 重试或升级授权。 - 动态服务器目录:用户配置的 MCP URL 在运行时才出现,客户端无法预先在控制台为每个 AS 手工注册 OAuth 应用。
- 与 Streamable HTTP 并存:规范要求每一次 HTTP 请求(含同一逻辑会话内的 POST/GET/DELETE)都携带
Authorization: Bearer,不能只在initialize时带一次令牌。
MCP 规范明确:stdio 传输不应走本章 OAuth 流程,而应从环境变量等受控渠道取凭据;HTTP 类传输在支持鉴权时应符合本章。因此「网关只反代、不实现 401 → 元数据发现」会在客户端首次无 token 访问时无法自动拉起授权。
可落地实现:发现、注册、授权与请求头
1. 401 与 Protected Resource Metadata(RFC 9728)
客户端向 MCP 端点发起无 token 请求时,资源服务器应返回 HTTP 401,并在 WWW-Authenticate 中给出资源元数据 URL(RFC 9728 §5.1)。客户端必须能解析该头并继续发现流程。
资源元数据文档(路径由部署约定,规范示例为 /.well-known/oauth-protected-resource)必须包含 authorization_servers 数组,且至少一个授权服务器。多 AS 时,由客户端按 RFC 9728 §7.6 选择使用哪一个。
最小元数据形状(字段名以 RFC 9728 为准,值需与真实部署一致):
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}
运维对照项:
| 检查项 | 期望 |
|---|---|
无 token 的 POST /mcp |
401 + WWW-Authenticate 含 resource_metadata 或等效指引 |
resource 字段 |
与后续 RFC 8707 resource 参数使用的 canonical URI 一致 |
| HTTPS | 授权服务器所有端点必须为 HTTPS(OAuth 2.1 §1.5) |
2. Authorization Server Metadata(RFC 8414)
客户端对选定的 AS 请求 /.well-known/oauth-authorization-server,获取 authorization_endpoint、token_endpoint、registration_endpoint(若支持动态注册)等。MCP 规范要求客户端必须使用该元数据,不得硬编码猜测路径。
3. 动态客户端注册(RFC 7591,SHOULD)
规范 SHOULD 支持 RFC 7591,以便客户端在未知 MCP/AS 组合时自动拿到 client_id(及机密客户端的凭据)。不支持 DCR 的 AS 需另提供注册途径:客户端只能硬编码该 AS 的 client 配置,或向用户展示配置 UI。
Agent 平台侧建议为每次 DCR 记录:client_id、as_issuer、registered_at,并与租户绑定;轮换 AS 密钥时需能重新注册或更新元数据缓存 TTL(例如 24h,失败时强制刷新)。
4. 授权码流 + PKCE(必须)
MCP 客户端必须实现 PKCE(OAuth 2.1 §7.5.2)。推荐参数(与 OAuth 2.1 常见实践一致):
| 参数 | 建议值 |
|---|---|
code_challenge_method |
S256 |
code_verifier |
43–128 字符高熵随机串 |
state |
客户端随机值,回调时严格比对,不匹配则丢弃 |
redirect_uri |
预注册精确匹配;原生 / 桌面客户端常用 http://127.0.0.1:<port>/callback 或自定义 scheme |
授权请求与令牌请求中,客户端必须携带 RFC 8707 resource 参数,标识令牌所针对的 MCP 资源(与 Protected Resource Metadata 中的 canonical URI 对齐)。规范要求:无论 AS 是否声明支持,客户端都必须发送。
示例(授权端点查询片段,需 URL 编码):
response_type=code
&client_id=<client_id>
&redirect_uri=https%3A%2F%2Fagent.example.com%2Foauth%2Fcallback
&scope=mcp%3Atools
&state=<random>
&code_challenge=<S256_challenge>
&code_challenge_method=S256
&resource=https%3A%2F%2Fmcp.example.com%2Fmcp
Canonical URI 规则(摘要):须含 scheme;不得含 fragment;宜使用小写 scheme/host;路径需能唯一标识 MCP 服务(例如 https://mcp.example.com/mcp)。无效示例:mcp.example.com(缺 scheme)、带 #fragment 的 URL。
5. 访问令牌在 MCP HTTP 上的使用
规范要求(OAuth 2.1 §5):
- 每个发往 MCP 的 HTTP 请求都带
Authorization: Bearer <access_token>,包括同一Mcp-Session-Id会话内的后续 POST/GET。 - 禁止把 token 放在 query string。
- 资源服务器必须按 OAuth 2.1 §5.2 校验令牌,并验证令牌是为本 MCP 资源签发(RFC 8707 audience);失败返回 401。
- MCP 服务器不得接受或转发「发给其他受众」的令牌;若 MCP 再调用上游 API,必须使用上游 AS 签发的独立令牌,禁止 token passthrough。
与 Streamable HTTP 组合的请求示例:
POST /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18
Mcp-Session-Id: 7f3c9e2a-4b1d-4c8a-9f0e-1a2b3c4d5e6f
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
6. 错误码与 Agent 行为
| HTTP 状态 | 含义 | Agent 侧建议 |
|---|---|---|
| 401 | 缺 token、过期或 audience 错误 | 触发刷新令牌或重新走授权;勿用错误 token 重试写工具 |
| 403 | scope 或策略拒绝 | 提示用户扩大授权或换工具;勿无限重试 |
| 400 | 畸形 OAuth/MCP 请求 | 记录请求 id,修配置后重试 |
授权服务器应签发短生命周期 access token;对 public client,AS 必须按 OAuth 2.1 §4.3.1 轮换 refresh token。Agent 运行时应把 token 存于 OS 密钥链或加密的租户级密钥库,避免写入模型上下文或工具入参日志。
7. 网关与反向代理注意点
若 Nginx/Envoy 在 MCP 上游前终结 TLS 并做 WAF:
- 不要把 Bearer token 记入 access log 默认字段;需要审计时使用哈希或截断。
- 401 响应体与
WWW-Authenticate头应原样透传,避免「统一 403 页面」破坏发现流程。 - 多 AS 场景下,缓存 Protected Resource Metadata 时宜以完整 MCP base URL 为键,防止路径级 MCP 实例串元数据。
风险与边界
规范可选 vs 生产强制。 Authorization 对 MCP 实现是 OPTIONAL,但企业内网「默认无鉴权」的 HTTP MCP 一旦暴露到可被用户配置的 Agent,风险与公网等价。传输层 Origin 校验不能替代 OAuth。
resource URI 不一致。 授权时用 https://mcp.example.com,校验 audience 时期望 https://mcp.example.com/mcp(或相反)会导致合法用户持续 401。应在部署清单中固定 canonical URI,并在 CI 中用契约测试打 401→元数据→mock token 校验。
DCR 滥用。 自动注册降低摩擦,也扩大攻击面:AS 应配合速率限制、批准策略或 mTLS;Agent 厂商需防止恶意租户批量注册傀儡 client。
刷新与长会话 SSE。 Streamable HTTP 下 SSE 可能持续数小时;access token 过期时,需在不中断用户预期的前提下刷新并重连,同时遵守「每请求带 Authorization」。实现上宜在 token 剩余寿命低于阈值(例如 120s)时主动刷新,而不是在 401 后才刷新,以减少写工具半途中断。
Confused deputy 与静态 client_id 代理。 规范要求:使用静态 client_id 的 MCP 代理在把请求转发到第三方 AS 之前,必须对每个动态注册的客户端取得用户同意。自建「MCP 聚合网关」时需单独设计同意 UI 与审计,不能假设 OAuth 层已隐含授权。
stdio 与 HTTP 凭据混用。 同一工具实现若同时提供 stdio 与 HTTP,凭据来源不同;文档与配置必须隔离,避免把 Bearer token 写入子进程环境变量供无关进程读取。
参考来源
- Model Context Protocol — Authorization(2025-06-18) — 角色、发现序列、PKCE、resource 参数、错误码与安全考虑
- MCP 规范源码:
authorization.mdx— MUST/SHOULD 原文 - RFC 9728 — OAuth 2.0 Protected Resource Metadata —
authorization_servers、WWW-Authenticate与元数据文档 - RFC 8414 — OAuth 2.0 Authorization Server Metadata — 授权 / 令牌 / 注册端点发现
- RFC 8707 — Resource Indicators for OAuth 2.0 —
resource参数与 audience 语义 - RFC 7591 — OAuth 2.0 Dynamic Client Registration — 动态
client_id注册 - OAuth 2.1 IETF Draft(draft-ietf-oauth-v2-1-13) — Bearer 请求、令牌校验、PKCE 与通信安全
- Model Context Protocol — Security Best Practices(2025-06-18) — token passthrough、confused deputy 补充说明
- Model Context Protocol — Transports(2025-06-18) — HTTP 传输与鉴权请求的配合上下文
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。