用 MCP 接过工具的人,大多见过两种接法:有的工具装完就能用,有的要在配置里填一个 URL。前者是 stdio,后者是 Streamable HTTP。这两个名字背后,是 MCP 官方规范里仅有的两种标准传输方式,而「MCP 也支持 SSE」这个流传很广的说法,其实已经过时了。
这篇把两种传输的运行方式、SSE 的真实身份、断线怎么续、会话怎么管,一次讲清楚。所有细节都对照 2025-06-18 版官方规范核实过。
先记住协议本身的两条底线
MCP 的消息用 JSON-RPC 编码,规范里有两句大写的 MUST:消息必须是 UTF-8 编码;协议本身与传输无关,只要通信通道支持双向消息交换,你甚至可以自己实现自定义传输。
所以传输层解决的是同一个问题:JSON-RPC 这封信,怎么从客户端送到服务器,再怎么把回信送回来。官方标准答案有两个。
stdio:本机的管子
stdio 传输的机制一句话说完:客户端把 MCP 服务器作为子进程启动,服务器从标准输入读 JSON-RPC 消息,往标准输出写响应,消息用换行分隔,且不允许嵌换行。日志走 stderr,客户端可以捕获、转发,也可以无视。
规范里有两句容易被忽略的纪律:服务器不得向 stdout 写任何不是 MCP 消息的东西,客户端也不得向 stdin 写任何不是 MCP 消息的东西。原因很直接:stdout 和 stdin 是协议消息的专用管道,谁往里 print 了一行调试日志,对面的解析就会炸。这也是很多自制工具「本地好好的,接到 MCP 客户端里就报错」的常见根源。
stdio 的适用边界也因此很清楚:它快、零配置、天然隔离在本机,官方建议客户端尽可能支持 stdio。但它只能本机用,管子两端是父子进程,跨不了网络。
Streamable HTTP:跨网络的正解
跨网络的标准答案是 Streamable HTTP。服务器作为独立进程运行,可以同时处理多个客户端连接,用 HTTP POST 和 GET 通信,服务器可选地用 SSE 流式发送多条消息。
它的工作方式有四个要点。
第一,服务器必须提供一个同时支持 POST 和 GET 的单一端点,比如 example.com/mcp 这样一个 MCP endpoint。
第二,客户端发的每条 JSON-RPC 消息,都是一次全新的 HTTP POST,请求头必须同时声明接受 application/json 和 text/event-stream 两种类型。服务器可以回一个普通 JSON 对象,也可以开一条 SSE 流再回响应,客户端两种都必须支持。
第三,GET 是反向通道:客户端可以主动 GET 这个端点打开一条 SSE 流,用来接收服务器主动推送的消息;服务器不想提供这条通道,回 405 即可。
第四,安全红线有三条,全是官方原文级别的要求:验证所有连入连接的 Origin 头,防 DNS 重绑定攻击;本地运行时只绑定 localhost,不要绑 0.0.0.0;为所有连接实现正当的鉴权。少一条,远端网页就有可能借 DNS 重绑定摸到你本机的 MCP 服务器。
SSE 在 MCP 里的身份到此可以还原了:它不是一种独立传输,而是 Streamable HTTP 里服务器可选使用的流式发送手段。
「MCP 支持 SSE」为什么过时了
这个说法来自旧规范。2024-11-05 版协议定义过一种 HTTP+SSE 传输:两个端点,GET 拿事件端点,POST 发消息。新版把它整个替换掉了。
规范原文写得很明确:Streamable HTTP replaces the HTTP+SSE transport from protocol version 2024-11-05。新版把两个端点合并成一个,消息方向也不再绑定在单条 SSE 连接上。
如果你今天还在维护老服务器,规范给了向后兼容的做法:服务器想照顾老客户端,就把新旧端点并存;客户端想兼容老服务器,先对新端点 POST 初始化请求,成功就是新传输,失败收到 405 或 404,再 GET 一次,收到 endpoint 事件就说明对面是老传输。
断线重连:Last-Event-ID 接着放
SSE 流随时可能断,规范为此准备了续传机制,规则围绕一个游标展开。
服务器可以在 SSE 事件上附 id,这个 id 在会话内全局唯一,按流分配,作用是这条流内部的光标。客户端断线后重连时,GET 请求带上 Last-Event-ID 头,标明自己最后收到的位置,服务器可以从这个点之后把丢失的消息重放回来,且只重放断掉的那条流的消息,别的流不掺和。
顺带一提,这条「事件 id 当游标」的机制,与流式输出里的 SSE 断线重连是同一套思路,此前写过一篇 流式输出与 SSE 入门,可以对照着看。
会话管理:Mcp-Session-Id 五条规则
Streamable HTTP 还定义了有状态会话,靠一个响应头撑起来。
服务器在初始化响应的 Mcp-Session-Id 头里发一个会话 ID,要求全局唯一、密码学安全,且只含可见 ASCII 字符。客户端之后每个请求都必须带上它。五条规则值得整段记下来:
- 服务器可以随时终止会话,终止后对带这个会话 ID 的请求一律回 404;
- 客户端收到 404,必须不带会话 ID 重新发初始化请求,开新会话;
- 客户端不用了,应该主动发 HTTP DELETE 显式终止会话,服务器不想让客户端删,回 405;
- 用 HTTP 时,客户端后续请求都要带 MCP-Protocol-Version 头,写初始化时协商好的版本号;
- 服务器收到无效或不支持的协议版本,必须回 400。
这套规则的本质,是把「会话失效」变成一个客户端可以明确处理的信号:404 就重连,DELETE 就清理,不猜、不重试到天荒地老。报错之后该怎么办,此前写过一篇 Retry-After 与指数退避重试指南,思路相通。
怎么选:一张表收尾
- 本机工具、命令行场景:stdio,快、零配置,守住 stdout 纯净的纪律;
- 远程服务、多人共用:Streamable HTTP,单端点,管好 Origin 校验、本地绑定和鉴权三条红线;
- 听到「MCP 走 SSE」:先问一句是不是老版本协议,SSE 只是流式手段,标准传输只有 stdio 和 Streamable HTTP 两个名字。
MCP 协议本体的入门此前写过两篇,一篇讲它为什么是 AI 连接外部世界的桥梁,一篇是 实用 Server 清单,配合这篇传输层,从协议到部署的拼图就齐了。