Claude Code now lazy-loads MCP tool definitions by default. Here's what MCP Tool Search really does to your context window, and where it bites.
Open Claude Code in a project wired to a few MCP servers — GitHub, Postgres, Sentry, maybe a couple of internal ones — and run /context before you give a single instruction. On a loaded setup you'll watch 60–80K tokens already gone. Not on your code. Not on your prompt. On tool definitions: the JSON schemas for tools you may never call this session.
That was the quiet tax MCP imposed all through 2025. Every connected server injected every tool's full schema into the system prompt at startup. Forty tools here, sixty there, and a third of a 200K window vanished before Claude read your first line. MCP Tool Search is the fix, and in the recent Claude Code releases it's on by default. It's worth understanding exactly what it does, because it changes how you should write and organize servers — not just how much context you have left.
The old behavior was eager loading: connect a server, get all of its tool schemas in the prompt, full stop. Tool Search swaps that for lazy loading. Instead of every schema up front, Claude Code loads a lightweight index — tool names and one-line descriptions — and fetches the full definition only when it decides a tool is relevant to your request. Once loaded, a tool stays available for the rest of the session.
The trigger is automatic. Tool Search kicks in when your MCP tool descriptions would consume more than roughly 10% of the context window. Under that, eager loading is fine and nothing changes. Over it, Claude Code switches to the indexed mode without you doing anything. You can see the effect directly:
# Eager loading, 50+ MCP tools connected
/context
# ~77K tokens consumed before any work
# With Tool Search active
/context
# ~8.7K tokens — roughly 95% of that overhead reclaimed
That's not a rounded-up marketing number; it tracks with what people running several heavy servers at once report. Drop from 12K of startup tool tokens to a few hundred and you've bought yourself back most of a session's worth of working memory. Run /doctor for a per-server token breakdown if you want to see which connector is the glutton.
It's enabled for everyone by default. If you want the old eager behavior — and there are real reasons to — set the flag in your settings:
{
"enableAllProjectMcpServers": true,
"enable_tool_search": false
}
Why would you ever turn it off? Determinism. With eager loading, every tool is present every time, so behavior is identical run to run. With Tool Search, whether a tool gets used depends on whether Claude's index lookup surfaces it. If you have a small, curated set of tools that you always want available — say a five-tool internal deploy server you lean on constantly — the lazy path adds a discovery step that can occasionally miss. For small servers under the threshold this is moot. For a tight, hot-path toolset you use in every session, eager is the safer default. For the sprawling "I connected eight servers and forgot half of them" reality most of us live in, lazy wins easily.
Here's the part that gets missed. Under eager loading, a tool's description was a nicety — Claude saw the full schema regardless, so a vague description cost you a little accuracy at call time. Under Tool Search, the description is the index entry. It's the only thing Claude sees when deciding whether to pull your tool in. A bad description doesn't just degrade usage now; it makes the tool effectively invisible. The model can't load what it can't find.
So this stops being acceptable:
server.registerTool(
"search_orders",
{
title: "Search Orders",
description: "Order lookup",
inputSchema: { /* ... */ },
},
handler,
);
And this becomes the standard:
server.registerTool(
"search_orders",
{
title: "Search Orders",
description:
"Find customer orders by email, order ID, date range, or status " +
"(pending, shipped, refunded). Use for questions like 'where is " +
"order 1234' or 'list all refunds from last week'.",
inputSchema: { /* ... */ },
},
handler,
);
The second one names the search keys, enumerates the status values, and — this matters — gives example phrasings a user might actually type. Those examples are doing semantic-match work in the index. If you maintain an MCP server, treat descriptions like the searchable metadata they now are: lead with the verb, name the inputs, list the enums, and drop in a couple of real-world phrasings. Terse is no longer clever; it's a footgun.
When you run multiple servers, generic tool names start to overlap — two servers both exposing a search or a get_status. Eager loading tolerated this because the full schemas disambiguated. The index is shorter and shallower, so collisions hurt more. Prefix tool names with their domain — orders_search, github_search_issues, sentry_get_status — so the index has something distinctive to match against. It reads better in logs too.
Reclaiming 95% of your context isn't free. Eager loading front-loads everything once; after that, every tool call is a direct invocation. Tool Search adds a step — before Claude can call a tool it hasn't loaded yet, it has to find it in the index, which is an extra bit of reasoning and, on the first touch, a fetch of the full schema. In practice this is small, but on the first use of a given tool you're paying a discovery cost you didn't pay before. For a one-shot task that touches ten different tools across ten servers, you'll feel a few of those lookups.
The mental model I've settled on: Tool Search trades a small, repeated per-tool discovery cost for a large, one-time context saving. That's a great trade when you have many tools and use a handful per session — the common case. It's a worse trade when you have few tools and hammer all of them, because you pay discovery overhead to save context you weren't short on anyway. Match the mode to the shape of your toolset, don't just take the default on faith.
One concrete gotcha I hit: if a tool's description is weak and the task is phrased in domain jargon that doesn't match the index text, Claude can fail to load a tool that's right there, connected and healthy. The failure looks like the tool "not existing" rather than erroring. If Claude insists a capability isn't available when you know the server is up, check the description before you blame the connection — nine times out of ten the index just didn't match.
Concrete version of the problem. I had a Next.js app with a Postgres MCP for the database, the GitHub server for issues and PRs, a Sentry server for error triage, and an internal "deploy" server — four servers, roughly fifty-five tools between them. The task was mundane: read a Sentry error, trace it to a query in the database, open a PR with the fix. Three tools, maybe four.
Pre-Tool-Search, here's what the start of that session looked like:
/context
# System prompt 2.1K
# MCP tools 71.4K <-- fifty-five schemas I mostly won't touch
# Messages 0.0K
# Free space 126.5K / 200K
I'd spent 71K tokens to make four calls. By the time Claude had read a couple of large source files and a stack trace, I was compacting halfway through a trivial fix. With Tool Search on, the same project opens completely differently:
/context
# System prompt 2.1K
# MCP tool index 3.8K <-- names + one-liners for all 55
# Messages 0.0K
# Free space 194.1K / 200K
The four tools I actually used got pulled in on demand — sentry_get_issue, postgres_query, github_create_pull_request, and one I didn't expect, a postgres_explain Claude found in the index and decided to run on its own to confirm the slow query. That last one is the upside of the index nobody advertises: because discovery is cheap, the model is more willing to reach for a tool it might need, instead of you having to know it exists. Eager loading gave you everything and trusted the model to wade through it; lazy loading gives the model a searchable menu and lets it order precisely.
A few habits that pay off once the index is in charge. Keep servers domain-scoped rather than dumping thirty unrelated tools into one mega-server — a focused server produces a cleaner cluster of index entries. Kill connectors you're not using in a given project; an unused forty-tool server still costs you index space and adds collision risk even if its schemas are no longer eager-loaded. And lean on project-level .mcp.json so each repo only advertises the servers that repo actually needs, instead of carrying your entire global connector list into every codebase. The point of all three: a smaller, sharper index matches better and faster.
Tool Search is one move in a larger shift toward MCP treating context as the scarce resource it is. The 2026-07 spec release candidate pushes the same direction at the protocol layer — server/discover, cacheable list and read results with TTLs, and lazy capability negotiation are all about not paying for what you don't use. The pattern is consistent: stop shoving everything into the model up front, fetch on demand, keep the working window for actual work.
For day-to-day Claude Code use, the takeaway is small and immediate. Connect the servers you want without guilt — the startup tax is mostly gone. But if you build or maintain MCP servers, your descriptions just got promoted from documentation to runtime infrastructure. Write them like a search engine is going to read them, because now one is. And if a session ever feels like Claude is ignoring a tool you know is connected, run /context and /doctor first — the answer is usually sitting right there in the token breakdown.