◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-07-14Advanced

My MCP Timeout Was Being Ignored, and Every Call Died at Exactly 60 Seconds — Reclaiming Per-Server request_timeout_ms

A longer MCP tool call kept dying at exactly 60 seconds, ignoring the wait I had set. The cause: a per-server request_timeout_ms in .mcp.json that was never read, silently falling back to the default. Here is the correct placement after the fix, how to back the value out from a tool's response profile, and how to verify the cutoff yourself — with working code.

claude-code132mcp20timeout10configuration10reliability18

✦ Premium Article

A nightly scheduled run was failing on one step with mechanical regularity. When I lined up the elapsed times from the log — 59.8s, 60.1s, 59.9s — the payload was different every time, but the moment of the cutoff was pinned to exactly 60 seconds. I was certain I had set a longer wait for that MCP server. No matter how many times I reread my config, the number was right. Yet what was actually in effect was not the value I wrote, but a default of 60 seconds living somewhere else.

The July 14 update explained the unease. A per-server request_timeout_ms written in --mcp-config or .mcp.json was not being read, and it quietly fell back to the global default. The fix landed. But what the fix gives you is only the premise that "the value you wrote will finally take effect" — not a decision about which value belongs where, or whether it is genuinely working. Today is my record of redesigning exactly that, anchored to how I run things at night as a personal developer.

The question of not making a run wait on a non-responding call at all, I covered in the day a non-responding MCP call swallowed an unattended run. This piece stays one layer earlier: whether the wait value itself takes effect as configured.

An exact 60 seconds was not "the value I set" but "a default that got picked up"

Timeout incidents are treacherous because the cutoff itself looks like normal behavior. A heavy tool can exceed its wait, and being cut off then is arguably by design. So it is easy to shrug it off as "the tool just happened to be slow today."

But when the cutoff time clusters at nearly the same value every run, the story changes. Tool load varies day to day, so if only the cutoff stays constant, that is evidence of a fixed ceiling rather than load. In my case, that ceiling was not the value I had written — it was the default 60 seconds.

Some background. The wait time for an MCP tool call has several layers. One is the time to wait for the server to start; another is the time to wait for an individual tool to execute. The latter has a global default, which you can raise wholesale through the MCP_TOOL_TIMEOUT environment variable, and override per server with request_timeout_ms inside the server's definition in .mcp.json. The bug was that this per-server override was skipped, always falling back to the global default.

"Silently falling back to the default" is the hardest thing to catch

If a misconfiguration surfaced as an error, it would be easier. If startup told me "that value is invalid," I would notice on the spot. What made this case hard was that the ignored setting produced no warning at all — it simply ran on the default value as if nothing were wrong.

In unattended runs, that silence bites especially hard. Interactively you feel it — "huh, that cut off sooner than I expected" — but a scheduled log is read after the fact, and only the bare fact of the cutoff remains. Unless you deliberately record elapsed time, you cannot tell whether it was a configured cutoff or an unread default.

So the first move was not to fix the value, but to make the cutoff distribution visible. Line up the cutoff times for the same step across several runs and see whether they collapse to a single point. If they do, that is a sign the ceiling, not the load, is in charge. Whether or not you take this one small step changes how quickly you can self-diagnose whether a setting is working.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Recognize the bug where a per-server request_timeout_ms is ignored and falls back to the 60-second default, and confirm from your own log distribution whether the configured value is actually taking effect
✦Place per-server timeouts correctly in .mcp.json and understand their precedence over global defaults like MCP_TOOL_TIMEOUT, so long-running tool calls stay stable
✦Back timeout values out from each tool's response profile with a decision table, and drop in a lightweight checker that flags cutoffs pinned to the default — ready for unattended nightly runs
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-03-24
How to Automate Game Development with Claude Code × unity-mcp — A Complete Workflow from Concept to Release
Learn how to combine Claude Code with unity-mcp to automate Unity game development. The hcg-workflows skill set provides an 8-phase workflow from planning to deployment.
⟐ Claude Code2026-09-11
inferenceGatewayHeaders still works today. At noon on October 7 it falls back to a default
I read the deprecation notice that started appearing on September 10 as a spelling cleanup. After the cut-off, the old names are not ignored — they fall back to a fail-closed value or a default. Here is the old-to-new map and a script that audits your config in one pass.
⟐ Claude Code2026-08-28
A small script that catches invisible characters in plugin names before you install
Control characters and zero-width characters hiding in plugin or skill names are invisible in a terminal listing. I built six deliberately confusing names, compared what ls actually shows, and wrote a short check you can run before installing anything — plus an honest note on what that check misses.