Cleanly terminated streamable-HTTP sessions are never deregistered: the DELETE path skips its own cleanup

该报错发生在 MCP Python SDK 的 streamable-HTTP 握手路径中:客户端通过 DELETE 干净终止会话后,会话并未从 StreamableHTTPSessionManager._server_instances 注销,导致每终止一个会话就泄漏几 KB 内存。优先检查 St

快速结论:该报错发生在 MCP Python SDK 的 streamable-HTTP 握手路径中:客户端通过 DELETE 干净终止会话后,会话并未从 StreamableHTTPSessionManager._server_instances 注销,导致每终止一个会话就泄漏几 KB 内存。优先检查 StreamableHTTPServerTransport.terminate()self._terminated = Truerun_serverfinally 块中 and not http_transport.is_terminated 判断条件之间的交互。

适用环境:MCP Python SDK v1.29.0 及更早版本(含手写握手协议路径);仅影响流式 HTTP 传输(StreamableHTTPServerTransport);不适用于 stateless_http=True 模式;2.x 客户端在 mode="auto" 下使用 2026-07-28 协议版本时不受影响(但 1.29.0 无 handle_modern_request,所有客户端都会落到受影响路径)。

最快修复方案:暂无确认的一步修复方案。社区已提交 PR #3302,通过在干净 DELETE 后同步从两个注册表中移除终止的会话来修复此问题,但尚未合并到主分支。

注意事项:该问题只影响 2024-11-052025-11-25 协议版本的手写握手路径;配置 session_idle_timeout 不是有效缓解手段(会话被显式 DELETE 时不会触发空闲回收),且 FastMCP、streamable_http_app()MCPServer 均未暴露该参数。

问题场景

在运行 MCP Python SDK 的 FastMCP 服务器(真实 uvicorn 套接字)时,客户端依次建立 200 个会话:每个会话都完成完整的 initialize 握手(返回 200),发送 notifications/initialized,然后显式发送 DELETE 请求。实测发现每个被 DELETE 终止的会话在进程生命周期内都不会被注销,每会话泄漏约 5201 字节内存。

报错原文

Cleanly terminated streamable-HTTP sessions are never deregistered: the DELETE path skips its own cleanup

原因分析

经代码确认(提交 57394b0),问题根源在 streamable_http_manager.py_server_instances 的移除逻辑:

  • 空闲超时分支:在调用 terminate() 之前先从映射表中 pop 掉会话,因此能正确清理。
  • finally 清理分支:被 and not http_transport.is_terminated 条件保护。当客户端干净地发送 DELETE 时,terminate() 先将 self._terminated = True,随后 serve_loop 正常返回(无空闲超时、无异常),此时 finally 的保护条件为假,del 被跳过。

这意味着礼貌终止(显式 DELETE)比粗暴断开(客户端消失)更糟糕:前者永远不会被回收,后者反而会被空闲超时机制回收。根因是 terminate() 在关闭让会话任务展开的流 之前就设置了 _terminated 标志,导致 finally 块误判为”已终止”而跳过清理。

环境排查

  • MCP Python SDK 版本:1.29.0 或更早(含 handle_modern_request 缺失的版本)
  • 协议版本:2024-11-052025-11-25 之间的手写握手路径
  • 客户端模式:mode="legacy"mode="auto"(但在 1.29.0 上所有模式都会落到受影响路径)
  • Python 版本:Issue 中未指定,需自行确认
  • 部署方式:FastMCP 服务器(无法配置 session_idle_timeout)或直接使用 StreamableHTTPServerTransport
  • 无需 CUDA/显卡相关配置

解决步骤

  1. 确认受影响范围:检查服务端日志中对每个会话的 DELETE 路径是否触发 terminate(),以及 _server_instances 是否会持续增长。
  2. 监控内存泄漏:在真实部署中运行 200 个顺序会话(每个完整握手后显式 DELETE),对比进程 RSS 前后变化,确认每会话泄漏约 5 KB。
  3. 检查上游修复:关注 PR #3302 是否合并到主分支;该 PR 在干净 DELETE 后从 _server_instances_session_owners 两个注册表中同步移除终止的会话,并保留现有 404 状态行为。
  4. 临时缓解手段:此问题的唯一缓解方式是重启进程,因为现有版本中空闲超时机制(session_idle_timeout)对显式 DELETE 的会话无效,且 FastMCP 无法暴露该参数。
  5. 验证修复是否有效:如果使用 PR #3302 的分支,运行回归测试确认会话 ID 在干净 DELETE 后从 _server_instances 中消失。

验证方法

在修复后的版本上运行回归测试:启动一个会话,发起干净的 DELETE,断言会话 ID 已从 _server_instances_session_owners 中移除。对于生产验证,可对比修复前后同样运行 200 个顺序 DELETE 会话后的进程 RSS:修复前每会话泄漏约 5201 字节,修复后应无持续增长。

参考来源

modelcontextprotocol/python-sdk #3300

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 21235

发表回复

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