先给结论
选 MCP 传输方式只有一条主线判断:服务器必须碰用户本机(本地文件、桌面应用、 localhost 服务)就用 stdio;它是包装外部网络 API 、要给多个人用、需要正常 OAuth 的就用 Streamable HTTP 。协议语义在两种传输上完全一致,差别只在消息怎么打包送达、取消怎么做、以及由谁负责进程生命周期。
对照表
| 维度 | stdio | Streamable HTTP |
|---|---|---|
| 谁启动进程 | 客户端启动子进程 | 你作为服务自行部署 |
| 能触达范围 | 仅使用者的机器 | 任何能访问 URL 的人 |
| 鉴权方式 | 环境变量、系统钥匙串 | OAuth 2.1 资源服务器等标准方案 |
| 升级方式 | 用户重新安装 | 你重新部署一次 |
| 消息格式 | 换行分隔的 JSON-RPC | HTTP POST + JSON 或 SSE 流 |
| 典型场景 | 文件读写、本地脚本、原型 | SaaS 接入、团队共享能力 |
stdio:规则简单,违规代价大
工作方式:客户端把服务器当子进程拉起,服务器从 stdin 读 JSON-RPC 消息、往 stdout 写响应,消息之间用换行分隔且内部不得含换行。
三条必须记住的约束:
- 不得向 stdout 写任何非协议内容。日志、进度条、第三方库的启动横幅、依赖告警——每一个字节都是协议线上的一帧。
- 日志一律走 stderr,客户端不得把 stderr 输出当成错误信号。
- 优雅退出靠 stdin EOF:客户端关闭输入流,服务器应尽快退出,否则会被升级为终止信号。
「随机卡住」的真实成因
部分 SDK 做了防护(例如 Python 版在内部重定向标准输出),但这是尽力而为的行为,不要依赖它。正确做法:显式把日志配置到 stderr,并且在任何 print 之前就完成这件事。
Streamable HTTP:一个端点搞定所有
工作方式:服务器提供一个路径(约定为 /mcp),每条客户端消息发一次新的 HTTP POST,请求头必须同时声明支持 application/json 与 text/event-stream。服务器可以选择直接返回一个 JSON 对象,也可以返回一个请求级的 SSE 流。
它是替代早期「 HTTP + SSE 双端点」方案的新标准。旧方案需要两个端点、靠 endpoint 事件告知客户端往哪发消息;新方案把复杂度砍掉了一半。
服务端四条硬性要求
- 接收任何连接时都必须校验 Origin 头,无效则返回 403,这是防 DNS 重绑定的必要手段。
- 本地运行时只绑定 127.0.0.1,不要绑 0.0.0.0 。
- 所有连接都要有恰当的身份验证。
- 收到通知或响应类消息时返回 202 Accepted 空响应体。
流与重连
服务器可以先发一个带事件 ID 的空 SSE 事件做「预热」,随后随时可以关闭连接而不终止流;客户端应带着 Last-Event-ID 重连,并遵守服务器给出的重试间隔。响应发完后,服务器应正常终止该流。断开连接不等于客户端取消了请求,这一点在实现重试逻辑时尤其重要。
取消、元数据与自定义传输
- 取消:stdio 上客户端发通知;HTTP 上直接关闭响应流。协议层面的规则一致。
- 元数据:协议版本与能力都放在消息体里,HTTP 传输会把它镜像到请求头,方便中间件路由,但消息体始终是唯一事实来源。
- 自定义传输:允许实现,只要保留 JSON-RPC 格式、消息模型与元数据模型;跑在可靠双向字节流上时,官方建议直接复用 stdio 的分帧方式。
迁移与兼容
如果你的服务还在用旧的 SSE 双端点方案,迁移要点是:合并成单一端点、每条消息一次 POST 、按 SSE 事件 ID 支持断线重连、补齐 Origin 校验。客户端与服务器可以通过初始化握手互相判定对方所处的协议阶段并降级处理。









评论 (0)