
Server OAuth metadata hardcodes token_endpoint_auth_methods_supported, breaking public client flows
快速结论:该报错发生在 MCP Python SDK 构建的 OAuth 授权服务器上,当公客户端(如 Claude Code、Cursor、VS Code)尝试通过动态客户端注册(DCR)完成 OAuth 2.1 流程时,服务器元数据错误地未声明支持 none 认证方法,导致客户端虽然注册成功但令牌交换失败。优先排查服务器端 build_metadata() 函数返回的 token_endpoint_auth_methods_supported 字段是否包含 "none"。
问题场景
用户基于 MCP Python SDK 构建了支持 OAuth 授权的 MCP 远程服务器,并通过 MCP 客户端(如 Claude Code、Cursor、ChatGPT、VS Code)使用动态客户端注册(DCR)进行公客户端 OAuth 2.1 流程时触发。浏览器 OAuth 流程可以完成,授权码已收到,但令牌交换失败,客户端显示相关错误信息。
报错原文
"Existing OAuth client information is required when exchanging an authorization code"
(Claude Code 端报错;其他客户端可能有类似表述,但根本原因相同。)
原因分析
build_metadata() 函数在 mcp/server/auth/routes.py 第 168 行硬编码了 token_endpoint_auth_methods_supported 为 ["client_secret_post", "client_secret_basic"],遗漏了 "none" 值。这是因为:
- OAuth 2.1 (draft-ietf-oauth-v2-1-13) 和 RFC 7591 明确定义
"none"是公客户端的合法认证方法。 - MCP 授权规范(2025-06-18)要求认证服务器同时支持机密客户端和公客户端,并强烈建议本地客户端作为公客户端实现 OAuth 2.1。
- 服务器端
register.py:54-60的注册处理程序已正确支持token_endpoint_auth_method: "none"的注册请求(不返回client_secret)。 - 但
build_metadata()返回的元数据声明仅支持需要客户端密钥的认证方法,与服务器实际行为矛盾。
可能原因:开发者意图默认支持机密客户端认证,但未包含公客户端必需的 "none" 方法,导致对协议实现的疏忽。
环境排查
- MCP Python SDK 版本:
mcp==1.27.1(已验证仍存在该问题) - FastMCP 版本(如适用):
fastmcp==3.3.1(直接调用build_metadata(),继承相同问题) - 服务器端代码: 检查
src/mcp/server/auth/routes.py中build_metadata()函数的token_endpoint_auth_methods_supported字段 - 客户端: Claude Code、Cursor、ChatGPT、VS Code 等遵循 MCP 授权规范的客户端
解决步骤
- 直接解决方案(等待上游合并 PR #2261): 在服务器端,修改
mcp/server/auth/routes.py中的build_metadata()函数,将token_endpoint_auth_methods_supported字段修改为包含"none",至少补充为["none", "client_secret_post", "client_secret_basic"]。 - 临时解决(ASGI 中间件): 在 ASGI 应用中添加中间件,拦截
/.well-known/oauth-authorization-server的响应,将token_endpoint_auth_methods_supported重写为包含"none":
{"token_endpoint_auth_methods_supported": ["none", "client_secret_post", "client_secret_basic"]} - 参考官方示例: 官方
modelcontextprotocol/example-remote-server使用token_endpoint_auth_methods_supported: ['none'],可将其作为配置依据。
验证方法
修复后,使用公客户端(如 Claude Code)重新执行完整的 OAuth 2.1 动态客户端注册流程:
- 客户端发现服务器元数据时,应能看到
"none"在token_endpoint_auth_methods_supported字段中。 - 客户端使用
token_endpoint_auth_method: "none"注册后,无需client_secret即可完成令牌交换。 - 浏览器 OAuth 流程完成后,授权码应能成功兑换为访问令牌。



