快速结论:该报错发生在 MCP Python SDK 的 streamable-HTTP 握手路径中:客户端通过 DELETE 干净终止会话后,会话并未从 StreamableHTTPSessionManager._server_instances 注销,导致每终止一个会话就泄漏几 KB 内存。优先检查 StreamableHTTPServerTransport.terminate() 中 self._terminated = True 与 run_server 的 finally 块中 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-05 至 2025-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-05至2025-11-25之间的手写握手路径 - 客户端模式:
mode="legacy"或mode="auto"(但在 1.29.0 上所有模式都会落到受影响路径) - Python 版本:Issue 中未指定,需自行确认
- 部署方式:FastMCP 服务器(无法配置
session_idle_timeout)或直接使用StreamableHTTPServerTransport - 无需 CUDA/显卡相关配置
解决步骤
- 确认受影响范围:检查服务端日志中对每个会话的
DELETE路径是否触发terminate(),以及_server_instances是否会持续增长。 - 监控内存泄漏:在真实部署中运行 200 个顺序会话(每个完整握手后显式 DELETE),对比进程 RSS 前后变化,确认每会话泄漏约 5 KB。
- 检查上游修复:关注 PR #3302 是否合并到主分支;该 PR 在干净 DELETE 后从
_server_instances和_session_owners两个注册表中同步移除终止的会话,并保留现有 404 状态行为。 - 临时缓解手段:此问题的唯一缓解方式是重启进程,因为现有版本中空闲超时机制(
session_idle_timeout)对显式 DELETE 的会话无效,且 FastMCP 无法暴露该参数。 - 验证修复是否有效:如果使用 PR #3302 的分支,运行回归测试确认会话 ID 在干净 DELETE 后从
_server_instances中消失。
验证方法
在修复后的版本上运行回归测试:启动一个会话,发起干净的 DELETE,断言会话 ID 已从 _server_instances 和 _session_owners 中移除。对于生产验证,可对比修复前后同样运行 200 个顺序 DELETE 会话后的进程 RSS:修复前每会话泄漏约 5201 字节,修复后应无持续增长。
参考来源
modelcontextprotocol/python-sdk #3300
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[bug]: InvokeAI v6.14.0-RC1 Crashed while generating Krea-2 Image](https://www.chat-gpts.plus/wp-content/uploads/2026/09/9444-d6bdc60c-768x403.jpg)
![[Question]: Shared embedded chat URL fails to access documents after logout or when accessed by other users](https://www.chat-gpts.plus/wp-content/uploads/2026/09/15895-cf3f7033-768x403.jpg)
