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 之间做选择。
本节摘要:stdio 只在本地管用。Streamable HTTP(2025-03-26 规范引入)才是远程标准。旧的 HTTP+SSE 传输已弃用,2026 年中正在被移除。选错传输要付一次迁移代价;选对了,你就得到一个可远程托管、带会话连续性与 DNS 重绑定防护的 MCP 服务端。本节把三种传输并排讲透:stdio(本机)、Streamable HTTP(过网)、以及如何从旧 HTTP+SSE 迁移。
阅读完本节,你应当能够:
Origin 校验与 session-id 语义,击退 DNS 重绑定。第一个 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 给「过网络」,无交叉。
单端点 /mcp(或任意路径),支持三种 HTTP 方法:
会话由 Mcp-Session-Id 头标识——服务端在首个响应上设,客户端在后续每个请求上回显。session id 必须密码学随机(128+ 位);客户端自选的 id 会被拒绝(安全考虑)。
旧规范的双端点模式 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.ai、vscode-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
Mcp-Session-Id。Mcp-Session-Id。GET /mcp 流上都回显该头。SSE 连接会掉。客户端靠带相同 Mcp-Session-Id 重新 GET 来重建。服务端必须把断连期间错过的事件排队(在一个合理窗口内),并通过客户端回显的 last-event-id 头重放。
第 13 节覆盖 Tasks,让长跑工作即使全会话重连也能存活。
一个想同时支持新旧服务端的客户端:
/mcp。200 OK 带 JSON 或 SSE——这是 Streamable HTTP。200 OK 带 Content-Type: text/event-stream 且有指向次端点的 Location 头——这是 legacy HTTP+SSE,跟随 Location。2026 年的生产远程 MCP 服务端跑在 Cloudflare Workers(配其 MCP Agents SDK)、Vercel Functions 或容器化的 Node/Python 上。关键:你的托管必须为 SSE GET 支持长寿命 HTTP 连接。Vercel 免费档上限 10 秒,不适用;Cloudflare Workers 支持无限时长流。
当你用网关(第 17 节)前置多个 MCP 服务端时,网关是单个 Streamable HTTP 端点,重写 session id 并多路复用上游。工具在网关层合并;客户端看到单个逻辑服务端。
Mcp-Session-Id 与可选 last-event-id 重连续传。某些企业在自有网络内把 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.py 用 http.server(标准库)实现一个最小 Streamable HTTP 端点,处理 /mcp 上的 POST、GET、DELETE,首响应设 Mcp-Session-Id,校验 Origin,拒绝非白名单来源。处理函数复用第 07 节笔记服务端的分发逻辑。
观察 session id:运行 code/main.py,用 curl POST 一个 initialize,观察响应头里的 Mcp-Session-Id。再 POST 一个回显该头的请求,验证会话连续。
加 GET 流:加一个 GET 处理函数开 SSE 流,每 5 秒发一个 notifications/progress 事件。带相同 session id 重新 GET,确认服务端接受重连。
实现重放:实现 last-event-id 重放逻辑,重连时重放该 id 之后产生的所有事件。
通配 Origin:把 Origin 校验扩展到通配模式(https://*.example.com),确认它接受 https://app.example.com 但拒绝 https://evil.example.com.attacker.net。
迁移旧服务端:从官方注册表取一个 legacy HTTP+SSE 服务端(有好几个),画出迁移:端点处理、session id 生成、头语义各改什么。
Mcp-Session-Id。Origin 校验防 DNS 重绑定:白名单只含已知 MCP 客户端 host 与 localhost;非白名单即 403。Mcp-Session-Id 重新 GET,服务端用 last-event-id 重放错过事件。Location 的 text/event-stream,自动选传输。下一节,我们深入 resources 与 prompts 两个原语——URI 寻址的只读数据与可复用模板提示,以及订阅、动态资源、模板化 prompts。