概念图解 · Unix 进程与 MCP

stdio:进程的三条标准流

程序从 fd 0 读取输入,向 fd 1 和 fd 2 写入输出。启动程序的一方决定这些流连接到哪里。

stdin=标准输入(fd 0) · stdout=标准输出(fd 1) · stderr=标准错误(fd 2) · fd=文件描述符,内核用来标识已打开文件或流的小整数

输入 输出 诊断 键盘 任意程序 0 stdin stdout 1 stderr 2 屏幕
图 1在交互式终端中,fd 0 通常接收键盘输入,fd 1 和 fd 2 都输出到屏幕。

重定向

Shell 改变流的去向,程序保持不变

程序只需「向 fd 1 写入」。目标可以是屏幕、文件,也可以是另一个程序。Shell 会在启动程序前打开目标并完成连接。逐步查看下面四条命令。

$ ./app $ ./app > out.txt $ ./app > out.txt 2> err.log $ ./app 2> err.log | sort 输入 键盘 ./app 0 stdin stdout 1 stderr 2 输出 诊断 屏幕 输出 诊断 out.txt 屏幕 输出 诊断 out.txt err.log 管道 诊断 sort 屏幕 err.log
  1. 没有重定向:stdout 和 stderr 都输出到屏幕。
  2. > 把 fd 1 接到 out.txt。错误信息仍显示在屏幕上,方便发现失败。
  3. 2> 把 fd 2 接到 err.log。两条输出流都已写入文件,屏幕不再显示它们。
  4. | 把 fd 1 接到管道,再接到 sort 的 fd 0。sort 从管道读取数据,将排序结果通过自己的 fd 1 输出到屏幕。
图 2四条命令运行的都是同一个 ./app;变化的是 fd 1 和 fd 2 的去向。

这就是区分两条输出流的意义。stdout 传递下一个程序要处理的数据,stderr 传递诊断信息。普通管道只转接 stdout,让 stderr 与业务数据分开。

缓冲

写入管道后,缓冲可能让输出迟到

默认情况下,C stdio 通常对终端上的 stdout 使用行缓冲,对管道或文件使用块缓冲。Python 的 stdout 也有类似的默认行为。显式刷新可以提前发送缓冲内容;运行环境也可以调整这些默认设置。

及时输出

stdout → 终端

app 终端 遇到 \n 就发送
可能延迟

stdout → 管道或文件

app 管道 满/刷新/退出
及时输出

C stderr → 任意目标

app 目标 通常无缓冲
图 3同样一条带换行的 print,在终端中通常立即显示,写入管道时却可能暂留在缓冲区。

解决方法:调用 fflush(stdout),使用 print(…, flush=True),或运行 python -u。MCP 的另一端正在等待回复,因此及时刷新很重要。C 的 stderr 通常无缓冲;Python 的 stderr 默认使用行缓冲。

MCP

MCP stdio 用 stdin 和 stdout 传递 JSON-RPC

使用 stdio 传输时,客户端把服务端作为子进程启动,并持有三条标准流的另一端。每条消息都是一行 JSON。stdout 专用于协议消息,日志写入 stderr。

启动:node server.js stdin: {"method":"initialize",…}\n stdout: {"id":1,"result":{…}}\n stderr: connected to CRM stdout: debug x=3 ✕ 关闭 stdin → 服务端读到 EOF 退出 1 2 3 4 5 6 MCP 客户端 MCP 服务端进程
  1. 客户端启动服务端子进程,接好它的 stdin、stdout 和 stderr。客户端持有这些流的另一端。
  2. 客户端向服务端的 stdin 写入一条 JSON-RPC 请求,并以换行符结束。
  3. 服务端把响应作为一行写入 stdout。客户端逐行读取,每行解析为一条消息。
  4. 日志写入 stderr。客户端可以显示、保存或忽略日志,协议消息不受影响。
  5. 一次随手写到 stdout 的 print 可能输出普通文本,导致客户端解析失败。规范要求 stdout 只承载有效的 MCP 消息。
  6. 停止时,客户端关闭 stdin。服务端读到 EOF(输入结束)后应退出;必要时客户端还会发送终止信号。
图 4MCP stdio 复用标准输入输出机制,并要求 stdout 的每一行都是有效的 MCP 消息。

选择传输方式

直接使用 stdio 时,客户端连接本地服务端进程

本地

stdio

同一台机器 启动 客户端 服务端
远程

Streamable HTTP

客户端 客户端 客户端 /mcp
图 5stdio 通常由一个客户端启动专用服务端进程;Streamable HTTP 可让多个客户端访问同一个已部署服务。
stdioStreamable HTTP
谁启动服务端客户端将其作为子进程启动独立部署并启动
每个服务端进程的客户端数量通常一个可有多个
客户端如何连接通过接好的 stdin 和 stdout向同一个 URL 发起 HTTP POST 和 GET
凭据从运行环境获取,例如环境变量随请求携带授权,例如 OAuth Bearer 令牌
云端智能体能否访问你的电脑需要桥接 直接使用 stdio 没有网络访问端点可以 需要网络可达,并具备所需授权

要点

记住这三点,就能理解大多数 stdio 问题

  1. 进程从启动它的一方获得 fd 0、1、2。Shell 或 MCP 客户端决定这些流的连接目标。
  2. stdout 传递数据,stderr 承载诊断信息。MCP 的 stdout 混入一行非 JSON 文本,就可能导致协议解析失败。
  3. 许多运行环境会缓冲写入管道的 stdout。每条消息写完后及时刷新,避免回复迟到。

来源

MCP 规范 2025-06-18,传输层:modelcontextprotocol.io/specification/2025-06-18/basic/transports。缓冲默认行为参见 stdin(3)、setvbuf(3) 手册及 Python 标准流说明。图 2 和图 4 使用简化的示例命令与消息。