[Bug?] `defer_loading` in `.claude.json` mcpServers has no effect
Summary
The defer_loading field in .claude.json's mcpServers configuration appears to have no effect on how MCP tools are loaded into context. Regardless of whether defer_loading is set to true or false, all MCP tools behave identically — loaded on-demand when ENABLE_TOOL_SEARCH=true, or loaded upfront when ENABLE_TOOL_SEARCH=false.
---
The silent kind of wrong: no error, no warning, and /context can't tell the difference
There are bugs that announce themselves with stack traces. This isn't one of them.
When defer_loading is set in .claude.json, Claude Code parses the field without complaint. No warning. No "unknown field" notice. No indication that the value will be ignored. The config file nods, moves on, and leaves the user with complete confidence that their setting is active.
Three things make this especially hard to catch:
1. Silent acceptance breeds false confidence.
A user writes defer_loading: false, sees no error, observes no unusual behavior, and reasonably concludes the setting is working. There's nothing to prompt further investigation.
2. /context offers no observable signal.
When ENABLE_TOOL_SEARCH=true, the /context output lists all MCP tools under "Available" (grayed out) — regardless of defer_loading value. A tool configured with defer_loading: false (which should appear as immediately loaded) is visually indistinguishable from one configured with defer_loading: true. The UI provides no way to verify whether the setting had any effect.
3. The inference from API docs is natural.
The defer_loading parameter is documented and functional at the Messages API level. A developer reading those docs and then writing .claude.json would reasonably assume the field maps through to the API. There's nothing in the Claude Code CLI docs to indicate otherwise.
The result: users configure defer_loading with genuine intent, see no errors, observe nothing unusual in /context, and walk away confident their configuration is doing something. It takes a controlled multi-session experiment — as documented below — to discover that it isn't.
---
Environment
- Claude Code version: 2.1.47
- Plan: Claude Max
- OS: Windows 10/11 (MSYS2 bash)
- Model: claude-sonnet-4-6
- ENABLE_TOOL_SEARCH: tested with both
trueandfalse
---
Configuration
.claude.json (simplified):
{
"mcpServers": {
"fetch": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-server-fetch"],
"defer_loading": false
},
"codex": {
"command": "codex",
"args": ["mcp-server"],
"defer_loading": true
}
}
}
The intent: fetch should be preloaded into context (defer_loading: false), while codex should be loaded on-demand (defer_loading: true).
---
Steps to Reproduce
- Set
ENABLE_TOOL_SEARCH=truein your environment - Configure two MCP servers in
.claude.json— one withdefer_loading: false, one withdefer_loading: true - Start a Claude Code session
- Run
/contextimmediately after session start (before invoking any tools) - Run
tasklist(Windows) orps aux(Linux/macOS) to check MCP server processes
---
Expected Behavior
defer_loading: false→ Tool definition loaded into context immediately at session start ("Loaded" in/context)defer_loading: true→ Tool available for search but not loaded until used ("Available" in/context)
Additionally, if defer_loading: true were a process-level signal, the OS process for that server might start lazily. However, even process-level deferral would be acceptable if context-level behavior differed.
---
Actual Behavior
Test A — /context output (with ENABLE_TOOL_SEARCH=true):
MCP tools · /mcp (loaded on-demand)
Loaded
└ mcp__codex__codex: 450 tokens ← after manually invoking this tool
Available
└ mcp__fetch__fetch ← defer_loading: false, but NOT preloaded
└ mcp__codex__codex-reply
└ mcp__claude-in-chrome__* (18 tools)
fetch has defer_loading: false but is not preloaded — it remains in "Available" (on-demand) state, identical to codex which has defer_loading: true.
Test B — OS process check (immediately after session start, before any tool call):
| Process | PID | Status |
|---------|-----|--------|
| uvx.exe (fetch server) | 21080 | Running ✅ |
| mcp-server-fetch.exe | 12128 | Running ✅ |
| codex.exe | 25688 | Running ✅ |
Both MCP server processes start immediately on session start, regardless of defer_loading value. So defer_loading: true does not defer the OS process either.
---
Evidence
Test C — 5-way ENABLE_TOOL_SEARCH × defer_loading matrix
Two independent sessions (08:41 and 09:12) produced identical results:
| ENABLE_TOOL_SEARCH | defer_loading | /context result | /mcp status |
|----------------------|-----------------|-------------------|---------------|
| false | (not set) | All tools preloaded | All connected |
| false | false | All tools preloaded | All connected |
| false | true | All tools preloaded | All connected |
| true | false | On-demand (Available) | All connected |
| true | true | On-demand (Available) | All connected |
Key finding: Changing defer_loading produces zero observable change in /context behavior. Only ENABLE_TOOL_SEARCH affects loading behavior.
---
Analysis
There appear to be two distinct defer_loading fields in the Claude ecosystem:
1. Anthropic Messages API — documented, functional:
Per the Tool Search Tool docs:
"Mark tools for on-demand loading by addingdefer_loading: true" "Tools withoutdefer_loadingare loaded into context immediately"
At the API level, defer_loading is a per-tool parameter in the tool definition object. The API also supports per-server granularity via mcp_toolset.configs:
{
"type": "mcp_toolset",
"mcp_server_name": "database-server",
"default_config": { "defer_loading": true },
"configs": {
"search_events": { "defer_loading": false }
}
}
2. .claude.json mcpServers — undocumented, appears to be no-op:
The Claude Code MCP docs list the supported mcpServers fields as: command, args, env, type, url, headers. defer_loading is not listed.
My hypothesis: defer_loading in .claude.json is a reserved field intended to expose the API's per-server defer_loading control in the CLI — but the mapping from .claude.json → API request has not yet been implemented. The field is accepted without error but silently ignored.
Layer diagram:
.claude.json (defer_loading) ──→ [not passed through?] ──→ API request
↑
ENABLE_TOOL_SEARCH=true ────────────────────────────────→ wraps all tools
with defer_loading: true
---
Question
- Is
defer_loadingin.claude.jsonmcpServers intended to work for per-server control of context loading? - Or is it a reserved field for future implementation?
- If it's planned: is there an ETA or tracking issue for surfacing the API's per-tool
defer_loadinginto the CLI config?
This would be extremely useful for users managing many MCP servers — some high-priority tools preloaded, others loaded on-demand — without having to rely solely on the all-or-nothing ENABLE_TOOL_SEARCH flag.
---
Related Issues
- #7336 — Lazy Loading for MCP Servers and Tools (👍 83 reactions)
- #12836 — Support Tool Search and Programmatic Tool Use betas
- #19445 — Deferred Loading for Task Agents and Skills
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗