前面三篇里,两个 MCP Server 在传输上做了截然不同的选择:Python 能力适配器 用 SSE,Node.js 工具平台 用 Streamable-HTTP。
同一时期、同一个团队,一个用了已被官方标记为 legacy 的 SSE,一个用了官方当前推荐的 Streamable HTTP。这篇就把传输层单独拎出来讲透:不同协议版本到底差在哪、各自什么时候该选、以及已经在 SSE 上的服务怎么迁过去。
版本说明(2026-08):MCP 2026-07-28 已经发布,并移除了 Streamable HTTP 的 GET 端点和协议级 Session。下文把 2025 及更早版本称为“兼容模式”,把 2026-07-28 称为“当前协议”。旧 SDK 的 POST/GET/DELETE 与
Mcp-Session-Id实现仍可用于兼容旧客户端,但不代表当前协议。
两个传输是什么
先说本质。
SSE(Server-Sent Events):一条长连接,服务端单向往客户端推消息。是 MCP 早期的传输标准,现在被官方标记为 legacy。
Streamable HTTP:基于标准 HTTP 的请求-响应传输,响应仍可按协议和 SDK 能力流式返回。它取代了旧式 HTTP+SSE 双端点。
两者放一起对比:
| 维度 | 旧式 HTTP+SSE | Streamable HTTP(2025 兼容模式) | Streamable HTTP(2026-07-28) |
|---|---|---|---|
| HTTP 端点 | GET 建立 SSE,POST 发送消息 | 同一路由处理 POST/GET/DELETE | 只使用 POST |
| 协议会话 | 依赖长连接 | 可用 Mcp-Session-Id 管理会话 | 移除协议级 Session |
| 流式返回 | 独立 SSE 长连接 | GET 流或 POST 响应流 | POST 响应范围内按需流式返回 |
| 水平扩展 | 需要连接亲和与事件恢复设计 | 常需 sticky session 或共享会话存储 | 协议层天然无状态;业务状态仍需显式外置 |
| 协议地位 | legacy,仅兼容存量客户端 | 兼容旧客户端/旧 SDK | 当前协议 |
这张表是后面所有讨论的基础。
为什么能力适配器当年选 SSE
不是判断失误,是时机和场景。
能力适配器启动得早。那个时候,SSE 还是 MCP 主流推荐的传输方式,Streamable-HTTP 还没被定为标准。对一个要支持”工具执行进度实时推送”的服务,SSE 的长连接模型天然合适——不用额外搭通道,进度顺着同一条连接就推过去了。
再加上它部署在内网,调用方都是受控的内部 Agent,代理链路可控,SSE 长连接的那些兼容性问题(CDN 截断、超时断连)基本碰不到。
所以在那个时间点、那个场景下,SSE 是合理选择。
为什么工具平台直接上 Streamable-HTTP
工具平台是新服务,启动的时候,官方已经明确推荐 Streamable-HTTP 了。它选 Streamable-HTTP 的理由,每一条都和它的场景对得上:
- 对外公网部署:代理兼容性是硬指标,标准 HTTP 的 Streamable-HTTP 完胜长连接的 SSE;
- Next.js 原生支持:
WebStandardStreamableHTTPServerTransport基于 Web 标准 Request/Response,和 Route Handler 无缝; - 路由更集中:项目当时使用的 2025 兼容模式让 POST / GET / DELETE 共用一个路由,比旧式 SSE 的“双端点”干净;
- 升级路径明确:切到 2026-07-28 后只保留 POST,协议层不再维护 Session,更适合水平扩展。
一句话:新服务没有历史包袱,直接上当前推荐标准。
SSE 的 legacy 现状
官方对 SSE 的态度很明确,原文大致是:
SSE transport is maintained for backward compatibility. Use Streamable HTTP for new deployments.
翻译过来就是:SSE 还会维护,是为了向后兼容;但新部署应该用 Streamable-HTTP。
这等于官方给 SSE 判了”存量维持、不再扩展”。已经在跑的 SSE 服务不用慌(短期内不会废弃),但新增的部署、新增的能力,都不该再建在 SSE 上。
SSE 的工程坑:老服务的真实账单
选 SSE 不是没代价,它的工程成本主要在反向代理那一层。SSE 走长连接,Nginx 默认配置会把它”整坏”——缓冲、缓存、短超时,任何一项都会让推送中断或延迟。所以必须显式配置:
location /mcp {
proxy_pass http://mcp-server:8000;
proxy_buffering off; # 关缓冲,否则推送被攒着不发出
proxy_cache off; # 关缓存
proxy_read_timeout 300s; # 长连接超时拉长
proxy_send_timeout 300s;
proxy_http_version 1.1;
proxy_set_header Connection '';
}
这条配置背后是 SSE 的几个固有麻烦:
- 长连接资源:每个客户端占一条 TCP 连接,并发上去之后连接数会成问题;
- 断线恢复:连接断了,中间错过的消息怎么办?如果业务要求可靠补发,就要自行引入持久事件记录、幂等标识和重放机制;旧式 SSE 传输本身不会替你保证业务消息不丢;
- 代理/CDN 限制:不少 CDN 对长连接有超时或直接截断,公网部署尤其头疼。
这些坑,能力适配器因为内网 + 受控调用方,大部分没踩到;但只要哪天它要出公网、或者并发上来,这些账就得还。
迁移路径:SSE → Streamable-HTTP
已经在 SSE 上的服务,不用一刀切,推荐分三步走:
第一步:保持 SSE,评估收益。 内网、稳定、并发不高的服务,迁移收益可能不大,可以暂缓。但别再在 SSE 上新增功能。
第二步:双协议并存。 让服务同时暴露 SSE 和 Streamable-HTTP 两种传输。新客户端走 Streamable-HTTP,老客户端继续走 SSE,逐步把流量切过去。这个阶段两种传输共存,是迁移的安全网。
第三步:全面切到 Streamable HTTP + 无状态化。 流量切完后下掉旧式 SSE。若仍需兼容 2025 客户端,可暂时保留 SDK 的会话模式与 EventStore;采用 2026-07-28 时,应使用 Tasks 扩展或业务任务 ID 显式承载长任务状态,而不是继续依赖协议级 Session。
Node.js 工具平台应把 2026-07-28 作为升级目标:移除进程内协议会话,并把确实需要跨请求保存的业务状态放进显式任务记录或共享存储。
决策建议
收成几条可直接用的:
- 新项目:直接 Streamable-HTTP,别再从 SSE 起步。
- 存量 SSE(内网、稳定):评估迁移收益,收益不大可暂缓,但停止在 SSE 上新增能力。
- 存量 SSE(要出公网 / 要扩容):尽快走双协议 → Streamable-HTTP 的迁移。
- 要水平扩展:优先采用 2026-07-28 无状态协议;只有业务本身需要跨请求状态时,才引入任务存储或其他共享状态组件。
动手验证
FastMCP 切传输,就是一行参数的事:
# 当前:SSE
mcp.run(transport="sse", host="0.0.0.0", port=8000)
# 迁移目标:Streamable-HTTP(FastMCP 里 transport="http")
mcp.run(transport="http", host="0.0.0.0", port=8000)
在仍使用 2025 兼容协议的 FastMCP 版本中,可以通过无状态选项减少会话依赖:
mcp = FastMCP("Server", stateless_http=True)
这个参数的行为受 FastMCP 与底层 MCP SDK 版本影响。上线前要固定依赖版本,并用 2025 与 2026 客户端分别验证协议协商、HTTP 方法和长任务行为。
Nginx 的 SSE 配置上面已经贴了。Node.js 那一侧 Streamable-HTTP 的完整实现(单端点、会话管理),可以看 agent-runtime-lab。
传输层不是”选一个”,是”认清你在哪一阶段”
回到开头那个反差:一个 SSE、一个 Streamable-HTTP,看起来矛盾,其实是两个服务处在不同阶段、不同场景下的合理选择。
传输层选型的核心,不是争论 SSE 和 Streamable-HTTP 谁好,而是认清:
- 你的服务是新是旧(有没有历史包袱);
- 部署在内网还是公网(代理兼容性是不是硬指标);
- 要不要水平扩展(无状态是不是刚需)。
认清楚这三点,传输选哪个、要不要迁、什么时候迁,自然就有答案。