MCP 传输:stdio 对 Streamable HTTP 与 SSE 迁移


文档摘要

MCP 传输:stdio 对 Streamable HTTP 与 SSE 迁移 本节摘要:stdio 只在本地管用。Streamable HTTP(2025-03-26 规范引入)才是远程标准。旧的 HTTP+SSE 传输已弃用,2026 年中正在被移除。选错传输要付一次迁移代价;选对了,你就得到一个可远程托管、带会话连续性与 DNS 重绑定防护的 MCP 服务端。本节把三种传输并排讲透:stdio(本机)、Streamable HTTP(过网)、以及如何从旧 HTTP+SSE 迁移。 学习目标 阅读完本节,你应当能够: 根据部署形态(本地 vs 远程、单进程 vs 机群)在 stdio 与 Streamable HTTP 之间做选择。

MCP 传输:stdio 对 Streamable HTTP 与 SSE 迁移

本节摘要:stdio 只在本地管用。Streamable HTTP(2025-03-26 规范引入)才是远程标准。旧的 HTTP+SSE 传输已弃用,2026 年中正在被移除。选错传输要付一次迁移代价;选对了,你就得到一个可远程托管、带会话连续性与 DNS 重绑定防护的 MCP 服务端。本节把三种传输并排讲透:stdio(本机)、Streamable HTTP(过网)、以及如何从旧 HTTP+SSE 迁移。

学习目标

阅读完本节,你应当能够:

  1. 根据部署形态(本地 vs 远程、单进程 vs 机群)在 stdio 与 Streamable HTTP 之间做选择。
  2. 实现 Streamable HTTP 单端点模式:POST 发请求、GET 建会话流。
  3. 强制 Origin 校验与 session-id 语义,击退 DNS 重绑定。
  4. 在 2026 年中移除截止前,把旧 HTTP+SSE 服务端迁移到 Streamable HTTP。

一、问题与直觉

第一个 MCP 远程传输(2024-11)是 HTTP+SSE:两个端点——一个收客户端 POST,一个 Server-Sent-Events 通道走服务端到客户端的流。它能跑,但笨拙:每会话两个端点、某些 CDN 前面的缓存会坏、以及对长寿命 SSE 连接的硬依赖(某些 WAF 会激进掐断)。

2025-03-26 规范用 Streamable HTTP 取代了它:一个端点,POST 走客户端请求、GET 建会话流,两者共享 Mcp-Session-Id 头。2025-03-26 之后新建或迁移的每个服务端都用 Streamable HTTP。旧 SSE 模式正在弃用——Atlassian Rovo 2026-06-30 移除、Keboola 2026-04-01、其余多数企业服务端在 2026 年底前。

而 stdio 对本地服务端仍然重要。Claude Desktop、VS Code、每个 IDE 形态的客户端都通过 stdio 拉起服务端。正确的心智模型:stdio 给「这台机器」,Streamable HTTP 给「过网络」,无交叉。

二、从零实现

stdio

  • 子进程传输:客户端拉起服务端,经 stdin/stdout 通信。
  • 一行一个 JSON 对象,换行分隔。
  • 无 session id:进程身份即会话。
  • 无需鉴权:子进程继承父进程的信任边界。
  • 绝不用于远程服务端:你得 SSH 或 socat 隧道,到那步不如直接用 Streamable HTTP。

Streamable HTTP

单端点 /mcp(或任意路径),支持三种 HTTP 方法:

  • POST /mcp:客户端发一条 JSON-RPC 消息。服务端要么回单条 JSON 响应,要么回一个 SSE 流(一条或多条响应,适合批响应与该请求相关的通知)。
  • GET /mcp:客户端开一条长寿命 SSE 通道。服务端用它走服务端到客户端的请求(sampling、通知、elicitation)。
  • DELETE /mcp:客户端显式终止会话。

会话由 Mcp-Session-Id 头标识——服务端在首个响应上设,客户端在后续每个请求上回显。session id 必须密码学随机(128+ 位);客户端自选的 id 会被拒绝(安全考虑)。

单端点 vs 双端点

旧规范的双端点模式 2026 年仍可调用——规范称其「legacy compatible」。但所有新服务端都应是单端点。官方 SDK 发出的都是单端点;只在对接未迁移的远程时才用 legacy 模式。

Origin 校验与 DNS 重绑定

浏览器(今天)不是 MCP 客户端,但攻击者能造一个网页,诱使浏览器向 localhost:1234/mcp(用户本地 MCP 服务端监听处)POST。若服务端不查 Origin,浏览器的同源策略救不了你——因为 Origin: http://evil.com 是合法的跨域。

2025-11-25 规范要求服务端拒绝 Origin 不在白名单的请求。白名单通常含 MCP 客户端 host(https://claude.aivscode-webview://*)与本地 UI 的 localhost 变体。

ALLOWED_ORIGINS = {"https://claude.ai", "http://localhost", "vscode-webview://*"} def check_origin(headers): origin = headers.get("Origin", "") if origin and origin not in ALLOWED_ORIGINS: return resp(403, "Origin 不在白名单") # 防 DNS 重绑定 return None

session id 生命周期

  1. 客户端发首个请求,不带 Mcp-Session-Id
  2. 服务端分配随机 id,在响应头设 Mcp-Session-Id
  3. 客户端在所有后续请求与 GET /mcp 流上都回显该头。
  4. 会话可被服务端撤销;客户端在后续请求上看到 404,必须重新 initialize。
  5. 客户端可显式 DELETE 会话以干净关闭。

Keepalive 与重连

SSE 连接会掉。客户端靠带相同 Mcp-Session-Id 重新 GET 来重建。服务端必须把断连期间错过的事件排队(在一个合理窗口内),并通过客户端回显的 last-event-id 头重放。

第 13 节覆盖 Tasks,让长跑工作即使全会话重连也能存活。

向后兼容探针

一个想同时支持新旧服务端的客户端:

  1. POST 到 /mcp
  2. 若响应是 200 OK 带 JSON 或 SSE——这是 Streamable HTTP。
  3. 若响应是 200 OKContent-Type: text/event-stream 有指向次端点的 Location 头——这是 legacy HTTP+SSE,跟随 Location

Cloudflare、ngrok 与托管

2026 年的生产远程 MCP 服务端跑在 Cloudflare Workers(配其 MCP Agents SDK)、Vercel Functions 或容器化的 Node/Python 上。关键:你的托管必须为 SSE GET 支持长寿命 HTTP 连接。Vercel 免费档上限 10 秒,不适用;Cloudflare Workers 支持无限时长流。

网关组合

当你用网关(第 17 节)前置多个 MCP 服务端时,网关是单个 Streamable HTTP 端点,重写 session id 并多路复用上游。工具在网关层合并;客户端看到单个逻辑服务端。

传输失败模式

  • stdio SIGPIPE:子进程在写中途死会抛 SIGPIPE;服务端应干净退出。客户端应检测 EOF 并标记会话死。
  • HTTP 502 / 504:Cloudflare、nginx 等代理在上游失败时发这些。Streamable HTTP 客户端应在短退避后重试一次。
  • SSE 连接掉:TCP RST、代理超时或客户端换网都会关闭流。客户端带 Mcp-Session-Id 与可选 last-event-id 重连续传。
  • 会话撤销:服务端令 session id 失效;客户端下次请求见 404,必须重新握手。
  • 时钟偏移:客户端的 Resource-TTL 计算与服务端发散。客户端应把服务端时间戳当权威。

何时绕过 Streamable HTTP

某些企业在自有网络内把 MCP 服务端部署在 gRPC 或消息队列传输之后。这是非标准的——MCP 规范未正式定义这些。网关可以对 MCP 客户端暴露 Streamable HTTP 表面,内部用 gRPC。保持外表合规,网关拥有翻译。

三、框架对比

维度 stdio Streamable HTTP 旧 HTTP+SSE
部署 本机子进程 远程 / 机群 远程(弃用中)
端点 stdin/stdout /mcp 双端点
鉴权 继承父进程 需要(第 16 节) 需要
会话连续性 进程即会话 Mcp-Session-Id + 重连 脆弱
状态(2026) 标准活跃 标准活跃 移除中

💡 选型心法:本机一律 stdio;过网一律 Streamable HTTP。绝不混用,绝不为新服务端选 HTTP+SSE。

四、可复用产物

本节产出 outputs/skill-mcp-transport-migrator.md——给定一个 HTTP+SSE(legacy)MCP 服务端,它产出迁移到 Streamable HTTP 的计划,含 session-id 连续性、Origin 校验、向后兼容探针支持。

code/main.pyhttp.server(标准库)实现一个最小 Streamable HTTP 端点,处理 /mcp 上的 POST、GET、DELETE,首响应设 Mcp-Session-Id,校验 Origin,拒绝非白名单来源。处理函数复用第 07 节笔记服务端的分发逻辑。

五、练习

  1. 观察 session id:运行 code/main.py,用 curl POST 一个 initialize,观察响应头里的 Mcp-Session-Id。再 POST 一个回显该头的请求,验证会话连续。

  2. 加 GET 流:加一个 GET 处理函数开 SSE 流,每 5 秒发一个 notifications/progress 事件。带相同 session id 重新 GET,确认服务端接受重连。

  3. 实现重放:实现 last-event-id 重放逻辑,重连时重放该 id 之后产生的所有事件。

  4. 通配 Origin:把 Origin 校验扩展到通配模式(https://*.example.com),确认它接受 https://app.example.com 但拒绝 https://evil.example.com.attacker.net

  5. 迁移旧服务端:从官方注册表取一个 legacy HTTP+SSE 服务端(有好几个),画出迁移:端点处理、session id 生成、头语义各改什么。

本节要点回顾

  1. stdio 给本机,Streamable HTTP 给过网:无交叉;子进程继承父信任边界故无需鉴权。
  2. Streamable HTTP 单端点:POST 请求、GET 会话流、DELETE 终止,共享 Mcp-Session-Id
  3. session id 密码学随机:128+ 位,服务端分配,客户端回显;客户端自选 id 被拒。
  4. Origin 校验防 DNS 重绑定:白名单只含已知 MCP 客户端 host 与 localhost;非白名单即 403。
  5. 会话生命周期:首请求无 id → 服务端设 → 客户端回显 → 可撤销(404)→ 可显式 DELETE。
  6. SSE 重连:带 Mcp-Session-Id 重新 GET,服务端用 last-event-id 重放错过事件。
  7. 向后兼容探针:看响应是 JSON/SSE 还是带 Locationtext/event-stream,自动选传输。
  8. 托管要支持长连接:Vercel 免费档 10 秒不够,Cloudflare Workers 支持无限流。
  9. HTTP+SSE 弃用中:2026 年中多家移除,新服务端一律 Streamable HTTP。

下一节,我们深入 resources 与 prompts 两个原语——URI 寻址的只读数据与可复用模板提示,以及订阅、动态资源、模板化 prompts。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U