[BUG] Session permanently dead-locks with "400 Tool reference 'WaitForMcpServers' not found" after an early tool search during MCP startup

Status Open
Reported on v2.1.217
Maintainer reply ✓ Yes — bcherny
Activity 4 comments · opened Jul 21, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

A session becomes permanently unusable: every user message returns the same API error and no assistant turn can complete.

API Error: 400 Tool reference 'WaitForMcpServers' not found in available tools

The failure is not recoverable from inside the session — continue, /doctor, and plain retries all reproduce the identical 400 instantly, because each request re-sends the same poisoned conversation history. The only escape is /clear (loses all context) or hand-editing the transcript JSONL.

Root cause, traced from the session transcript:

  1. Early in the session — while MCP servers were still connecting — the assistant issued a server-side deferred-tool search (tool_search_tool_regex). The resulting tool_search_tool_result was written into the transcript with a snapshot of the tool catalog as a list of tool_reference entries. That snapshot included WaitForMcpServers.
  2. WaitForMcpServers is a transient startup tool — it is only advertised in the request's tools while MCP servers are initializing, then dropped once they finish connecting.
  3. A tool_search_tool_result is immutable transcript history. On every subsequent request the API re-validates the entire conversation, finds a tool_reference to WaitForMcpServers that is no longer in the advertised tool set, and rejects the whole request with a 400. The session is poisoned for good.

The trigger is a race: the tool search must land inside the MCP-connect window for the transient tool to be captured. This makes MCP-heavy configs (multiple MCP servers/plugins) the most exposed, since they lengthen that window and often prompt an early tool search.

What Should Happen?

A completed session must never become permanently un-loadable. Either:

  • a tool_search_tool_result should not surface transient/session-lifecycle tools (like WaitForMcpServers) into a persisted result, or
  • on every request the harness should reconcile persisted tool_references against the current tool set — re-advertising or stripping now-absent tools so the API never sees a dangling reference.

Error Messages/Logs

API Error: 400 Tool reference 'WaitForMcpServers' not found in available tools

Steps to Reproduce

  1. Configure the session with one or more MCP servers (via plugins or .mcp.json) so MCP connection takes a moment at startup.
  2. Start a session and, early on (while MCP servers are still connecting), take an action that causes a deferred-tool search — e.g. a task that makes the assistant call tool_search_tool_regex.
  3. Observe the transcript: the resulting tool_search_tool_result records a tool_references list that includes WaitForMcpServers (and, notably, the full catalog — entries not matching the search regex; e.g. a pattern of bash|shell|execute|read|glob|grep|write|edit returned Agent, Artifact, WaitForMcpServers, etc.).
  4. Continue the session past MCP-connect completion (send any further message).
  5. Every subsequent turn fails with 400 Tool reference 'WaitForMcpServers' not found in available tools, permanently.

Confirming evidence in the JSONL (~/.claude/projects/<project>/<session-id>.jsonl): the tool_search_tool_result block's content.tool_references contains {"type":"tool_reference","tool_name":"WaitForMcpServers"}; the earliest turn containing it succeeds, and all later turns 400. Removing only that single reference from the block and resuming clears the error with full context intact — confirming that one dangling reference is the sole cause.

Environment

  • Claude Model: Not sure / Multiple models
  • Is this a regression?: I don't know
  • Claude Code Version: 2.1.217 (Claude Code)
  • Platform: Anthropic API
  • Operating System: macOS
  • Terminal/Shell: Terminal.app (macOS)

Additional Information

Workaround for an already-poisoned session (avoids losing context): with the session closed, edit its transcript JSONL to drop the WaitForMcpServers entry from the tool_search_tool_result's content.tool_references, then claude --resume <session-id>. /clear also clears it but discards conversation history.

Secondary observation worth checking: the tool search returned the entire tool catalog rather than only entries matching the supplied regex — surfacing transient tools that the query didn't ask for is what allowed the poison in.

View original on GitHub ↗

3 Comments

dkxnr49dtw-dev · 28 days ago

Reproduced deterministically on Claude Code 2.1.220, macOS 27.0, with two MCP
servers configured (one HTTP, one stdio). Adding some detail that may narrow it,
plus a recovery that preserves the session.

It is deterministic per boot, not intermittent. Two consecutive fresh
sessions in the same window, same project, same two MCP servers, both ended up
carrying the poisoned block, and both capped their context window shortly after
start. The trigger is not a rare race for us: if a server-side tool search fires
while the MCP servers are still connecting, the block lands, every time.

The poisoned block is much larger than the one name in the error. The error
names a single tool, so the obvious repair is to remove that one. That is not
enough. In our transcript the snapshot spanned 5 tool_search_tool_result
blocks holding 64 tool_reference entries
, 37 built-in and 27 MCP tools. The
built-in set included tools that are not in this client's advertised set at all:

WaitForMcpServers   EndConversation   DesignSync      PushNotification
ScheduleWakeup      ReportFindings    EnterWorktree   ExitWorktree
Artifact            Monitor           CronCreate      SendMessage

plus MCP tools from a server that was no longer connected. So any of these can be
the tool that 400s, depending on which one the API validates first. We removed
WaitForMcpServers, resumed, and the next /compact failed with
400 Tool reference 'EndConversation' not found in available tools. It is a
queue of dangling references, not a single bad entry.

Nothing ever called these tools. We checked every transcript on the machine:
there is no tool_use for WaitForMcpServers anywhere, in any session. The
references exist only inside the search-result snapshot, so they are a record of
what was advertised at that instant, not of anything the model did. That matters
for the fix: dropping them costs the conversation nothing.

Why the session cannot recover on its own. /compact re-sends the full
history, which still contains the block, so compaction fails with the same 400
that every other request fails with. The window fills and there is no path back.
Ours reached 760k tokens on a 200k window before we intervened. Auto-compact
cannot save it either, for the same reason.

Recovery that keeps the conversation. /clear works but discards the
session. Instead, with the session closed, strip every tool_reference entry
from the tool_search_tool_result blocks in
~/.claude/projects/<project-dir>/<session-id>.jsonl, then
claude --resume <session-id>. Strip all of them rather than the named one, for
the queue reason above. After doing that on a session that had been failing every
compact:

  • /compact ran to completion instead of returning 400 instantly
  • the PreCompact and PostCompact hooks both fired and completed
  • the context counter dropped off the full mark
  • the conversation came back intact

Line count unchanged, every line still valid JSON. So the rest of the transcript
is undamaged by the removal, which further suggests the references are inert
history rather than load-bearing structure.

Suggested fix, from the outside. Either exclude transient startup tools from
the catalogue snapshot written into tool_search_tool_result, or have the client
filter tool_reference entries against the currently advertised tool set when it
builds a request, rather than replaying the snapshot verbatim. The second also
covers references left behind when an MCP server disconnects mid-session, which
is the same failure with a different cause.

Happy to supply a redacted transcript fragment if that helps.

bcherny collaborator · 14 days ago

Thanks for the unusually detailed report — the transcript-level tracing made this much easier to test.

I tried to reproduce on 2.1.233 (Linux, Anthropic API) using a deliberately slow-connecting MCP server and forcing tool searches during the connect window:

  • The search never returned a reference to the transient "wait for MCP servers" tool. On the current release that tool and tool search aren't offered to the model at the same time, so a search result can't capture it.
  • Searches returned only tools matching the pattern, not the whole catalog — so the "full catalog was returned" observation didn't reproduce either.
  • I also hand-poisoned a saved session with a dangling reference to that tool and resumed it on 2.1.233. The session loaded and the turn completed: the stale reference is dropped before the request is sent, so no permanent 400.

So I can't reproduce the dead-lock on the current release — but I can't rule out the path you hit, since the search result you describe looks like one recorded by the API itself rather than by the local tool, and that variant isn't something I could trigger here.

Could you check whether it still happens on 2.1.233 or newer? If it does, please share the exact version, whether the session used a custom base URL or gateway, and the (redacted) shape of that block in the JSONL. Tagging needs-repro in the meantime.

🤖 Generated with Claude Code

github-actions[bot] · 14 days ago

We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.

Showing cached comments. Read the full discussion on GitHub ↗