MCP 的 stdio 和 Streamable HTTP 怎么选?一次连接的生命周期是怎样的?
简化版
MCP 的消息都是 JSON-RPC 2.0,传输层有两种标准方式。stdio:客户端把 Server 当作子进程启动,通过标准输入写请求、从标准输出读响应,适合本机运行的 Server(读本地文件、调本地命令),部署简单、没有网络暴露,但只能一个客户端独占一个进程。Streamable HTTP:Server 作为独立的 HTTP 服务,提供一个端点,客户端用 POST 发消息,Server 可以返回一次性的 JSON,也可以返回 SSE 流逐条推送;会话用 Mcp-Session-Id 头标识,适合远程部署、多客户端共享,但要自己处理认证、跨域和安全。它在 2025-03-26 版规范中取代了早期的「HTTP + SSE」传输。一次连接的生命周期是:客户端发 initialize(带协议版本、自身能力、客户端信息)→ Server 回应(协商后的版本、Server 能力、Server 信息)→ 客户端发 notifications/initialized → 正常交互(列工具、调工具等)→ 关闭连接。初始化完成之前,除了心跳之类的消息,双方不应发送其他请求。
详细版
| 维度 | stdio | Streamable HTTP |
|---|---|---|
| Server 在哪 | 客户端启动的本地子进程 | 独立运行的 HTTP 服务,本地或远程 |
| 消息怎么走 | 标准输入写、标准输出读,每条消息一行 | POST 到同一个端点;响应是 JSON 或 SSE 流 |
| 多个客户端 | 一个进程服务一个客户端 | 一个服务可以同时服务多个客户端 |
| 会话 | 进程存活即会话存在 | Mcp-Session-Id 头标识会话 |
| 认证 | 靠本机进程权限和环境变量里的凭证 | 需要认证授权,规范给出基于 OAuth 2.1 的方案 |
| 适合 | 本地文件、本地命令行工具、个人开发环境 | 团队共享的服务、云上部署、对外开放 |
生命周期:
1. initialize 客户端 → Server:protocolVersion、capabilities、clientInfo
2. initialize 响应 Server → 客户端:protocolVersion、capabilities、serverInfo
3. notifications/initialized 客户端 → Server:初始化完成
4. 正常交互 tools/list、tools/call、resources/read ……(只用协商过的能力)
5. 关闭 stdio:关闭输入流、结束子进程;HTTP:关闭连接,可显式结束会话
完整版教学
一、传输层和消息层是分开的
MCP 的消息格式和传输方式是两层:消息层统一用 JSON-RPC 2.0,有三种消息:
请求(request): 有 id,要求对方回应,如 tools/call
响应(response): 带同一个 id,返回结果或错误
通知(notification):没有 id,不需要回应,如 notifications/initialized
传输层只负责把这些消息从一端送到另一端。同一套 Server 逻辑,可以同时支持 stdio 和 HTTP 两种传输,业务代码不需要关心消息是从管道来的还是从网络来的。三层角色和能力协商的整体架构见「MCP 的架构是什么?它在 AI Agent 系统中解决了什么问题?」。
二、stdio:本机子进程
stdio 是最简单的传输:客户端启动 Server 进程,自己持有它的标准输入和标准输出。
客户端 Server 子进程
│ ── 写 stdin:{"jsonrpc":"2.0","id":1,"method":"initialize",…}\n ──▶
│ ◀── 读 stdout:{"jsonrpc":"2.0","id":1,"result":{…}}\n ──
│ 日志 → stderr(不能写进 stdout)
几条硬规则:每条消息一行,消息内部不能有换行;Server 的标准输出只能写 MCP 消息,打日志要写到标准错误,否则客户端会把日志当成协议消息解析失败。关闭时客户端先关闭输入流,等 Server 退出,超时不退再发终止信号。
它的优点是没有网络暴露、部署就是一条启动命令;缺点是一个进程只服务一个客户端,Server 跟着客户端的生命周期走,不适合共享。
三、Streamable HTTP:一个端点,两种响应
Streamable HTTP 下,Server 只提供一个 HTTP 端点(比如 /mcp):
客户端发消息: POST /mcp,Accept 同时声明 application/json 和 text/event-stream
Server 响应: 简单请求 → 直接返回一个 JSON 响应
耗时或需要中途推送 → 返回 SSE 流,逐条推送消息,最后推送响应
Server 主动推送:客户端可以 GET /mcp 打开一条 SSE 流,接收 Server 发起的消息
会话: 初始化响应里 Server 给出 Mcp-Session-Id,之后每个请求都带上它
这种设计让简单调用保持一次请求一次响应,同时又能支持长任务的进度推送。它取代了早期规范里「一个端点收消息、另一个 SSE 端点发消息」的 HTTP + SSE 传输,好处是只有一个端点,也可以做成无状态的简单服务。
记忆钩子:stdio 是「本机插线」,Streamable HTTP 是「一个网口,能一问一答也能边做边播」。
四、HTTP 传输的安全要点
HTTP 传输把 Server 暴露在网络上,就要处理 stdio 不需要操心的问题:
| 问题 | 做法 |
|---|---|
| 本地服务被网页利用(DNS 重绑定) | 校验请求的 Origin 头;本机服务只监听 127.0.0.1,不监听所有网卡 |
| 谁能调用 | 做认证授权,规范给出基于 OAuth 2.1 的授权方案;令牌要确认是发给本 Server 的 |
| 会话被冒用 | 会话 ID 要足够随机,不能把它当作身份凭证,授权仍然每次校验 |
| 断线续传 | SSE 事件可以带 id,断开后客户端可以用最后收到的事件 id 请求续传 |
stdio 传输下,凭证一般从环境变量传给子进程,不走 HTTP 的授权流程。
五、生命周期:先握手,再干活
初始化是一次能力协商:
{"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": "2025-06-18",
"capabilities": {"roots": {"listChanged": true}, "sampling": {}},
"clientInfo": {"name": "demo-client", "version": "1.0.0"}}}
Server 回应它支持的协议版本、自己的能力(比如 tools、resources、prompts,以及是否支持列表变更通知)和 Server 信息。如果 Server 不支持客户端提出的版本,会回应一个自己支持的版本;客户端也不支持的话,就应该断开。之后客户端发送 notifications/initialized,双方进入正常交互。
协商的意义在于:只能使用双方都声明过的能力。Server 没声明 resources,客户端就不该发 resources/list;客户端没声明 sampling,Server 就不该请求它代为调用模型。
六、交互中的几个通用机制
正常交互阶段,除了业务请求,还有几个通用机制:
| 机制 | 作用 |
|---|---|
| ping | 检查对方是否还活着 |
| 取消通知 | 请求发出后不再需要结果时,通知对方取消 |
| 进度通知 | 请求里带进度令牌,对方执行长任务时推送进度 |
| 超时 | 规范建议发送方为请求设置超时,超时后发送取消通知并停止等待 |
用一个数字感受超时的必要:如果某个工具调用卡住,没有超时的话,一次 Agent 运行会一直挂着;设 30 秒超时(示意),超时后取消请求、把「工具超时」作为结果交回模型,Agent 至少能换一条路继续。
七、常见误区与追问
- 误区:MCP 用的是 HTTP + SSE 两个端点。 那是早期传输;现行规范用 Streamable HTTP,一个端点、响应可以是 JSON 或 SSE 流。
- 误区:stdio Server 可以随便往标准输出打日志。 标准输出只能写协议消息,日志要写标准错误。
- 误区:连上就能直接调工具。 要先完成初始化和能力协商,只使用双方都声明过的能力。
- 误区:有了会话 ID 就不用鉴权。 会话 ID 不是身份凭证,HTTP 传输下授权仍要每次校验。
- 误区:本地 HTTP Server 只有自己用,不用管安全。 本机服务可能被网页通过 DNS 重绑定访问,要校验
Origin、只监听本机地址。 - 追问:什么时候选 stdio? Server 需要访问本机资源、只给一个客户端用、希望零网络暴露时,比如本地文件和命令行工具。
- 追问:版本协商失败怎么办? Server 回应自己支持的版本,客户端不支持就断开,不能在不兼容的版本上继续交互。
八、加强记忆
MCP 传输与生命周期记「一套消息、两种线路、先握手后干活」:消息统一是 JSON-RPC 2.0 的请求、响应、通知。stdio 是客户端启动子进程,一行一条消息,标准输出只写协议消息、日志走标准错误,适合本机单客户端;Streamable HTTP 是一个端点,POST 发消息,响应可以是 JSON 或 SSE 流,Mcp-Session-Id 标识会话,要校验 Origin、只监听本机或做 OAuth 授权,它取代了早期的 HTTP + SSE。生命周期是 initialize 协商版本和能力、notifications/initialized 确认、只用协商过的能力交互、最后关闭;交互中有 ping、取消、进度和超时。