MCPServer serves subscriptions/listen and advertises listChanged=true unconditionally — hold-open streams pin serverless invocations to the

该报错通常出现在用 MCPServer 以 stateless_http=True 部署到 serverless 平台(如 AWS Lambda Function URL 流式响应)时:默认注册的 subscriptions/listen 处理器会让服务端无条件广告 listChanged=true

快速结论:该报错通常出现在用 MCPServer 以 stateless_http=True 部署到 serverless 平台(如 AWS Lambda Function URL 流式响应)时:默认注册的 subscriptions/listen 处理器会让服务端无条件广告 listChanged=true/subscribe=true,客户端随即打开一条保持开启的流,在客户端断开无法传到 ASGI 应用的平台上,这次调用会被一直挂到平台超时。优先排查是否能关闭 subscriptions 能力。

适用环境:mcp 2.1.1(本地复现)与 2.2.0(生产);MCPServer(...) + mcp.streamable_http_app(streamable_http_path="/", stateless_http=True, transport_security=...),未使用 json_response;uvicorn 0.52.4(uvloop + httptools)、sse-starlette 3.4.8、Python 3.12;AWS Lambda(容器镜像、Function URL RESPONSE_STREAM、aws-lambda-web-adapter 0.9.1);客户端为 TypeScript SDK(连接时自动打开 listen)与一个托管连接器。

最快修复方案:升级到包含 #3357 修复并新增 MCPServer(..., subscriptions=False)(#3626)的版本,显式传入该参数关闭 subscriptions。Issue 中维护者确认:关闭后 server/discover 里的标志变为 false,subscriptions/listen 的 POST 会直接返回 404 / -32601。注意默认值未变,必须手动传入。

注意事项:subscriptions=False 自 PR #3626 引入,需确认所用 mcp 版本已包含该改动,旧版本可能不存在此参数;Issue 中的“私有属性 workaround”并非受支持方式,仅作背景说明。若该方案无法覆盖你的 Lambda 部署场景,Issue 建议另开新 issue。

问题场景

用户使用 MCP Python SDK 的 MCPServer 构建服务,通过 mcp.streamable_http_app(..., stateless_http=True) 以无状态 HTTP 方式部署在 serverless 平台上(案例为 AWS Lambda 容器镜像 + Function URL RESPONSE_STREAM + aws-lambda-web-adapter)。即使服务端从不发布任何变更通知,2026-07-28 协议版本的客户端也会在连接时自动打开 subscriptions/listen,产生一条保持开启的响应流。由于 Lambda 这类平台上客户端断连不会传递到 ASGI 应用,每次打开流都会占用一次调用直到平台超时。该问题与 #3492(旧版 GET 流)是同一失效模式,只是出现在新版协议链路上。

报错原文

server/discover completed
   {"jsonrpc":"2.0","id":"d1","result":{"cacheScope":"private","capabilities":{"prompts":{"listChanged":true},"resources":{"listChanged":true,"subscribe":true},"tools":{"listChanged":true}}, ...

subscriptions/listen STILL OPEN after 3 s

原因分析

最可能的原因是:MCPServer 总是注册 subscriptions/listen 处理器(on_subscriptions_listen=ListenHandler(self._subscriptions)),而 Server.get_capabilities() 仅根据该处理器是否存在,就在 2026-07-28 协议线路上推导出 tools/prompts/resources.listChanged = true 与 resources.subscribe = true。因此一个从不发布变更通知的服务端也会对外广告订阅能力,新版客户端据此打开 subscriptions/listen 并按规范保持流开启直到客户端关闭。在客户端断连无法传播到应用的 serverless 平台上,这条流会把一次调用钉到平台超时。Issue 报告时没有任何公开方式可以退出该行为。

环境排查

  • 确认 mcp 版本:Issue 中 2.1.1(本地)与 2.2.0(生产)均复现同样行为;subscriptions=False 需版本包含 PR #3626。
  • 确认协议版本为 2026-07-28(mcp-protocol-version: 2026-07-28)。
  • 确认应用构建方式:MCPServer(...) + streamable_http_app(streamable_http_path="/", stateless_http=True, transport_security=...),未启用 json_response。
  • 确认运行时依赖:uvicorn 0.52.4(uvloop + httptools)、sse-starlette 3.4.8、Python 3.12。
  • 确认部署平台是否会出现“客户端断连不到达 ASGI 应用”的情况,案例为 AWS Lambda 容器镜像 + Function URL RESPONSE_STREAM + aws-lambda-web-adapter 0.9.1。
  • 确认客户端类型:TypeScript SDK(连接时自动 open listen)或托管连接器。

解决步骤

  1. 先确认当前 mcp 版本是否已包含 #3357 的修复以及 #3626 新增的 subscriptions 开关;若版本过旧,先按官方发布说明升级到包含该参数的版本。
  2. 在构造 MCPServer 时显式传入 subscriptions=False,例如 MCPServer("demo", subscriptions=False),以停止广告并停止服务订阅能力。
  3. 启动后发送一次 server/discover,确认返回的 capabilities 中不再出现 listChanged: true / subscribe: true。
  4. 再向 subscriptions/listen 发送 POST,确认即时返回 404 / 错误码 -32601,而不是保持流开启。
  5. 重新在生产(Lambda)环境验证一次调用是否还会被挂到平台超时;若仍复现且 subscriptions=False 不覆盖你的部署形态,按 Issue 建议另开新 issue 并附上复现数据。

验证方法

在 server/discover 的响应中检查 capabilities:若 prompts/resources/tools 的 listChanged 与 resources.subscribe 均为 false(不再出现 listChanged: true / subscribe: true),并且对 subscriptions/listen 的请求立刻得到 404 / -32601 而非 3 秒后仍显示 STILL OPEN,说明问题已解决。可在 serverless 环境观察是否仍有调用被挂到平台超时。

参考来源

modelcontextprotocol/python-sdk #3493

相关:modelcontextprotocol/python-sdk #3357(重复项,修复与讨论集中于此)、modelcontextprotocol/python-sdk #3492(旧版 GET 流的同类问题)、modelcontextprotocol/python-sdk #3626(新增 subscriptions=False)

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 27024

发表回复

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