CLAUDE LABJP
SUNSET — The legacy Workbench and experimental prompt tool APIs retire tomorrow, August 17, one day outLATEST — Version 2.1.233, released August 15, is current: GitLab merge request URLs now work with --worktree and the claude agents view, where MRs appear as !NSECURITY — Windows paths written with the NT \??\ device prefix no longer bypass UNC validation, closing an NTLM credential-leak vectorTODO — Todo and task tracking tools are off by default on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, and newer models; set CLAUDE_CODE_ENABLE_TODO_TOOLS=1 to bring them backFORK — Version 2.1.232, released August 14, makes subagent_type: 'fork' the default, so a forked subagent inherits the full conversation and prompt cacheMENTION — Typing @ in the prompt now mentions another Claude session by name, and SendMessage reaches that session directlySUNSET — The legacy Workbench and experimental prompt tool APIs retire tomorrow, August 17, one day outLATEST — Version 2.1.233, released August 15, is current: GitLab merge request URLs now work with --worktree and the claude agents view, where MRs appear as !NSECURITY — Windows paths written with the NT \??\ device prefix no longer bypass UNC validation, closing an NTLM credential-leak vectorTODO — Todo and task tracking tools are off by default on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, and newer models; set CLAUDE_CODE_ENABLE_TODO_TOOLS=1 to bring them backFORK — Version 2.1.232, released August 14, makes subagent_type: 'fork' the default, so a forked subagent inherits the full conversation and prompt cacheMENTION — Typing @ in the prompt now mentions another Claude session by name, and SendMessage reaches that session directly
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-code129mcp19timeout10configuration7reliability17

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-06-25
The Day a Non-Responding MCP Call Swallowed an Entire Unattended Run — Owning the Stop With Your Own Deadline
When a remote MCP tool call stops responding, an unattended scheduled run just keeps waiting. Instead of leaving the cutoff entirely to the platform, here is how I designed my own deadline and a per-connector circuit breaker to own the stop — with working code.
Claude Code2026-07-17
The Morning My Table Ended in "… 2,847 more rows" — Separating Render Caps from Token Cost in Tool Output
Claude Code 2.1.209 caps markdown tables at 200 rows plus a remainder count. Only the rendering is capped — the model still receives every row. Here is how to measure the gap and redesign tool output around aggregates.
Claude Code2026-07-15
Answering auto mode's confirmation prompts in headless runs — a deny-by-default permission-prompt-tool
auto mode's confirmation step is a friend when you're at the keyboard, but in an unattended midnight run it becomes the reason a job sits waiting until morning. Here is how I catch those prompts with permission-prompt-tool, decide deny-by-default, and log every ruling — with working code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →