你在 Claude Code 里配了一个 MCP server(filesystem、GitHub、或者你自己写的)。直接 curl 它的 HTTP endpoint 一切正常、server 启动日志也健康、但 Claude Code 发出的每次工具调用 30 秒左右就超时、还没有像样的报错。Claude Code 和 server 之间走的是 JSON-RPC、底层 transport 要么 stdio 要么 HTTP/SSE。哪种 transport 都会因为帧错、响应形态对不上 schema、或者 server 持着连接不发结果而卡死。多数情况下 server 并没有真正挂——是 Claude Code 在等一个永远不会以它能识别的形式到来的回复。
常见原因
按命中率从高到低。
1. Server 返回的 JSON-RPC id 不对
JSON-RPC 是按 id 配对请求和响应的。Server 返回的 result 没带 id 或 id 对不上,Claude Code 会一直等原来那个 id 的响应。
怎么判断:抓 wire 流量(stdio 看 stderr 日志,HTTP 看 network 面板)。响应 id 必须等于请求 id。
2. Stdio 帧坏了(少了 Content-Length 或多了换行)
Stdio MCP 的每条消息必须有 Content-Length 头、加上双 CRLF。Server 往 stdout 多打一行 log 都会把帧搞坏。
怎么判断:Server 跑的时候 stderr 和 stdout 分开。stdout 上出现任何非 JSON-RPC 字节就完了。
3. Server 从来不回 initialize
Claude Code 每次会话开头都会发 initialize。Server 不回,后面任何工具调用都跑不起来、每次都超时。
怎么判断:Server 日志应该能看到入向 initialize。有的话还得看出向的 initialize 响应。少了出向响应,就是你 handler 写错了。
4. Tool schema 非法、Claude Code 根本没注册这个工具
Server 返回的 tool list 里 JSON Schema 写坏了,Claude Code 可能会静默丢掉这个 tool。后续调用会 fallback 到一个不存在的同名 tool。
怎么判断:连上之后跑 claude --debug,找 「tool registered」或 schema 校验报错。
5. HTTP transport 被反代或防火墙挡了
MCP server 在 nginx 或 Cloudflare 后面、chunked-transfer 或 SSE 没开,long-poll 响应就会卡。
怎么判断:curl 同一个 endpoint 带 --no-buffer,看有没有分块返回。curl 也和 Claude Code 一样挂,就是代理的问题。
6. MCP 配置里的 command 不对
MCP 配置里的 command 启动了错的二进制、或者用了过期路径。Claude Code 起了进程、进程立刻退、调用一直等一个根本不存在的 server。
怎么判断:Claude Code 跑着的时候 ps aux | grep mcp,server 进程应该活着。
开始前
- 准备好 MCP server 自己的日志。
- 知道 transport 是 stdio 还是 HTTP/SSE。
- 能在 Claude Code 之外独立 curl 这个 server。
- 改 MCP 配置前先备份。
需要收集的信息
- Claude Code 版本:
claude --version。 - settings.json 或 mcp.json 里的 MCP server 配置块。
- Server 自己的版本号 + 挂的时候的日志输出。
- HTTP 的话:
curl --no-buffer -v的 wire 流量。 - Stdio 的话:spawn 出来那个进程的 stderr。
claude --debug启动日志过滤mcp。
一步一步修复
Step 1:确认 Claude Code 真的把 server 起来了
Stdio server 的话跑:
ps aux | grep -i mcp
应该能看到 server 进程作为 Claude Code 的子进程。没有就是 command 路径或参数错了。
Step 2:看 MCP debug 日志
claude --debug 2>&1 | grep -i mcp
找 initialize 请求和响应、tools/list 调用、和任何 error 行。第一个失败点通常一眼能看出来。
Step 3:Stdio 情况下,server 端把 stdout 和 stderr 分开
Server 只能往 stdout 写 JSON-RPC 帧。所有 log 都改到 stderr:
node my-mcp-server.js 2> /tmp/mcp.log
Claude Code 启动的时候 tail 一下 /tmp/mcp.log。
Step 4:HTTP 情况下,直接测 endpoint
curl --no-buffer -v -X POST https://mcp.example.com/rpc \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
curl 也挂,那就是 server 或代理的问题,不是 Claude Code。
Step 5:校验 JSON-RPC id 配对
Server 里把每个入向 id 和出向 id 都 log 一下、确认对得上。常见 bug:server 自己生成 id,不是 echo 请求的。
Step 6:校验 tool schema
拉一下 tool list:
curl -X POST https://mcp.example.com/rpc \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
拿返回的 schema 过一遍 JSON Schema validator,把报的错都修了。
Step 7:降低 timeout、设好重试
Server 自身 timeout 设得比 Claude Code MCP timeout 短(一般 30 秒以内)。工具时间内完不成就显式报错、别一直把连接挂着。
怎么验证修好了
- 一次简单的 MCP 调用(list、ping、status)一秒内回结果。
- Server 日志里能看到 id 配对的请求/响应。
claude --debug显示工具注册成功、调用也成功。- 反复调用几分钟后行为不回退。
长期预防
- MCP server 启动时跑一次 health check,
initialize失败就直接退出。 - Server 日志默认全走 stderr;stdio transport 下永远不能往 stdout 写非 JSON-RPC 的东西。
- JSON-RPC
id原样从请求 echo 到响应里。 - 配置里把 MCP server 版本钉死,避免 schema 突然变。
- 加集成测试:stdio 模式起 server、真的跑一遍
initialize+tools/list往返。
容易踩的坑
- Server 里用
print()调试,输出落 stdout、帧就坏了。 - 加个「以防万一」的 sleep 几毫秒,结果总响应时间超过 30 秒。
- HTTP MCP 在 Cloudflare 后面忘了开 streaming。
- 同一个 request id 写了两份响应,Claude Code 只消费第一份。
- Server 配了两份(一份用户全局、一份项目),连到了错的那份。
常见问答
- Claude Code 里 MCP 默认 timeout 是多少? 单次调用 30 秒左右,initialize 给几秒。长耗时工具该返回 job id 让 Claude Code 轮询。
- 能调大 timeout 吗? 某些版本 settings.json 里能设 MCP timeout,看 release notes。
- stdio 和 HTTP 哪个更好? stdio 单次开销低、HTTP 适合多客户端。按场景选。
- 为啥 curl 能通 Claude Code 不通? 几乎都是帧错(stdio)或响应形态错(HTTP)。把两边 wire 格式抓下来比一下。
- 不开 Claude Code 能调试 MCP 吗? 能,用
@modelcontextprotocol/inspector直接和 server 对话。 - 调用会自动重试吗? 某些调用会重试一次,超过就得自己重发 prompt。
相关
- Claude Code 工具执行卡住
- Claude Code settings.json 不生效
- Claude Code subagent 结果回传不到主会话
- Claude Code hook 莫名拦下 Edit
- Claude Code 半执行后卡住
- Claude Code 输出被上下文截断
标签: #Claude Code #mcp #排查 #排查