[v2] Expose the SSE max_event_size setting in Streamable HTTP clients

这是 MCP Python SDK v2 的 Streamable HTTP 客户端在解析单条 SSE 事件时的默认 1 MiB 大小上限导致的:只要服务端把大于 1 MiB 的 tools/call 或 tools/list 结果作为单个 data: 事件返回,HTTPX2 就抛 SSEError

快速结论:这是 MCP Python SDK v2 的 Streamable HTTP 客户端在解析单条 SSE 事件时的默认 1 MiB 大小上限导致的:只要服务端把大于 1 MiB 的 tools/call 或 tools/list 结果作为单个 data: 事件返回,HTTPX2 就抛 SSEError,SDK 却把它转成误导性的 [v2] Expose the SSE max_event_size setting in Streamable HTTP clients 语境下的网络类错误。优先确认是不是大响应被塞进了单个 SSE 事件。

适用环境:Python 3.11;MCP Python SDK 2.0.0(评论中实测 mcp 2.1.1);HTTPX2 2.10.0(评论中实测 httpx2 2.12.0);FastMCP 4.0.0 / 4.0.1(fastmcp-slim);对比环境为 mcp 1.x + httpx 0.28 + httpx-sse。Issue 未提供操作系统、CUDA、显卡信息。

最快修复方案:Issue 未给出直接可用的客户端侧 one-liner;当前确认有效的做法是给 SDK 的 Streamable HTTP 通道暴露并传入 max_event_size(POST 响应、GET 流、重连流都要一致生效),或让上游服务端不要把超大结果压成单条 SSE 事件。若要临时绕开,可优先尝试退回 mcp 1.x(评论中该场景可正常列出 1030 个工具)。

注意事项:max_event_size 的公开 API 形态在 Issue 中仍“open for maintainer direction”,streamable_http_client() 与 StreamableHTTPTransport 均未暴露该参数,FastMCP 也无法透传。真实 SSEError("Server-sent event exceeded the 1048576 byte limit.") 只在 DEBUG 级别可见,默认日志下只会看到误导性的 SSE stream ended without a response。超限时应返回请求级错误、不重放已发送的 POST、同会话的其他请求仍可用。

问题场景

使用 MCP Python SDK v2 的 Streamable HTTP 客户端调用远端 MCP 服务(例如通过 FastMCP 代理托管服务)时,服务端以 text/event-stream 返回单个 data: 事件,且该事件超过 1 MiB。典型触发点:await client.call_tool("large_result", {}) 返回 2 MiB 单事件文本;评论中的真实场景是 Composio 托管服务暴露 1030 个工具,tools/list 用 HTTP 200 + 单个 2,140,787 字节的 data: 事件返回,客户端调用直接失败,代理层报告该上游 0 个工具。

报错原文

SSE stream ended without a response

MCPError: SSE stream ended without a response

SSEError("Server-sent event exceeded the 1048576 byte limit.")

原因分析

MCP Python SDK v2.0.0 在解析 Streamable HTTP POST 响应时直接构造 httpx2.EventSource(response),未传入 max_event_size,因此落到 HTTPX2 自 2.10 起的默认上限 httpx2._config.DEFAULT_MAX_EVENT_SIZE_BYTES = 1024 * 1024。当有效结果大于 1 MiB 并以单个 SSE 事件返回时,HTTPX2 解码器抛出 SSEError;SDK 在 _handle_sse_response(src/mcp/client/streamable_http.py:411-460)中用 except Exception 捕获并以 DEBUG 记录 "SSE stream ended",随后通过 _resolve_abandoned_request(..., "SSE stream ended without a response") 结束请求,调用方拿到的是指向网络问题的泛化 CONNECTION_CLOSED,而真实原因是客户端侧大小上限。此外传输层在多处创建 SSE reader:POST 路径直接构造 EventSource(response)(streamable_http.py:426),GET 与重连路径调用 AsyncClient.sse(),均无传输级事件大小设置。

环境排查

  • 确认 MCP Python SDK 版本:Issue 为 2.0.0,评论实测为 2.1.1。
  • 确认 HTTPX2 版本:Issue 为 2.10.0,评论实测为 2.12.0(1 MiB 默认自 2.10 引入)。
  • 确认 Python 版本为 3.11。
  • 确认 FastMCP 版本(4.0.0 / 4.0.1,fastmcp-slim)以及是否能透传 Streamable HTTP 传输参数。
  • 对比 mcp 1.x(httpx 0.28 + httpx-sse)下同一请求是否成功。
  • 打开 DEBUG 日志,确认是否能看到 SSEError("Server-sent event exceeded the 1048576 byte limit.")。
  • 用原始请求回放确认服务端返回的 data: 事件字节数(例如 2,140,787 字节)。

解决步骤

  1. 先用 DEBUG 级别日志复现,确认隐藏在泛化错误下的是 SSEError("Server-sent event exceeded the 1048576 byte limit."),排除真实网络中断。
  2. 抓取或回放原始请求,测量服务端单条 SSE 事件的实际字节数;若超过 1 MiB,即命中该上限。
  3. 按 Issue 的期望方案,为 Streamable HTTP 客户端/传输暴露 SSE 事件大小设置(建议命名为 max_event_size,与 HTTPX2 术语一致),并一致地应用到 POST SSE 响应、GET 流与重连流三处。
  4. 在实现中构造 EventSource(response, max_event_size=...),替换 streamable_http.py:426 处的无参构造(可优先尝试)。
  5. 扩展 streamable_http_client() 与 StreamableHTTPTransport 的签名以接受该参数,并检查 FastMCP 侧透传能力。
  6. 超限时返回请求级清晰错误:不重放已发送的 POST,且同会话其他请求保持可用。
  7. 若暂无法改动 SDK,可优先尝试退回 mcp 1.x,或推动上游服务端把大结果拆分为多个 SSE 事件、避免单事件超限。

验证方法

用 Issue 中的复现方式:起一个 Starlette 服务,对每个 JSON-RPC 请求返回 text/event-stream,其中只有一条 data: 事件(无状态、无 mcp-session-id),运行 uv run --no-project --with "mcp==2.1.1" --with uvicorn --with starlette python repro_sse_event_limit.py。逐步增大 total_kib,观察超过 1 MiB 后 session.list_tools() 是否仍成功;配置了足够大的 max_event_size 后应返回 OK, N tools,而在原版 2.1.1 下则失败。同时确认失败或超限时不再影响同会话的其他请求。

参考来源

modelcontextprotocol/python-sdk #3332

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 26753

发表回复

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