Claude Code MCP Call Times Out Repeatedly

MCP tool calls (filesystem, GitHub, custom) time out in Claude Code, but a direct curl to the server works. Usually transport, stdio framing, or response shape.

You configured an MCP server (filesystem, GitHub, a custom one) in Claude Code. Direct curl to its HTTP endpoint returns fine, the server logs show healthy startup, but every tool call from Claude Code times out after about 30 seconds with no useful error. MCP between Claude Code and the server speaks JSON-RPC over either stdio or HTTP/SSE; either transport can stall if framing is wrong, the response shape does not match the schema, or the server holds the connection open without sending a result. Most of the time the server is not really hung — Claude Code is waiting for a reply that will never come in a form it recognizes.

Common causes

Ordered by hit rate, highest first.

1. Server returns a response with the wrong JSON-RPC id

JSON-RPC pairs requests and responses by id. If the server returns a result with a missing or mismatched id, Claude Code waits forever for the original id.

How to judge: Capture the wire traffic (stderr log on stdio, network panel on HTTP). The response id must equal the request id.

2. Stdio framing is broken (missing Content-Length or extra newlines)

For stdio MCP, each message must be prefixed with a Content-Length header and double CRLF. A server that prints stray log lines to stdout corrupts the frame.

How to judge: Run the server with stderr separated from stdout. Any non-JSON-RPC bytes on stdout will break it.

3. The server never sends initialize response

Claude Code begins every session by sending initialize. If the server does not reply, no tool calls can proceed and every call eventually times out.

How to judge: Server logs should show an inbound initialize. If yes, must show outbound initialize response. If missing, your handler is wrong.

4. Tool schema is invalid; Claude Code never advertises the tool

If the server returns a tool list with malformed JSON Schema, Claude Code may silently drop the tool. Calls then fall back to a similar-named tool that does not exist.

How to judge: After connecting, run claude --debug and look for “tool registered” or schema validation errors.

5. HTTP transport blocked by reverse proxy or firewall

If the MCP server is behind nginx or Cloudflare with chunked-transfer or SSE disabled, long-poll responses stall.

How to judge: Curl the same endpoint with --no-buffer and watch for incremental chunks. If curl hangs the same way Claude Code does, the proxy is the problem.

6. Wrong command in the MCP config

The command in your MCP config may launch the wrong binary or use a stale path. Claude Code spawns the process, the process exits immediately, and the call hangs waiting for a server that is not there.

How to judge: ps aux | grep mcp while Claude Code is running. The server process should be alive.

Before you start

  • Have the MCP server’s own logs accessible.
  • Know whether the transport is stdio or HTTP/SSE.
  • Be able to curl the server independently of Claude Code.
  • Save your MCP config file before tweaking.

Information to collect

  • Claude Code version: claude --version.
  • The MCP server config block from settings.json or mcp.json.
  • The server’s own version and log output during the hang.
  • Wire traffic, if HTTP: a curl with --no-buffer -v.
  • For stdio: stderr output of the spawned process.
  • claude --debug startup log filtered for mcp.

Step-by-step fix

Step 1: Confirm Claude Code actually launched the server

For stdio servers, run:

ps aux | grep -i mcp

You should see the server process spawned as a child of Claude Code. If absent, the command path or args are wrong.

Step 2: Inspect the MCP debug log

claude --debug 2>&1 | grep -i mcp

Look for initialize request and response, tools/list calls, and any error lines. The first failure point is usually obvious.

Step 3: For stdio, separate stdout from stderr in the server

The server must only write JSON-RPC frames to stdout. Move all logs to stderr:

node my-mcp-server.js 2> /tmp/mcp.log

Then tail /tmp/mcp.log while Claude Code starts.

Step 4: For HTTP, test the endpoint directly

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":{}}'

If curl also hangs, the issue is server- or proxy-side, not Claude Code.

Step 5: Validate JSON-RPC id pairing

In the server, log every inbound id and every outbound id. Confirm they match. A common bug: the server generates its own id instead of echoing the request’s.

Step 6: Validate tool schema

Pull the tool list:

curl -X POST https://mcp.example.com/rpc \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Run the returned schema through a JSON Schema validator. Fix any errors.

Step 7: Reduce timeouts and retry

Lower the server’s internal timeout to less than Claude Code’s MCP timeout (typically 30s). Always return an explicit error if the tool cannot complete in time, instead of holding the connection open.

Verify

  • A simple MCP tool call (list, ping, status) returns in under a second.
  • Server logs show paired request/response with matching ids.
  • claude --debug shows the tool registered and called successfully.
  • Repeated calls do not regress after several minutes.

Long-term prevention

  • Add a health check that runs once at MCP server startup and exits if initialize fails.
  • Send all server logs to stderr by default; never to stdout for stdio transports.
  • Echo the JSON-RPC id verbatim from the request to the response.
  • Pin the MCP server version in your config to avoid surprise schema changes.
  • Add integration tests that run the server in stdio mode and exchange a real initialize + tools/list round trip.

Common pitfalls

  • Using print() in the server for debug, which lands on stdout and breaks framing.
  • Adding a millisecond sleep “to be safe” that pushes total response over 30 seconds.
  • Forgetting that an HTTP MCP behind Cloudflare needs streaming enabled.
  • Writing two simultaneous responses for one request id; Claude Code only consumes the first.
  • Configuring the server twice (once user-global, once project) and connecting to the wrong instance.

FAQ

  • What is the default MCP timeout in Claude Code? Around 30 seconds per call, with a few seconds for initialize. Long-running tools should return a job id and let Claude Code poll.
  • Can I increase the timeout? Some versions expose an MCP timeout in settings.json. Check your release notes.
  • Does stdio or HTTP transport perform better? Stdio has lower per-call overhead; HTTP scales for multiple clients. Pick based on use case.
  • Why does curl work but Claude Code does not? Almost always wrong framing (stdio) or response shape (HTTP). Capture both wire formats and compare.
  • Can I debug MCP without Claude Code? Yes — use the @modelcontextprotocol/inspector tool to talk to your server directly.
  • Will the call retry automatically? Some calls retry once. Beyond that you must re-prompt.

Tags: #Claude Code #mcp #Troubleshooting #Debug