Tool Call Timeouts Are Not Just One Thing: Four Failure Modes and Recovery Paths

Agents misreport four different tool call failures as timeouts, causing paid non-idempotent calls to be repeated; the fix is to retrieve results by id.
The article argues that Agent frameworks use "timeout" to mask at least four distinct tool call failures: client-side abort, server error, framework background task, and idle abort. In client-abort and idle-abort cases, the task is usually already completed and paid for on the server, so retrying because of "timeout" causes duplicate charges on non-idempotent calls. The article emphasizes that the limit is on the client, not the call, and presents the correct primitive—retrieval—as read-only, free, idempotent, and credential-free, recommending it be designed as a tool and tested in advance. It also covers timeout config differences across Claude Code, Cline, and others, noting that the idle window is the real ceiling for slow tools and that per-server timeout also serves as the idle timeout lower bound.
"Timeout" is the word Agent frameworks use most often when reporting a failed tool call, but it masks at least four completely different failures. The most dangerous of these is precisely the one that gets mistakenly treated as a timeout, causing paid, non-idempotent calls to be executed repeatedly.

Why "Timeout" Is a Misleading Word
Every Agent framework sets an impassable time ceiling for tool calls. When a long call hits this ceiling, the framework throws out just one word—timeout—and the Agent typically does one thing: call it again.
If the tool is paid and non-idempotent, this is the worst possible response, because the first call has usually already completed. The original author maintains a decision endpoint that by design requires 180–300 seconds. This long call is intentional, and no parameter in the tool schema can shorten it: the limit lives on the client, not in the call itself.
Because of this, the same failure arrives in four different shapes, each requiring a different response strategy.
MCP (Model Context Protocol) is an open protocol proposed by Anthropic at the end of 2024, used to let AI applications (hosts) connect to external tools and data sources in a unified way. It adopts a client–server architecture: MCP hosts (such as Claude Code, Cline, Cursor) embed an MCP client, communicating with MCP servers over transports like stdio or HTTP/SSE. A tool call is the client sending a tools/call request to the server. Understanding this is key: the timeout timer runs on the client side, and the server merely passively receives requests and returns results. Therefore, the server cannot "loosen" the client's limit through parameters—this is the protocol-layer reason behind the article's repeated emphasis that "the limit lives on the client, not in the call itself."
Four "Timeouts," Four Levels of Information
The decision table given in the original breaks down the four cases clearly:
- Client actively aborts the call (hitting its own limit): The task has usually already finished running on the server. Here you should not retry; the correct approach is to retrieve the result by id; if you can't get the id, list the tasks created by the current credential.
- Server returns an error result (
isError: true): The task was rejected or errored during execution. Whether to retry depends on the HTTP status code in the error block, not on the word "timeout" itself. - Framework converts the call into a background task and returns a task id: The task is indeed executing. Do not retry; wait for task notification or retrieve by id.
- Idle abort: The call sent nothing during the idle window. The task has usually completed, and again you should not retry; retrieve by id.
Of these four, the first two look like timeouts but aren't. The third isn't a failure at all but the framework's polite handling. And the first is the most expensive: the answer already exists and has already been paid for, yet the Agent calls again because of the word "timeout."
Idempotency means that performing the same operation once versus multiple times produces the same effect on system state. Query and read operations are naturally idempotent, with no side effects on repeated execution; while operations like payments, order placement, task creation, and message sending are typically non-idempotent—each execution produces a real side effect (a charge, a new task, a sent message). In distributed systems, when the caller cannot confirm whether the request has been processed by the server, blindly retrying non-idempotent operations is a classic anti-pattern that can lead to duplicate orders or duplicate charges. The industry's usual solution is to assign a unique idempotency key to each request, which the server uses to deduplicate; the "retrieve by id" proposed in this article essentially lets the client query results via the task id, thereby safely avoiding retries in non-idempotent scenarios.
How Clients Actually Differ
Timeout handling is not a transport-layer behavior, so the MCP spec can make no promises here. The original author compiled the following differences from each client's own documentation:
- Claude Code: Use
MCP_TIMEOUT(in milliseconds) for startup/connection; use the per-servertimeoutfield in.mcp.json(in milliseconds) for each tool call, which overridesMCP_TOOL_TIMEOUT. When unset, it's about 28 hours; the idle window is 5 minutes for HTTP/SSE and 30 minutes for stdio, adjustable viaCLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT, which when set to0disables it. Additionally, calls running longer than 2 minutes are moved to background tasks and reported via task notification. - Cline: Configure with the
timeoutfield incline_mcp_settings.json, in seconds. There has historically been a bug (issue #2296) where calls could die around 60 seconds regardless of the setting. - Cursor / Windsurf / VS Code: No related configuration is documented in the MCP page. The original advises treating transport/client defaults as unknown—don't tune what you can't see.
Two points here matter more than the table itself.
First, it's often not the wall-clock limit that actually fires. On HTTP connections, Claude Code also runs a per-request timer that starts at the greater of 60 seconds, the server tool timeout, and MCP_TIMEOUT—but this timer stops the moment the first response byte arrives. A server that thinks for five minutes before answering is effectively racing the idle window, not the wall-clock window.
Second, the idle window is what punishes "slow but correct" tools. A tool call that sends no response or progress notification during the idle window will be aborted, no matter how generous the per-call limit. If your tool takes minutes and sends nothing in between, the real ceiling is the idle window—5 minutes on Claude Code over HTTP—and you may never have seen that number, because it's written on the page right next to the timeout config you were looking for.
The mitigation is cheap and in the same documentation: when the per-server timeout is set to at least 1000 milliseconds, it also serves as the lower bound for the idle timeout. So the actual config for long calls isn't "crank the big number higher"—it's giving the corresponding server a sufficiently large timeout value (e.g., 600000 milliseconds). Note that in the idle comparison this value is the smaller of the two numbers, so read the client's own MCP page before copying numbers.
The difference between idle and wall-clock timeouts is key to understanding this article. A wall-clock timeout starts counting from when the request is sent, regardless of whether data flows in between; an idle timeout only counts during the silence between two data flows, resetting the moment any response byte or progress notification arrives. For a server that "thinks for a long time but will eventually answer," the wall-clock window may already be exhausted, while the idle window is the true arbiter. In addition, the stdio transport communicates between local processes via standard input/output, with low latency but constrained by the host process lifecycle; the HTTP/SSE transport goes over the network, and SSE (Server-Sent Events) allows the server to push streaming data unidirectionally, naturally capable of sending progress notifications mid-flight. These two transports usually differ in idle window settings, which is why Claude Code sets 5 minutes for HTTP/SSE and 30 minutes for stdio.
The Retrieval Path, and Where It Breaks
If the call was cut off after the server accepted it, the correct primitive is retrieval, not a second call. The original suggests making retrieval a tool rather than having the Agent do it with curl.
- With an id: Return task status, and once complete, return the payload the original call should have returned.
- Without an id: Return the list of recently created ids for that credential. This is the most critical case, because client aborts never give you an id.
- Read-only and free, so repeated calls are always safe. There is no reason for retrieval to be billed or to involve idempotency.
- Credentials stay on the host side. Within an MCP host, the Agent typically doesn't hold the API key, so doing retrieval directly inside the Agent with the vendor REST API yields a
401, while reading through the tool surface works fine. Putting the key in tool parameters pushes it into prompts and transcripts, which must be avoided.
Two server-side implementation details worth copying:
"Not yours" and "doesn't exist" must return exactly the same response. If they differ, the retrieval endpoint becomes an oracle for probing other tenants' task ids. Same status code, same body—and write this rule into the tool description, or a future maintainer will "fix" it into a vulnerability.
Explicitly declare the retention window and return empty lists as normal results. The original author's server retains ids for 7 days and gives identical answers for unknown ids and others' ids. An empty list is not an error, and there is no sentinel value of "id 0"; once you invent one, every client will eventually probe it.
Test the Retrieval Path Before Using It
This is the most-skipped step, and it takes twenty seconds: while everything is fine, call retrieval with no parameters.
- Returns
401orunauthorized: Retrieval is unreachable from where the Agent runs. Your recovery plan is just a box on a diagram, not a real capability—fix reachability before you need it. - Succeeds but returns an empty list: The credential is valid, the endpoint responds, and the empty window is handled correctly. This is exactly the state you want before the first cut-off.
- Succeeds and returns ids: Retrieval works end-to-end. Retrieve the oldest id and confirm the payload matches what the call should have returned.
Run it once in your own harness and record the result in the tool description, so the next Agent that reads the tool list knows whether it can retrieve or can only retry. The tool description is the only place a recovery strategy can reach the model without any code.
Design Takeaways
For any non-idempotent, paid operation, make retries impossible to select by accident:
- Return an id for every result, including successful ones. An id issued only on failure is an id nobody records.
- State the expected duration in the tool description—"budget 180–300 seconds" isn't marketing copy; it's the number the client needs to stop cutting off the call.
- Explain that the timeout is a client setting, not a parameter. An Agent that believes it can pass
timeout: 300will pass it forever, while actual behavior never changes. - Make retrieval read-only, free, idempotent, and credential-free from the Agent side.
- Write the "not yours" rule into the contract so answers remain uninformative to strangers.
The implementation run by the original author lives at https://mcp.turingcorp.net, and its tools/list is open by design, so the recovery contract—including the retrieval tool and the 180–300 second expectation—can be read without credentials.
One-line Summary
Agents misreport four different tool call failures as timeouts, causing paid non-idempotent calls to be executed repeatedly; the correct approach is to retrieve results by id.
Paragraph Summary
The article points out that Agent frameworks broadly use the word "timeout" to mask at least four tool call failures that are entirely different in nature: client-side abort, server returning an error, framework converting to a background task, and idle abort. In the client-abort and idle-abort cases, the task has usually already completed and been paid for on the server; retrying because of "timeout" causes duplicate charges for non-idempotent calls. The article stresses that the limit is on the client, not the call itself, and thus gives the correct primitive—retrieval: read-only, free, idempotent, credential-free—and recommends designing it as a tool and testing it in advance under normal conditions. It also surveys timeout configuration differences across clients like Claude Code and Cline, noting that the idle window is the true ceiling punishing slow tools, and that the per-server timeout also serves as the lower bound for the idle timeout.
Related articles

How Big Can Stars Really Get? The Physical Limits of the Largest Stars
Red supergiants are the largest stars by volume, with radii up to 2,000 times the Sun, engulfing the inner solar system. But there is no precise upper limit on stellar size—models predict up to 15,000 solar radii. The most massive blue stars are far smaller than red supergiants—biggest and heaviest are two different things.

Apple HomeView Smart Display Full Feature Breakdown: Wall-Mount or Desktop, $299
Apple launches HomeView, a $299 smart display with wall-mount and desktop options, 12MP camera for facial recognition, smart home control, music playback, and recipe step-by-step cooking. Full breakdown of watch faces, apps, privacy, and cooking integration.

Where Did the Promised New Studio Go? Craft Computing Answers After Five Years
Craft Computing's host responds to viewer questions: the new studio promised five years ago was shelved as construction costs soared from $30,000 to $75,000, and was replaced by a 168-square-foot garage conversion.