AI TOOLS

EP02. "MCP 进阶:STDIO 与 StreamableHTTP 两种传输方式"

首页 › AI 工具 › Claude · Developer and Platform · MCP Advanced · EP02
约 15 分钟· #EP02#Claude#MCP Advanced
🔒 登录后可标记已读
  • 承接 EP01,这篇讲 MCP 底层怎么传消息:本地开发常用的 STDIO、正式上线常用的 StreamableHTTP,以及为什么 HTTP 这种「单向」协议要靠一套 Server-Sent Events(SSE)的 workaround,才能撑起「Server 主动通知 Client」这件事
  • 覆盖课程「Transports and communication」这个大分类,对应 EP01 讲的 Sampling / Notifications 这些功能,全都要靠「Server 能主动发消息给 Client」才能运作,选错传输方式会直接让这些功能失效
  • 前置知识:学过 EP01,理解 Sampling / Notifications 这些依赖「Server 主动发消息」的功能实际长什么样

重点内容


MCP 的消息全部是 JSON:两大类

MCP 完全靠 JSON 消息通信,每种消息类型各司其职——从调用工具、列资源清单,到系统事件通知都算。举例:Claude 要用 Server 提供的某个工具时,Client 发一个 Call Tool Request 消息;Server 执行完,回一个包含结果的 Call Tool Result 消息。

📌 权威的消息类型定义在 MCP 官方 spec 仓库(GitHub),跟各语言的 SDK(Python、TypeScript 等)分开维护;消息类型用 TypeScript 记法描述数据结构,只是用来说明格式,不是能直接跑的代码。

消息分两大类:

类型特点例子
Request-Result(双向配对)发出去一定要有对应的回应Call Tool Request → Call Tool Result;List Prompts Request → List Prompts Result;Read Resource Request → Read Resource Result;Initialize Request → Initialize Result
Notification(单向,不需要回应)发出去就结束,不等对方回话Progress Notification、Logging Message Notification、Tool List Changed Notification、Resource Updated Notification

规范里还按「谁发的」把消息分成 Client 发给 Server 的、和 Server 发给 Client 的两种——这个双向设计,正是后面选传输方式时最关键的考量点。


STDIO Transport:开发阶段的「理想情况」

Client 把 MCP Server 当作子进程启动,两边靠标准输入输出(stdin/stdout)通信:

  • Client 发消息走 stdin
  • Server 回消息走 stdout
  • 双方都能随时主动发起通信
  • 要求 Client 和 Server 跑在同一台机器上

每次连接都要先走三段握手:

  1. Initialize Request——Client 先发
  2. Initialize Result——Server 回应,附带自己的能力清单
  3. Initialized Notification——Client 确认收到,这一步不需要 Server 再回应

握手走完,才能发后续的 tool call、list prompts 这些请求。

📌 STDIO 之所以重要,是因为它代表了「双向沟通完全没有阻碍」的理想情况——先搞懂这个基准,才看得懂后面 HTTP 传输为什么会遇到各种限制。

测试方式:可以直接在终端机里把 JSON 消息贴进 stdin,看 stdout 的回应,不用真的写一个完整的 Client 应用。


StreamableHTTP Transport:要公开上线,但 HTTP 天生不对等

要让 Server 能被远端访问、给公开用户使用,通常得走 HTTP。但普通 HTTP 的通信模式本质上是不对等的:

  • Client 能发请求给 Server(因为 Server 有一个已知的 URL)
  • Server 却没办法轻松主动发请求给 Client(Client 没有已知的 URL)

这个限制直接影响到一批 MCP 消息类型:

  • Server 主动发起的请求——Create Message(也就是 Sampling)、List Roots
  • 各种 Notification——progress、logging、initialized、cancelled

如果传输设置得比较严格,进度条、日志功能、Server 主动发起的 Sampling 请求都会失效——这也是为什么 EP01 讲的 Sampling / Notifications 要放在讲传输方式之前先理解,不然会搞不清楚为什么这些功能"在 HTTP 上用不了"。


SSE Workaround:怎么在 HTTP 里让 Server 也能「主动说话」

StreamableHTTP 用 Server-Sent Events(SSE) 撑出一条 Server 能持续向 Client 推送消息的通道,绕开 HTTP「只有 Client 能发起请求」的限制。

连接建立流程:

  1. Client 发 Initialize Request 给 Server
  2. Server 回 Initialize Result,里面带一个 mcp-session-id 请求头
  3. Client 发 Initialized Notification,带上这个 session id

这个 session id 是之后每个请求都要带的必要信息。

握手完成后,Client 会另外开一个长连的 GET 请求,打开一条持续存在的 SSE 通道,让 Server 能透过它持续往 Client 推消息。

Tool Call 时会同时用到两条 SSE 连接:

连接作用生命周期
主 SSE 连接处理 Server 主动发起的请求一直保持开启
工具专属 SSE 连接每次 tool call 各自开一条拿到结果就自动关闭

消息路由规则:progress notification 走主连接;logging 消息和 tool 执行结果走工具专属连接。


stateless_http 与 json_response:两个会削弱功能的开关

开关默认值打开(True)之后
stateless_httpfalse不再发 session id;Server 不能主动发请求给 Client;不能用 Sampling;不能报进度;不能订阅 Resource 更新。代价换来的好处:Client 不用再走初始化握手,可以直接发请求
json_responsefalse关掉 POST 响应的流式推送——tool 执行只会拿到最终的 JSON 结果,中间的 progress/logging 消息全部收不到

什么时候该开 stateless_http:典型场景是单一 Server 撑不住几千个并发 Client,要做水平扩展——在 load balancer 后面挂多台 Server 实例。但正常的 MCP Client 要同时维护两条独立连接(一条 GET SSE 收 Server 主动发来的请求,一条 POST 发 tool call 拿结果),水平扩展下多台 Server 实例要协调这些连接会很麻烦。开了 stateless_http=True,就不用维护 session,Client 可以直接发请求,省掉初始化握手,代价是放弃所有依赖「Server 主动通信」的功能。

适合开 stateless_http 的场景:要做水平扩展、本来就不需要 Server 主动跟 Client 说话、工具不需要用到 Sampling、想把连接开销降到最低。

适合开 json_response 的场景:不需要流式响应、系统本来就只期待拿到一份纯 JSON 结果。

📌 开发阶段最好直接用打算实际部署的那种传输方式来测——stateful 跟 stateless 两种模式的行为差异不小,图方便先用 STDIO 或 stateful HTTP 开发,上线前才临时切到 stateless,很容易踩到开发阶段完全没测过的坑。


学完你会

  • ✅ 分得清 Request-Result 和 Notification 两类消息的差别,理解「Server 能不能主动发消息」这件事其实是跟传输方式绑在一起的
  • ✅ 讲得出 STDIO 作为本地开发「理想情况」的原因——双向沟通没有限制,是理解后续限制的参照基准
  • ✅ 讲得清 StreamableHTTP 为什么要靠 SSE 撑出 Server 主动推送,以及 tool call 时那两条 SSE 连接各自负责什么
  • ✅ 分得清 stateless_http 和 json_response 这两个开关各自关掉了哪些功能,知道什么场景才该开

常见错误

  • ❌ 开发阶段随便切换 stateless_http 测试,以为跟 stateful 模式行为差不多——两种模式差别很大(session、进度、Sampling 全部受影响),上线前务必用打算实际部署的那种模式完整测过一遍
  • ❌ 想让 Server 支持 Sampling / Progress Notification,却把服务部署成没有 SSE 的纯 HTTP、或者开了 stateless_http=True——这些功能全部要靠「Server 能主动发消息给 Client」,跟 stateless 模式互斥
  • ❌ 为了应付更大并发直接开 stateless_http,却没意识到这会连带砍掉 Sampling、进度回报、Resource 订阅这些功能——先确认自己的 Server 到底用不用得到这些功能,再决定要不要开
  • 💡 STDIO 是本地开发的黄金标准——先在 STDIO 下把逻辑写对、验证过,再迁移到 HTTP,遇到问题时比较容易分清楚是「传输方式本身的限制」还是「自己代码写错了」

Sources

官方文档:

  1. Model Context Protocol: Advanced Topics(课程总览)— https://academy.claude.com/courses/model-context-protocol-advanced-topics
  2. JSON message types — https://academy.claude.com/courses/model-context-protocol-advanced-topics/json-message-types
  3. The STDIO transport — https://academy.claude.com/courses/model-context-protocol-advanced-topics/the-stdio-transport
  4. The StreamableHTTP transport — https://academy.claude.com/courses/model-context-protocol-advanced-topics/the-streamablehttp-transport
  5. StreamableHTTP in depth — https://academy.claude.com/courses/model-context-protocol-advanced-topics/streamablehttp-in-depth
  6. State and the StreamableHTTP transport — https://academy.claude.com/courses/model-context-protocol-advanced-topics/state-and-the-streamablehttp-transport