claude-code - 💡(How to fix) Fix [BUG] Cowork MCP client times out long-running tool calls despite server progress notifications [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#58687Fetched 2026-05-14 03:42:04
View on GitHub
Comments
2
Participants
2
Timeline
7
Reactions
0
Author
Timeline (top)
labeled ×5commented ×2

Error Message

Symptom (user-visible error from host):

MCP error -32001: Request timed out

Reproducible via stdio tee shim wrapping mcp-remote between Claude Code host and remote MCP server. All internal identifiers redacted.

=== Outbound JSON-RPC from host (in.jsonl) ===

{"method":"initialize","params":{"protocolVersion":"2025-11-25", "capabilities":{"roots":{"listChanged":true}, "extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}}, "clientInfo":{"name":"local-agent-mode-<server-id>","version":"1.0.0"}}, "jsonrpc":"2.0","id":0}

{"method":"notifications/initialized","jsonrpc":"2.0"} {"method":"tools/list","jsonrpc":"2.0","id":1} {"method":"notifications/roots/list_changed","jsonrpc":"2.0"}

{"method":"tools/call", "params":{"name":"<tool-name>","arguments":{"message":"<REDACTED>"}}, "jsonrpc":"2.0","id":2} ^^^ Note: params._meta is ABSENT. No progressToken supplied.

{"jsonrpc":"2.0","method":"notifications/cancelled", "params":{"requestId":2, "reason":"McpError: MCP error -32001: Request timed out"}}

=== Inbound JSON-RPC to host (out.jsonl) ===

{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26", ...}} {"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}

{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text", "text":"<REDACTED — valid full response payload>"}]}} ^^^ Note: the result for the cancelled request id arrived intact AFTER the host issued notifications/cancelled. Host discarded it silently.

=== mcp-remote bridge stderr (err.log) ===

[pid] Connected to remote server using StreamableHTTPClientTransport [pid] Local STDIO server running [pid] Proxy established successfully between local STDIO and remote StreamableHTTPClientTransport [pid] [Local→Remote] initialize [pid] [Remote→Local] 0 [pid] [Local→Remote] notifications/initialized [pid] [Local→Remote] tools/list [pid] [Remote→Local] 1 [pid] [Local→Remote] notifications/roots/list_changed [pid] [Local→Remote] tools/call [pid] [Local→Remote] notifications/cancelled [pid] [Remote→Local] 2 [pid] Shutting down...

=== Independent verification: server emits compliant progress ===

Direct curl against the same MCP endpoint with an explicit progressToken streams notifications/progress every ~5s with the matching token, interleaved with SSE ": keepalive" comments. Sample frames:

data: {"jsonrpc":"2.0","method":"notifications/progress", "params":{"progressToken":"probe-1","progress":1, "message":"Processing your request..."}}

: keepalive

data: {"jsonrpc":"2.0","method":"notifications/progress", "params":{"progressToken":"probe-1","progress":2, "message":"Still working on it, please wait..."}}

...

Server completes well within an extended client timeout (e.g. curl --max-time 300). Confirms the transport, ingress, LB, and server are healthy — defect is exclusively in the host's invocation of client.callTool (no onprogress / no resetTimeoutOnProgress).

=== Versions ===

  • mcp-remote: 0.1.37
  • Transport: StreamableHTTPClientTransport, http-first strategy
  • MCP protocol negotiated: 2025-03-26 (server) vs 2025-11-25 (client advertised)
  • Host: Claude Code (via Cowork local-agent-mode wrapper; same code path)

Code Example

Symptom (user-visible error from host):

  MCP error -32001: Request timed out


Reproducible via stdio tee shim wrapping mcp-remote between Claude Code
host and remote MCP server. All internal identifiers redacted.


=== Outbound JSON-RPC from host (in.jsonl) ===

{"method":"initialize","params":{"protocolVersion":"2025-11-25",
 "capabilities":{"roots":{"listChanged":true},
 "extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}},
 "clientInfo":{"name":"local-agent-mode-<server-id>","version":"1.0.0"}},
 "jsonrpc":"2.0","id":0}

{"method":"notifications/initialized","jsonrpc":"2.0"}
{"method":"tools/list","jsonrpc":"2.0","id":1}
{"method":"notifications/roots/list_changed","jsonrpc":"2.0"}

{"method":"tools/call",
 "params":{"name":"<tool-name>","arguments":{"message":"<REDACTED>"}},
 "jsonrpc":"2.0","id":2}
 ^^^ Note: params._meta is ABSENT. No progressToken supplied.

{"jsonrpc":"2.0","method":"notifications/cancelled",
 "params":{"requestId":2,
           "reason":"McpError: MCP error -32001: Request timed out"}}


=== Inbound JSON-RPC to host (out.jsonl) ===

{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26", ...}}
{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}

{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text",
 "text":"<REDACTED — valid full response payload>"}]}}
 ^^^ Note: the result for the cancelled request id arrived intact AFTER
     the host issued notifications/cancelled. Host discarded it silently.


=== mcp-remote bridge stderr (err.log) ===

[pid] Connected to remote server using StreamableHTTPClientTransport
[pid] Local STDIO server running
[pid] Proxy established successfully between local STDIO and remote
      StreamableHTTPClientTransport
[pid] [Local→Remote] initialize
[pid] [Remote→Local] 0
[pid] [Local→Remote] notifications/initialized
[pid] [Local→Remote] tools/list
[pid] [Remote→Local] 1
[pid] [Local→Remote] notifications/roots/list_changed
[pid] [Local→Remote] tools/call
[pid] [Local→Remote] notifications/cancelled
[pid] [Remote→Local] 2
[pid]
Shutting down...


=== Independent verification: server emits compliant progress ===

Direct curl against the same MCP endpoint with an explicit progressToken
streams notifications/progress every ~5s with the matching token,
interleaved with SSE ": keepalive" comments. Sample frames:

  data: {"jsonrpc":"2.0","method":"notifications/progress",
         "params":{"progressToken":"probe-1","progress":1,
                   "message":"Processing your request..."}}

  : keepalive

  data: {"jsonrpc":"2.0","method":"notifications/progress",
         "params":{"progressToken":"probe-1","progress":2,
                   "message":"Still working on it, please wait..."}}

  ...

Server completes well within an extended client timeout (e.g.
`curl --max-time 300`). Confirms the transport, ingress, LB, and server
are healthy — defect is exclusively in the host's invocation of
client.callTool (no onprogress / no resetTimeoutOnProgress).


=== Versions ===

- mcp-remote: 0.1.37
- Transport: StreamableHTTPClientTransport, http-first strategy
- MCP protocol negotiated: 2025-03-26 (server) vs 2025-11-25 (client advertised)
- Host: Claude Code (via Cowork local-agent-mode wrapper; same code path)

---

#!/usr/bin/env bash
   set -uo pipefail
   LOG_DIR="$HOME/mcp-debug"; mkdir -p "$LOG_DIR"
   TS=$(date +%Y%m%d-%H%M%S)-$$
   exec tee -a "$LOG_DIR/in-$TS.jsonl" \
     | npx -y mcp-remote@latest "$@" 2>>"$LOG_DIR/err-$TS.log" \
     | tee -a "$LOG_DIR/out-$TS.jsonl"
RAW_BUFFERClick to expand / collapse

Preflight Checklist

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

What's Wrong?

cowork-mcp-progress-timeout-bug.md

What Should Happen?

When Claude Code (or any host built on it, including Cowork's local-agent mode) invokes an MCP tools/call against a spec-compliant server that emits notifications/progress, the request should remain alive as long as the server is making progress — exactly as the MCP spec and the @modelcontextprotocol/sdk Client are designed to support.

Specifically:

  1. The MCP client should call client.callTool(params, schema, options) with onprogress and resetTimeoutOnProgress: true populated in options. This causes the SDK to (a) attach a _meta.progressToken to the outbound tools/call request, and (b) reset the per-request timer each time a correlated notifications/progress arrives.

  2. The MCP_TOOL_TIMEOUT env var (already documented for Claude Code) should set the idle timeout between progress notifications, not a hard wall-clock cap on the total call. A separate maxTotalTimeout knob (also supported by the SDK) can serve as the optional safety net.

  3. When the host does time out and emits notifications/cancelled, any result that subsequently arrives for the cancelled id over the transport should be surfaced as a warning in debug logs at minimum — today it is silently discarded, masking the fact that the work actually completed.

Net effect for users: long-running MCP tools (LLM-backed servers, large analytics queries, agentic loops) work correctly out of the box, with no need to hack around the host's accounting via async polling patterns or custom timeout env vars.

Error Messages/Logs

Symptom (user-visible error from host):

  MCP error -32001: Request timed out


Reproducible via stdio tee shim wrapping mcp-remote between Claude Code
host and remote MCP server. All internal identifiers redacted.


=== Outbound JSON-RPC from host (in.jsonl) ===

{"method":"initialize","params":{"protocolVersion":"2025-11-25",
 "capabilities":{"roots":{"listChanged":true},
 "extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}},
 "clientInfo":{"name":"local-agent-mode-<server-id>","version":"1.0.0"}},
 "jsonrpc":"2.0","id":0}

{"method":"notifications/initialized","jsonrpc":"2.0"}
{"method":"tools/list","jsonrpc":"2.0","id":1}
{"method":"notifications/roots/list_changed","jsonrpc":"2.0"}

{"method":"tools/call",
 "params":{"name":"<tool-name>","arguments":{"message":"<REDACTED>"}},
 "jsonrpc":"2.0","id":2}
 ^^^ Note: params._meta is ABSENT. No progressToken supplied.

{"jsonrpc":"2.0","method":"notifications/cancelled",
 "params":{"requestId":2,
           "reason":"McpError: MCP error -32001: Request timed out"}}


=== Inbound JSON-RPC to host (out.jsonl) ===

{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26", ...}}
{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}

{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text",
 "text":"<REDACTED — valid full response payload>"}]}}
 ^^^ Note: the result for the cancelled request id arrived intact AFTER
     the host issued notifications/cancelled. Host discarded it silently.


=== mcp-remote bridge stderr (err.log) ===

[pid] Connected to remote server using StreamableHTTPClientTransport
[pid] Local STDIO server running
[pid] Proxy established successfully between local STDIO and remote
      StreamableHTTPClientTransport
[pid] [Local→Remote] initialize
[pid] [Remote→Local] 0
[pid] [Local→Remote] notifications/initialized
[pid] [Local→Remote] tools/list
[pid] [Remote→Local] 1
[pid] [Local→Remote] notifications/roots/list_changed
[pid] [Local→Remote] tools/call
[pid] [Local→Remote] notifications/cancelled
[pid] [Remote→Local] 2
[pid]
Shutting down...


=== Independent verification: server emits compliant progress ===

Direct curl against the same MCP endpoint with an explicit progressToken
streams notifications/progress every ~5s with the matching token,
interleaved with SSE ": keepalive" comments. Sample frames:

  data: {"jsonrpc":"2.0","method":"notifications/progress",
         "params":{"progressToken":"probe-1","progress":1,
                   "message":"Processing your request..."}}

  : keepalive

  data: {"jsonrpc":"2.0","method":"notifications/progress",
         "params":{"progressToken":"probe-1","progress":2,
                   "message":"Still working on it, please wait..."}}

  ...

Server completes well within an extended client timeout (e.g.
`curl --max-time 300`). Confirms the transport, ingress, LB, and server
are healthy — defect is exclusively in the host's invocation of
client.callTool (no onprogress / no resetTimeoutOnProgress).


=== Versions ===

- mcp-remote: 0.1.37
- Transport: StreamableHTTPClientTransport, http-first strategy
- MCP protocol negotiated: 2025-03-26 (server) vs 2025-11-25 (client advertised)
- Host: Claude Code (via Cowork local-agent-mode wrapper; same code path)

Steps to Reproduce

  1. Configure any remote MCP server in Cowork that has at least one tool whose execution typically exceeds 30s (e.g., an LLM-backed send_message-style tool that fans out to internal sub-tools).

  2. Ensure the server emits notifications/progress with the matching progressToken and proper SSE framing — verifiable independently via a direct curl against the server endpoint with _meta.progressToken set in the tools/call request.

  3. Wrap the mcp-remote invocation with a stdio tee shim so both directions of JSON-RPC are captured:

    #!/usr/bin/env bash
    set -uo pipefail
    LOG_DIR="$HOME/mcp-debug"; mkdir -p "$LOG_DIR"
    TS=$(date +%Y%m%d-%H%M%S)-$$
    exec tee -a "$LOG_DIR/in-$TS.jsonl" \
      | npx -y mcp-remote@latest "$@" 2>>"$LOG_DIR/err-$TS.log" \
      | tee -a "$LOG_DIR/out-$TS.jsonl"
  4. In Cowork, invoke the slow tool. Observe MCP error -32001: Request timed out in the UI.

  5. Inspect the captured in-*.jsonl and out-*.jsonl.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.1.58 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING