[BUG] MCP per-server timeout not enforced on individual tool calls (stdio hangs indefinitely)

Resolved 💬 4 comments Opened Apr 26, 2026 by dksmith01 Closed May 23, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The per-server timeout field in MCP config is not enforced on individual tool calls. When an MCP server accepts the connection but a tool call hangs (e.g., the server blocks internally due to a validation error), the call blocks indefinitely — 10+ minutes in my case — with no timeout killing it.

This is distinct from #16837 and #43299, which are about startup/connection timeout being capped at 60s. This bug is about tool call timeout after a successful connection: the server is running, initialize completed, but a subsequent tool invocation never returns and Claude Code never kills it.

What Should Happen?

The per-server timeout value should apply to individual tool calls, not just server startup. If a tool call exceeds the configured timeout, Claude Code should kill the call and return an error to the model so it can retry or fall back.

Steps to Reproduce

  1. Configure an MCP server (stdio transport) with timeout: 30000 (30s):
{
  "mcpServers": {
    "example": {
      "command": "cmd",
      "args": ["/c", "obsidian-mcp", "C:\path\to\vault"],
      "timeout": 30000
    }
  }
}
  1. Cause the server to hang on a tool call (in my case: obsidian-mcp hangs indefinitely when the vault path has a casing mismatch on Windows — it passes initialize but blocks on any tool call)
  1. Invoke any tool (e.g., list-available-vaults)
  1. Observe: the call hangs for 10+ minutes. The 30s timeout is never enforced. Manual cancellation (Escape) is the only way to recover.

Observed Behavior

[tool call starts]
... 10+ minutes pass, no timeout, no error ...
[user manually cancels]

The debug log shows the connection started with the correct timeout, but no timeout fires on the tool call itself.

Expected Behavior

After 30 seconds (the configured timeout), the tool call should be killed and an error returned:

MCP error: Tool call to "list-available-vaults" timed out after 30000ms

Root Cause Hypothesis

Based on #43299's analysis, the @modelcontextprotocol/sdk Client's requestTimeout defaults to 60s and only applies to the connect() handshake. There appears to be no per-call timeout on client.callTool(). The outer timeout wrapper in Claude Code's MCP config may only gate the initial connection, not subsequent tool invocations.

Impact

When an MCP server hangs on a tool call, the entire Claude Code session is blocked. The user must manually cancel and often restart the session. This makes MCP servers a reliability risk — a single bad call can freeze the session with no automatic recovery.

Environment

  • Claude Code version: 2.1.113
  • Platform: Windows 11 (stdio transport)
  • MCP server: obsidian-mcp v1.0.6
  • Shell: Git Bash

Additional Context

  • The server process itself was running (healthy node.exe count)
  • The hang was caused by a vault path casing mismatch — the server silently blocked instead of returning an error
  • MCP_TIMEOUT env var (set to 30000) was also not enforced
  • Related but distinct: #16837 (startup timeout capped at 60s), #43299 (MCP_TIMEOUT env var ineffective for startup)

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗