[v2] Allow MCPServer to disable subscriptions/listen

报错 “v2 Allow MCPServer to disable subscriptions/listen” 通常出现在基于 2.x 的 MCP Python SDK 搭建无状态 Streamable HTTP 服务(例如 AWS Lambda)时: MCPServer 总是注册 subscrip

快速结论:报错 “v2 Allow MCPServer to disable subscriptions/listen” 通常出现在基于 2.x 的 MCP Python SDK 搭建无状态 Streamable HTTP 服务(例如 AWS Lambda)时:MCPServer 总是注册 subscriptions/listen 处理器,导致 server/discover 错误地宣告订阅能力,客户端随即发起长期 SSE 流并占用请求。优先排查是否使用了 2.0.0~2.2.0 且未显式关闭订阅。

适用环境:Issue 中已确认的环境为:Python 3.11、mcp 2.0.0(后续评论在 mcp 2.1.1 与 2.2.0 上复现)、AWS Lambda、Streamable HTTP、json_response=True、stateless_http=True;客户端为 Claude Code 2.1.235(及 2.1.2xx)。

最快修复方案:升级到包含 #3626 的版本,并使用公开参数 MCPServer(..., subscriptions=False)(该写法在 #3626 中落地并已在 Issue 中确认)。在此之前,Issue 中曾使用的临时办法是移除私有处理器,但这依赖私有内部结构。

注意事项:subscriptions=False 会保持 subscriptions/listen 未注册,server/discover 的 listChanged 与 subscribe 标志会变为 false,杂散的 listen 调用会返回 Method not found;默认行为不变,不影响现有兼容性。Issue 中提到的 ServerCapabilities 风格参数并不在该修复范围内,相关能力工作见 #2896,因此不要把它当作本次修复的一部分。

问题场景

用户在使用 modelcontextprotocol/python-sdk 2.x 的 MCPServer 构建 MCP 服务器时触发该问题。典型场景是:服务器拥有静态目录(static catalog),从不发布任何变更通知,却以无状态的 Streamable HTTP 方式运行在 AWS Lambda 上。由于 MCPServer 始终安装 on_subscriptions_listen=ListenHandler(self._subscriptions),构造函数虽然暴露了 subscriptions: SubscriptionBus | None = None,但传入 None 只是创建一个 InMemorySubscriptionBus,并不会禁用订阅。客户端在 discovery 阶段自动打开四个 subscriptions/listen 流,每个请求都走长期 SSE 路径并一直占用 Lambda 调用直到超时。

报错原文

[v2] Allow MCPServer to disable subscriptions/listen

`MCPServer` always installs a `subscriptions/listen` handler:
on_subscriptions_listen=ListenHandler(self._subscriptions)

This causes `server/discover` to advertise:
{
  "tools": {"listChanged": true},
  "prompts": {"listChanged": true},
  "resources": {"listChanged": true, "subscribe": true}
}

An unsolicited subscriptions/listen call should return Method not found.

原因分析

可能原因是:在 2.x 的 MCPServer 中,订阅/监听处理器是默认注册的,且没有对等的公开开关来关闭它。这会产生两个连锁结果:其一,server/discover 的能力声明把 listChanged 和 subscribe 报为 true,让客户端误以为该服务器支持变更通知与订阅;其二,客户端据此持续发起长期 subscriptions/listen 请求,在无状态 HTTP + Lambda 场景下这些请求不会正常结束,从而长时间占用执行并产生超时与计费。Issue 中多位用户独立复现了相同行为,并指出底层 Server 本身可以通过 on_subscriptions_listen=None 正确禁用,而高层 MCPServer 缺少等价入口;临时移除 _lowlevel_server._request_handlers 中的处理器虽可生效,但依赖私有内部结构,不是长期方案。

环境排查

  • 确认 Python 版本(Issue 中为 3.11)。
  • 确认 mcp Python SDK 版本,是否处于受影响区间(2.0.0,以及评论中验证的 2.1.1、2.2.0);确认是否已升级到包含 #3626 的版本。
  • 确认部署形态:是否为无状态 Streamable HTTP、AWS Lambda、json_response=True、stateless_http=True。
  • 确认客户端工具与版本(Issue 中为 Claude Code 2.1.235 及 2.1.2xx)。
  • 检查 server/discover 返回的能力声明中 listChanged / subscribe 是否为 true。
  • 检查是否仍在代码中手动移除 _lowlevel_server._request_handlers,或使用 ASGI 中间件拒绝请求作为临时缓解。

解决步骤

  1. 升级 mcp 到包含 #3626 的版本,获得公开的关闭开关。
  2. 在创建服务器时显式传入 subscriptions=False,例如 MCPServer("static-server", subscriptions=False)。该用法在 Issue 的收尾评论中确认已落地,并已在文档 Turning it off 中给出示例。
  3. 移除原有的临时规避手段,包括静默修改 _lowlevel_server._request_handlers,以及仅在 ASGI 中间件中拒绝请求的做法。
  4. 如果仍需要使用私有处理器移除等旧办法,请把它视为临时过渡(可优先尝试),不要作为长期方案。

验证方法

重新发布服务后,检查 server/discover 的能力声明:subscriptions/listen 应不再被注册,listChanged 与 subscribe 相关标志应变为 false。向服务发送一个非预期的 subscriptions/listen 调用,应返回 Method not found(客户端侧表现为 404 和 -32601,并作为软错误继续工作)。同时观察 Lambda 不再出现因长期 SSE 流导致的频繁超时调用。Issue 中的验证还提到:在 mcp 2.1.1 与 2.2.0 的本地 Streamable HTTP 测试中,移除私有处理器后 get_capabilities() 会翻转为 listChanged: false / subscribe: false,listen 调用立即返回 -32601。

参考来源

modelcontextprotocol/python-sdk #3357

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 27153

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注