概念图解 · Unix 进程与 MCP
stdio:进程的三条标准流
程序从 fd 0 读取输入,向 fd 1 和 fd 2 写入输出。启动程序的一方决定这些流连接到哪里。
重定向
Shell 改变流的去向,程序保持不变
程序只需「向 fd 1 写入」。目标可以是屏幕、文件,也可以是另一个程序。Shell 会在启动程序前打开目标并完成连接。逐步查看下面四条命令。
- 没有重定向:stdout 和 stderr 都输出到屏幕。
>把 fd 1 接到out.txt。错误信息仍显示在屏幕上,方便发现失败。2>把 fd 2 接到err.log。两条输出流都已写入文件,屏幕不再显示它们。|把 fd 1 接到管道,再接到sort的 fd 0。sort从管道读取数据,将排序结果通过自己的 fd 1 输出到屏幕。
./app;变化的是 fd 1 和 fd 2 的去向。这就是区分两条输出流的意义。stdout 传递下一个程序要处理的数据,stderr 传递诊断信息。普通管道只转接 stdout,让 stderr 与业务数据分开。
缓冲
写入管道后,缓冲可能让输出迟到
默认情况下,C stdio 通常对终端上的 stdout 使用行缓冲,对管道或文件使用块缓冲。Python 的 stdout 也有类似的默认行为。显式刷新可以提前发送缓冲内容;运行环境也可以调整这些默认设置。
及时输出
stdout → 终端
可能延迟
stdout → 管道或文件
及时输出
C stderr → 任意目标
print,在终端中通常立即显示,写入管道时却可能暂留在缓冲区。解决方法:调用 fflush(stdout),使用 print(…, flush=True),或运行 python -u。MCP 的另一端正在等待回复,因此及时刷新很重要。C 的 stderr 通常无缓冲;Python 的 stderr 默认使用行缓冲。
MCP
MCP stdio 用 stdin 和 stdout 传递 JSON-RPC
使用 stdio 传输时,客户端把服务端作为子进程启动,并持有三条标准流的另一端。每条消息都是一行 JSON。stdout 专用于协议消息,日志写入 stderr。
- 客户端启动服务端子进程,接好它的 stdin、stdout 和 stderr。客户端持有这些流的另一端。
- 客户端向服务端的 stdin 写入一条 JSON-RPC 请求,并以换行符结束。
- 服务端把响应作为一行写入 stdout。客户端逐行读取,每行解析为一条消息。
- 日志写入 stderr。客户端可以显示、保存或忽略日志,协议消息不受影响。
- 一次随手写到 stdout 的
print可能输出普通文本,导致客户端解析失败。规范要求 stdout 只承载有效的 MCP 消息。 - 停止时,客户端关闭 stdin。服务端读到 EOF(输入结束)后应退出;必要时客户端还会发送终止信号。
选择传输方式
直接使用 stdio 时,客户端连接本地服务端进程
本地
stdio
远程
Streamable HTTP
| stdio | Streamable HTTP | |
|---|---|---|
| 谁启动服务端 | 客户端将其作为子进程启动 | 独立部署并启动 |
| 每个服务端进程的客户端数量 | 通常一个 | 可有多个 |
| 客户端如何连接 | 通过接好的 stdin 和 stdout | 向同一个 URL 发起 HTTP POST 和 GET |
| 凭据 | 从运行环境获取,例如环境变量 | 随请求携带授权,例如 OAuth Bearer 令牌 |
| 云端智能体能否访问你的电脑 | 需要桥接 直接使用 stdio 没有网络访问端点 | 可以 需要网络可达,并具备所需授权 |
要点
记住这三点,就能理解大多数 stdio 问题
- 进程从启动它的一方获得 fd 0、1、2。Shell 或 MCP 客户端决定这些流的连接目标。
- stdout 传递数据,stderr 承载诊断信息。MCP 的 stdout 混入一行非 JSON 文本,就可能导致协议解析失败。
- 许多运行环境会缓冲写入管道的 stdout。每条消息写完后及时刷新,避免回复迟到。
来源
MCP 规范 2025-06-18,传输层:modelcontextprotocol.io/specification/2025-06-18/basic/transports。缓冲默认行为参见 stdin(3)、setvbuf(3) 手册及 Python 标准流说明。图 2 和图 4 使用简化的示例命令与消息。