anthropics / anthropics/claude-code
[BUG] Severe fixed-cost latency in Claude Desktop's tool-calling layer
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
### Preflight Checklist
- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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?
I have tried lots of things. I give up.
Claude code has produced this bug report:
# Severe fixed-cost latency in Claude Desktop's tool-calling layer
## Summary
Every tool call observed in a Claude Desktop "Code" tab session carried a **~5-10 second fixed latency floor**, regardless of what the tool actually did — confirmed for MCP tool calls (both bridged via `mcp-remote` and via direct Streamable-HTTP transport) and for a built-in tool (`Glob`) doing zero network I/O. On top of that floor, write operations requiring the model to author content inline show an **additional ~5 ms/byte cost** that scales with content size.
Net effect measured on a real product surface (Cowork): writing a ~14KB text file took **85.40 seconds** end-to-end. The equivalent raw operation against the same backend, bypassing Claude's tool-calling layer, took **under 300 milliseconds**. That is roughly **250-300x overhead**, and it appears unrelated to the specific MCP server, the network, or the user's environment.
## Environment
- Claude Desktop, "Code" tab (Claude Agent SDK-based session)
- A custom internal MCP server ("fs-mcp"): Python + FastMCP, exposing filesystem read/write over Streamable HTTP on a home LAN, no auth, no TLS (trusted local network)
- Default config at the time of testing: bridged via `npx -y mcp-remote --allow-http` (a stdio-to-HTTP proxy process) per `claude_desktop_config.json`
- Notably, fs-mcp itself was purpose-built to *fix* a prior performance problem: per its own module docstring, it replaced an earlier SMB-based file access setup that had proven unreliable ("dropped sessions mid-write, slow whole-file transfers"). The latency documented in this report is unrelated to SMB — it shows up specifically in the MCP tool-calling layer that was introduced to get *away* from that earlier problem, i.e. this is a second, independent performance issue on top of one that had already been solved once.
## Methodology
Every operation was bracketed with `date +%s%3N` (millisecond-resolution epoch time) immediately before and after, executed via the session's own Bash tool, to measure end-to-end wall-clock time as experienced within the session.
## How to reproduce
The MCP-specific numbers below depend on our own NAS/fs-mcp setup, which won't be available to whoever picks this up. The core finding does not — it reproduces with a plain built-in tool and no custom server at all:
1. Open a Claude Desktop "Code" tab (or the standalone `claude` CLI) in any project directory.
2. Ask Claude to time a trivial, zero-I/O built-in tool call: run `date +%s%3N` via Bash, call `Glob` with a simple pattern (e.g. `*.json`), then run `date +%s%3N` again, and report the millisecond difference.
3. Expected-if-healthy: well under a second — it's a local directory pattern match, no network, no disk work of consequence.
4. Observed instead: 8.58-11.87 s, repeatably, in both a long-running conversation and a freshly-opened empty one (tests 7 and 8 below).
To reproduce the MCP-specific part, repeat the same bracketing around a call to any Streamable-HTTP-transport MCP server's tool (self-hosted or otherwise) instead of `Glob`. Expect a similar multi-second floor through Claude, versus well under a second for a raw HTTP request to that same server made directly (bypassing Claude's tool-calling layer) — see tests 3, 4, and 6.
## Data
### 1. Reference data from a separate product surface ("Cowork"), provided by the user before this investigation started
| Nominal size | Actual bytes | Time |
|---|---|---|
| "1KB" | 1,020 | 13.97 s |
| "10KB" | 9,316 | 51.53 s |
| "20KB" | 14,178 | 85.40 s |
Fitting `t = overhead + K × bytes` to these three points: overhead ≈ 8-9 s, K ≈ 5.4 ms/byte.
### 2. `write_file` via `mcp-remote`-bridged fs-mcp, this session
| Actual bytes written | Time |
|---|---|
| 10 | 10.58 s |
| 920 | 44.36 s |
| 1,032 | 17.70 s |
| 1,654 | 18.94 s |
| 4,900 | 21.29 s |
| 6,950 | 20.69 s |
### 3. Control — `list_dir` via the same bridged fs-mcp, pre-whitelisted (zero approval clicks needed)
**6.11 s** — rules out human click-latency as the explanation for this call.
### 4. Control — same MCP server, raw `curl` directly to it (full JSON-RPC handshake by hand, bypassing Claude's tool-calling layer entirely)
| Operation | Time |
|---|---|
| `initialize` handshake | 576 ms |
| `notifications/initialized` | 322 ms |
| `tools/call` → `list_dir` | 503 ms |
| `tools/call` → `write_file` (1,020 bytes, byte-exact) | 260 ms |
### 5. Control — local file generation, no network at all (`head -c` via Bash)
| Size | Time |
|---|---|
| 1,020 B | 138 ms |
| 9,316 B | 140 ms |
| 14,178 B | 134 ms |
| 3 files, total | 879 ms |
### 6. Control — same fs-mcp server, reconfigured with **direct Streamable-HTTP transport** via a project-level `.mcp.json`, completely bypassing `mcp-remote`
| Call | Time |
|---|---|
| `list_dir`, first call (fresh approval needed) | 8.98 s |
| `list_dir`, second call (pre-whitelisted, zero clicks) | 5.16 s |
### 7. Control — built-in tool, zero MCP, zero network (`Glob`), same session, run immediately after test 6
| Call | Time |
|---|---|
| First | 11.87 s |
| Second | 8.58 s |
### 8. Control — brand-new conversation (empty context, freshly opened tab), same `list_dir` call via direct-HTTP fs-mcp
Both the approval-needed first call and the clean second call: **5-6 s each**, self-reported by the user running the same test independently.
## What we ruled out
- **fs-mcp server code itself** — reviewed the full source (Python/FastMCP, ~200 lines). `write_file` is a plain synchronous `Path.write_text`/`open(...).write()` call. No artificial delay, retry logic, locking, or anything else that could account for multi-second cost.
- **Network/NAS latency** — direct `curl` round-trips to the same server, same LAN: 260-576 ms.
- **Transport mechanism** — `mcp-remote` (stdio bridge) vs. direct Streamable HTTP: both showed the same ~5-9 s floor for equivalent, fully-whitelisted calls (tests 3 and 6).
- **Bridge process respawning** — confirmed via OS process inspection (`Get-CimInstance Win32_Process`) that the `mcp-remote` bridge processes are long-lived and were not respawned between calls.
- **Conversation/context length** — a brand-new, empty conversation showed the same ~5-6 s floor (test 8), ruling out "large accumulated context slows down each turn" as the primary explanation.
- **Human approval-click latency** — real and additive on a tool's *first* use in a session, but a clean ~5-10 s floor persists even for fully pre-whitelisted, zero-click calls (tests 3, 6-second-call, 7).
## What remains unexplained
After eliminating all of the above, a **~5-10 second fixed latency floor remains on every tool call — MCP or built-in — inside this Claude Desktop Code-tab session**, with no cause identifiable from the client side. Since a built-in tool with zero I/O (`Glob`) shows the same magnitude of delay as an MCP round-trip to a remote server, the floor appears to live somewhere in Claude Desktop's own tool-call dispatch/round-trip mechanism — something we have no visibility into from outside the client.
Separately, for any write operation where content must be authored inline by the model (required by the MCP tool schema — there is no local-path or binary-copy alternative available), there is an additional ~5 ms/byte cost that scales linearly with content size and dominates total time for anything beyond a few KB. As a related side effect, we also observed that model-authored long, homogeneous content cannot be guaranteed byte-exact — repeated attempts to write an exact 1,020-byte payload by hand produced 1,032-1,654 bytes; only content generated by a separate program (Node.js) and sent via raw HTTP was byte-exact on the first try.
## Impact
A single ~14KB documentation update took 85 seconds end-to-end for an operation that is, at the infrastructure level, sub-300-milliseconds — roughly 250-300x overhead. This makes routine file-editing workflows (updating notes, docs, config — a common Claude use case) disproportionately slow and disruptive, independent of any specific MCP server's design, the user's network, or file size in isolation.
## Suggested follow-up (needs Anthropic-side visibility we don't have)
- Instrument the tool-call dispatch path inside Claude Desktop end-to-end (model decision → client dispatch → local/remote execution → result return) to find where the ~5-10 s is actually spent.
- Check whether this floor is specific to Claude Desktop's "Code" tab, or also present in the standalone `claude` CLI, claude.ai, and Cowork.
- Consider whether large inline-content tool calls (e.g., an MCP `write_file`-style tool) could support a lower-overhead path that doesn't require the full content to pass through model-generated output tokens — the ~5 ms/byte cost is otherwise unavoidable by design.
### What Should Happen?
Thar Claude's tools work with low(er) latency and not spending 85 seconds internally to process/send 14kb of data to a mcp server that takes 0.5 secs when called with curl (exact same service/environ/time
### Error Messages/Logs
```shell
```
### Steps to Reproduce
see report above
### Claude Model
Not sure / Multiple models
### Is this a regression?
No, this never worked
### Last Working Version
_No response_
### Claude Code Version
1.24012.9 (03c61d)
### Platform
Anthropic API
### Operating System
Windows
### Terminal/Shell
PowerShell
### Additional Information
_No response_
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the timed built-in Glob reproduction using date +%s%3N, then compare it with the direct Streamable-HTTP calls and the .mcp.json and claude_desktop_config.json setups described here. Done means locating the Claude tool-dispatch stage responsible for the fixed delay and confirming the result across the listed transports or client entry points.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- powershell, python
- Domain
- devtools, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100