快速结论:当 Client(mode="auto") 访问一个走交互式 OAuth 的现代 MCP 服务器时,如果用户在浏览器里完成授权超过 10 秒,server/discover 探测会本地超时并让客户端错误地回退到 legacy 的 initialize 握手;优先排查 OAuth 授权耗时是否超过固定 discovery 超时。
适用环境:已确认使用 mcp==2.1.1(讨论中提到 v2.2.0 源码仍存在该常量)、Python 3.12、Streamable HTTP transport、Client(mode="auto") 及 OAuthClientProvider;Issue 未提供操作系统、CUDA、显卡等更多信息。
最快修复方案:暂无确认的一步修复方案。该 Issue 已关闭,但讨论中提到的可配置 discover_timeout_seconds、区分本地超时来源、或不计入交互式 OAuth 等待时间,均属于建议方向,未在 Issue 中确认合入。
注意事项:不要仅把 10 秒常量改大当作通用修复,因为问题还涉及“本地超时被当作远端协议时代证据”的语义混淆;另外,凭据一旦缓存,同一 upstream 会正常协商到 2026-07-28,所以问题只在冷启动的交互式 OAuth 流程中复现,容易漏测。
问题场景
在使用 MCP Python SDK 的 Client(mode="auto") 连接现代 MCP 服务器时,如果第一次请求触发交互式 OAuth 授权码流程(例如用户需要在浏览器中登录并授权),就会触发该问题。流程大致为:客户端先发送现代的 server/discover 请求,服务器返回 401 并启动 OAuth;等待用户回调的时间被计入 server/discover 的固定超时预算;一旦超过 10 秒,mode="auto" 就回退到 legacy initialize 握手,即使随后 OAuth 完成、认证后的 server/discover 重试返回了合法的现代 DiscoverResult,客户端也已经选定了 legacy 协议。
报错原文
Client(mode="auto") falls back to legacy when interactive OAuth exceeds the fixed `server/discover` timeout
DISCOVER_TIMEOUT_SECONDS = 10.0
原因分析
最可能的原因是 ClientSession.send_discover() 使用了固定的内部超时 DISCOVER_TIMEOUT_SECONDS = 10.0,而该超时预算覆盖了交互式 OAuth 等待用户完成浏览器授权的时间。讨论中进一步指出,调用链为 Client(mode="auto") → negotiate_auto() → send_discover() → CallOptions(timeout=10.0) → JSONRPCDispatcher.send_raw_request() → Streamable HTTP POST task → 401 / interactive OAuth → redirect_handler() / callback_handler() → authenticated retry。当本地 10 秒预算耗尽时,JSONRPCDispatcher 会将其转为 MCPError(REQUEST_TIMEOUT);随后 negotiate_auto() 几乎把任何 MCPError 都当作回退到 initialize() 的充分理由。也就是说,本地生成的超时被误用为关于远端服务器协议时代的证据。讨论中提出三种可能的修复层面:将 discovery 超时做成可配置项、保留超时来源以区分本地 deadline 与对端 REQUEST_TIMEOUT、或者不把交互式授权等待时间计入 discovery 服务时间预算。
环境排查
- 确认 MCP Python SDK 版本:Issue 中为
mcp==2.1.1,讨论提到v2.2.0源码仍定义DISCOVER_TIMEOUT_SECONDS = 10.0。 - 确认 Python 版本:Issue 中为 Python 3.12。
- 确认传输方式:Issue 中使用 Streamable HTTP transport。
- 确认客户端模式:
Client(mode="auto")。 - 确认认证方式:
OAuthClientProvider,且为冷启动、无缓存凭据的交互式 OAuth 流程。 - 确认首次请求是否触发
401并进入浏览器授权,以及用户完成授权是否可能超过 10 秒。 - 本地超时后是否出现回退到 legacy
initialize的日志或协议协商结果。 - 凭据缓存后重试同一 upstream 时,是否能正常协商到
2026-07-28。
解决步骤
- 先复现并确认触发条件:在无缓存凭据的情况下,用
Client(mode="auto")连接一个需要交互式 OAuth 的现代 MCP 服务器,让浏览器授权耗时超过 10 秒,观察是否回退到 legacyinitialize。 - 检查当前 SDK 源码中
ClientSession.send_discover()是否仍然直接使用固定的DISCOVER_TIMEOUT_SECONDS = 10.0,以及CallOptions(timeout=10.0)是否进入该路径。 - 对照讨论中的调用链,确认超时是由本地
JSONRPCDispatcher生成,而不是由对端服务器返回的REQUEST_TIMEOUT;区分这两者对判断回退逻辑是否合理很关键。 - 如果只是需要在当前版本绕过,可优先尝试缩短浏览器端授权时间(例如预先登录、减少跳转步骤),或先完成一次授权让凭据缓存,但这只是规避而非修复。
- 如果希望按讨论中的方向修复,可关注或参考“在
Client/ClientSession上配置discover_timeout_seconds并向后兼容”的 PR 方向;在合入前不要假设该配置项已存在。 - 如果自行修改,注意只调大常量可能不足以覆盖语义问题;讨论中建议保留超时来源,避免本地 deadline 被当作 legacy 回退的肯定证据。
验证方法
验证时应在无缓存凭据的冷启动场景下,让交互式 OAuth 授权耗时超过 DISCOVER_TIMEOUT_SECONDS,同时保持 mode="auto",确认客户端最终仍完成现代握手,而不是回退到 legacy initialize。讨论中提到可添加授权时间超过 DISCOVER_TIMEOUT_SECONDS 且 mode=auto 仍完成现代握手的测试。如果只是缓存凭据后重试,则不能证明问题已修复,因为凭据缓存场景本来就会正常协商到 2026-07-28。
参考来源
modelcontextprotocol/python-sdk #3601
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[BUG]: k8s manifest liveness/readiness probes hit the collector (8888), not the server](https://www.chat-gpts.plus/wp-content/uploads/2026/10/6579-e1af9770-768x403.jpg)

