MCP Stdio 污染怎么处理:为什么 Claude / Cursor 读取不到你的 Server 输出
这篇文章记录了我在贵阳实验室的实战过程。我坚信,在技术下行的时代,程序员唯一的护城河就是通过 AI 建立属于自己的数字资产。
本文解决的问题
- 为什么我的 MCP Server 在本地运行正常,但在 Cursor 里却一直报错
JSON-RPC parse error? - 如何在不破坏 Stdio 通信的前提下,实时查看 Server 的运行日志?
- 哪些常见的 Python 库会“悄悄”污染你的 Stdio 管道?
- 如何在 TypeScript/Node.js 环境下处理类似的
stdout泄露问题?
一、 病因诊断:脆性的 Stdio 管道
MCP (Model Context Protocol) 是一种基于管道的协议。当你把 Server 挂载到 Claude Desktop 或 Cursor 时,它们之间建立了一条物理级别的 Stdio 连接。
污染原理:
Client 预期读取到的是:
{"jsonrpc": "2.0", "id": 1, "result": {...}}
但如果你在代码里写了:
print("Initializing database...")
Client 实际读取到的是:
Initializing database...{"jsonrpc": "2.0", "id": 1, "result": {...}}
这时候,JSON 解析器会立刻抛出异常,导致你的 Tool 消失或报超时错误。
二、 物理级修复方案
1. Python 强制重定向
在你的 Python Server 入口处,执行全局拦截。
import sys
import logging
# 关键:将 logging 绑定到 stderr 而非 stdout
logging.basicConfig(
stream=sys.stderr,
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("mcp-server")
# 也可以直接重定向 stdout 到 stderr (暴力但有效)
# sys.stdout = sys.stderr
2. 检查第三方库的“私活”
有些库(特别是数据分析类的)在 import 或初始化时会打印欢迎语。
排查方法:
在终端执行 python your_server.py。如果你看到任何输出,那就是污染源。
三、 生产环境调试建议
1. 使用专用的 MCP 调试器
Anthropic 官方提供了 mcp-inspector,它可以模拟 Client 环境并高亮展示 Stdio 中的非协议文本。
2. 结果截断与 Stdio
如果你的返回结果包含数万行文本,Stdio 管道可能会因为缓冲区满而导致进程挂起。
对策:对较大的 Resources 或 Tools 结果执行语义压缩或分页。
FAQ
「问」我必须用 Stdio 吗?
不是。MCP 也支持 SSE (Server-Sent Events) 通过 HTTP 通信。但在本地开发和 IDE 集成中,Stdio 是最轻量、延迟最低的选择。
「问」为什么 sys.stdout.write 也不行?
只要是写入 stdout 的任何字节,都会被 Client 当作协议消息解析。请锁死 sys.stderr 进行日志记录。