stdio 传输(stdio transport) 协议
别名:
stdio
MCP 的标准传输之一:client spawn server 子进程,消息经 stdin/stdout 传递,每帧由 Content-Length 头(UTF-8 字节数)+ JSON 体组成,日志一律走 stderr。见[第 21 章](/chapters/21-mcp-v08/)。
它是什么
stdio transport 是 MCP 的两种标准传输之一:client spawn server 子进程,server 从 stdin 读消息、往 stdout 写消息,日志一律走 stderr。它适合「本地工具」场景(没有网络往返、进程即生命周期),也是 第 21 章手写的传输。另一条标准传输是 Streamable HTTP,用于远程 server。
帧格式:Content-Length 头 + JSON 体
stdout 是一根字节流,消息之间需要划界。MCP 规范文字版曾描述过「换行分隔 JSON(NDJSON)」,但现网参考实现(TypeScript SDK,Claude Code 等大多数 client 背后就是它)实际用的是 LSP 风格的长度前缀帧:
Content-Length: <JSON 体的 UTF-8 字节数>\r\n\r\n<JSON body>
为什么长度前缀更稳?因为 JSON 体里可以出现任何字符——换行、中文、emoji——换行分隔法遇到含换行的 JSON 就炸了;Content-Length 告诉读取端「这个帧总共读多少字节」,读到就切,跟内容无关。两个易错点:字节数必须按 UTF-8 字节数("你好" 是 6 个字节,按字符串长度 2 算帧就错位);stdout 上只准出现帧,任何一行调试日志都会被帧解析器当成半帧、让后面全部错位——这是和 chunk 的 buffer 思路同源的「累积缓冲 + 循环切帧」问题。
在本教程的位置
第 21 章的 lib/mcp-stdio.mjs 实现了 encodeFrame(字节数用 Buffer.byteLength(body, 'utf8'))与 FrameReader(字节缓冲累积、切完剩下的留到下一次,多字节字符从中间被切开也不损坏)。close() 给 stdin 发 EOF——管道结束本身就是关闭信号,不需要一条「关闭消息」。