[BUG] HTTP MCP servers are absent from context on the first turn under deferred tool loading, and the server-side `alwaysLoad` `_meta` key does not compensate

Status Fixed / completed
Reported on v2.1.219
Maintainer reply None cached
Activity 1 comment · opened Jul 31, 2026 · closed Aug 19, 2026

Preflight Checklist

  • [x] I have searched existing issues 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?

With deferred MCP tool loading (ENABLE_TOOL_SEARCH=true, the default on 2.1.219), an HTTP MCP server is still connecting when the first turn of a session is composed. The model therefore has neither the server's tool names nor its server-level instructions string in context, and behaves as though the server does not exist.

The part that matters to an MCP server author: the server-side key that looks like it should fix this, _meta["anthropic/alwaysLoad"] on a tool definition, has no effect. A server cannot make itself visible on turn 1 no matter what it does; only the user's own client config can, via a per-server "alwaysLoad": true in .mcp.json.

For a single-turn invocation (claude -p, or the first message of an interactive session) this means a configured server is silently unavailable. There is no error, no warning, and nothing in the transcript that says a configured server was skipped.

Why this is more than a context-budget question

Measured as a proxy for real-world impact: how often the host actually invokes one of the server's tools on prompts where that tool is the relevant one. Same server, same 9 test prompts, single-turn sessions, only the config differing:

| Condition | tool called |
|---|---|
| deferred, no alwaysLoad | 0/9 |
| deferred + client alwaysLoad | 5/9 |
| eager (ENABLE_TOOL_SEARCH=false) | 0.59 (16/27, 3 repeats) |

The client flag alone recovers the whole eager rate, which says the deferral gap here is entirely the context problem rather than a ranking or retrieval problem. Once the server is in context, discovery works well: in an earlier 99-session run, whenever the model issued a ToolSearch at all it retrieved the relevant tool 22/23 times and called it 22/22 times. The failure is upstream of all of that: on 76 of those 99 sessions it never searched, because nothing in context suggested there was anything to search for.

The practical consequence for any MCP server whose tools are meant to be invoked proactively (because the model recognises the situation, not because the user names the tool) is that the server is inert for the first turn of every session unless each user adds a flag they have no reason to know about.

What Should Happen?

A server the user explicitly configured is either available on turn 1, or the model is told that a configured server exists and is still connecting, so it can wait or search rather than answer as if the server were absent.

Suggested fixes, in order of preference:

  1. Await MCP connection (or a short bounded timeout) before composing the first turn, at least for servers explicitly configured in the current session's --mcp-config / .mcp.json.
  2. Failing that, put the server's name and instructions in context immediately even while its schemas are still deferred. That is cheap in tokens and is enough for the model to know a search is worth issuing.
  3. Failing that, honour _meta["anthropic/alwaysLoad"] from the server, so a server author can opt in without depending on every user's config. If the key is intentionally ignored, documenting that would help: it currently reads as supported.
  4. Independently: surface "configured server not yet connected" to the model or the transcript, so this is observable rather than silent.

Error Messages/Logs

No error is emitted anywhere. That absence is part of the report: a configured
MCP server that is not ready on turn 1 produces no error, no warning, and no
transcript entry.

The only observable signal is a missing one. In the --output-format stream-json
output, the system/init event's "tools" array contains the built-in tools plus
"ToolSearch", and no "mcp__example__*" entries. The model then describes the
server as still connecting, and the server's "instructions" string (returned in
the initialize response) is likewise absent from context.

Steps to Reproduce

Any streamable-HTTP MCP server will do; no product-specific code or data is involved.

  1. Point a config at it (./mcp.json):
{
  "mcpServers": {
    "example": { "type": "http", "url": "http://localhost:8000/mcp" }
  }
}
  1. Ask the model, in a single turn, what it can see, without letting it call anything:
ENABLE_TOOL_SEARCH=true claude -p "List every tool you can see right now, from context only. Do not call any tool." \
  --mcp-config ./mcp.json --strict-mcp-config --output-format stream-json --verbose --setting-sources ""
  1. Observed: the system/init event's tools array contains the built-in tools and ToolSearch, and no mcp__example__* entries. The model reports the server as "still connecting". The server's instructions string (returned in the initialize response) is also not in context.
  1. Re-run the same command under each of these conditions:

| Condition | tool names in context | server instructions in context |
|---|---|---|
| ENABLE_TOOL_SEARCH=false (eager) | yes | yes |
| ENABLE_TOOL_SEARCH=true (deferred) | no | no |
| deferred + tool _meta["anthropic/alwaysLoad"] | no | no |
| deferred + .mcp.json per-server "alwaysLoad": true | yes | yes |

Only the client-side flag helps. Declaring alwaysLoad server-side instead, as _meta: {"anthropic/alwaysLoad": true} on the tool definition, is identical to the bare deferred case: no effect.

The server side was verified independently to be correct: the initialize response does carry instructions (readable via the Python SDK as ClientSession.initialize().instructions), and the server answers tools/list normally.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.219 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Non-interactive/CI environment

Additional Information

  • Server under test: streamable-HTTP MCP server (Python mcp SDK / FastMCP), stateless_http=True, no auth, on localhost.
  • ENABLE_TOOL_SEARCH was toggled explicitly in both directions. Note it is undocumented in claude --help, so the naming here may not match internal terminology.
  • Model: claude-opus-5[1m]. OS: macOS 15 (Darwin 25.5.0).
  • Possibly related but distinct: #25894 (MCP tools never appear as deferred at all, via an mcp-remote proxy), #11364 (lazy-load proposal), #6638 (dynamic load/unload during a session).
  • Reproduced on 2.1.219. If this is already fixed on a newer build, apologies for the noise, happy to re-verify.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗