Skip to main content

Your Agent Is Drowning in Tool Definitions (Code Execution Throws It a Rope)

· 12 min read
TheMCPGuy
MCP Developer & Educator

Series cover. A figure is submerged in drifting tool-definition JSON, with counters reading twelve servers and forty tool definitions consumed before the user types. A beam labelled code execution offers discover, load, run. The strapline reads: discover first, load only what you need.

Here is a cost nobody warns you about when you wire up your fifth MCP server: your agent gets dumber and more expensive at the same time, and it happens before the user has typed a single word.

The reason is boring and brutal. Every tool your servers expose ships a definition: a name, a description, a JSON schema for its inputs, often examples. All of it gets serialized into the model's context window on every single turn, just so the model might pick the right tool. Connect a few busy servers and you are spending six figures of tokens describing tools the model will ignore for this particular request.

The savings quoted for fixing this run from a third to almost everything:

What was measuredBeforeAfterSavingWhere it comes from
One worked example, loading only the definitions the task needs150,0002,00098.7%Code execution with MCP, 4 November 2025
Tool definitions for a five-server setup, tool search switched on~55,000the 3 to 5 tools the request needsover 85%Tool search tool documentation
Programmatic tool calling, averaged over complex research tasks43,58827,29737%Advanced tool use, 24 November 2025
Version disclosure (as of June 2026)

The pattern here comes from Anthropic's engineering write-up "Code execution with MCP: building more efficient AI agents" (4 November 2025), which applies "progressive disclosure" to MCP. Anthropic had used the phrase five weeks earlier, on 29 September 2025. The code targets the MCP spec 2025-11-25, dated three weeks after that article, and resource links and application-driven resources are unchanged in the current revision, 2026-07-28. The 150,000 → 2,000 token example and the 98.7% figure come from one worked example, not a benchmark. The "300 tools at ~500 tokens each" breakdown below is mine, and that article does not give either figure. Treat both as illustrations and measure your own. As the spec and SDKs evolve, the shape of this technique should hold, but verify the mechanics against the current docs.

The tax you didn't know you were paying

There are actually two separate bloat problems, and conflating them is why people "optimize" the wrong one.

1. Definition bloat (up front). Before any work happens, the client loads every tool definition from every connected server. This is fixed per turn and scales with how many tools you have connected, not with what you are doing. Say you have three hundred tools at ~500 tokens each: that is ~150,000 tokens of overhead on turn one. (My arithmetic, not a measurement, but Anthropic's own framing is that "developers routinely build agents with access to hundreds or thousands of tools across dozens of MCP servers," so the scale is not invented.)

2. Result bloat (at runtime). A tool returns 4,000 rows of JSON, the model needs three of them, and the other 3,997 sit in the transcript forever, getting re-sent on every subsequent turn.

They have different fixes:

Kind of bloatWhen it costs youWhat it grows withWhat removes it
Definition bloaton every turn, before any work happenshow many tools are connectedloading a definition only when the model asks for that tool
Result bloaton every turn after the callhow much a tool returnedfiltering the rows in code, or handing back a resource link

Definition bloat makes your agent expensive and slow and dumber, because a context window crammed with tool schemas has less room for the actual problem, and models genuinely degrade as the relevant signal gets buried. Result bloat compounds it turn over turn.

The fix: stop describing tools, start importing them

The technique Anthropic documented flips the model's relationship to your tools. Instead of presenting every tool as a definition the model reads and then calls via JSON, you present your MCP servers as a filesystem of code. That is a directory of typed functions the model can import and call inside a sandboxed code-execution environment, a locked-down process with limited access to the machine.

// The model doesn't see 300 tool schemas.
// It sees a filesystem and writes code against it:
import { getInvoices } from "./servers/billing";
import { getCustomer } from "./servers/crm";

const overdue = (await getInvoices({ status: "overdue" }))
.filter(i => i.daysLate > 30);

// Only the 3 rows it needs ever re-enter the context, not 4,000.
console.log(overdue.map(i => ({ id: i.id, customer: i.customerName })));

Here is where the 4,000 rows stop:

The last arrow is the only one that reaches the model's context window.

Two things just happened:

  • The model only loads the definitions it actually imports, also known as progressive disclosure. The other 298 tools do not cost you a token this turn.
  • The model processes results in code and returns only the slice it cares about, so result bloat collapses too.

Treat the 98.7% in the table as "an order of magnitude or two, in a favorable case," not a number to put on a slide. The 37% is the figure Anthropic measured end to end.

Progressive disclosure is the real idea

"Code execution" is the mechanism; progressive disclosure is the principle, and it long predates MCP. Don't put everything in front of the model at once. Let it discover the tool surface lazily: list what's available cheaply, load the full signature of a tool only when it intends to use it, fetch only the fields a step needs.

You can apply the principle even without a full code sandbox:

  • Group tools and load definitions per-group on demand instead of all at once. tools/list is paginated, so the protocol already allows it.
  • Return compact, paginated, or summarized results by default and offer a "drill in" tool for detail.
  • On the Claude API this ships as the tool search tool: you still send every definition, and the ones you mark defer_loading: true stay out of the context window until Claude searches for them.
  • Return a resource_link block from the tool for large payloads, so the model gets a URI and a description it can point at, and the host fetches the body only when something needs it. Note the direction here: the link comes back from a tool call. Parking the data in a standalone resource and hoping the model finds it does not work, because resources are application-driven: the client issues every resources/read. Check that your host follows these links before you park a payload behind one.

That block is five fields:

{
"type": "resource_link",
"uri": "file:///reports/overdue-2026-06.json",
"name": "overdue-2026-06.json",
"description": "4,000 overdue invoice rows",
"mimeType": "application/json"
}

So who is supposed to fix this, you or the client?

This is the honest nuance the headline glosses over: a lot of definition bloat is the client's problem, not your server's. How tool definitions are packed into context, whether the host supports a code-execution surface, whether results can be offloaded to resources: much of that lives in the MCP host, the application the user actually runs, not in your server.

What you control as a server author:

  • Right-size your results. Default to lean; make verbosity opt-in.
  • Hand back resource links instead of payloads for big data. A resource_link gives the model a handle; the 4,000-row JSON blob rides along on every subsequent turn.
  • Write tight definitions (see Your Tool Description Is a Prompt; every wasted word is a wasted token, on every turn).

What you control as a host/client author:

  • Whether you expose tools as schemas-in-context or as an importable code API.
  • Whether you implement progressive disclosure of definitions at all.

What to do Monday

  1. Measure first. Log the token count of your tool definitions on a cold turn, the first turn of a conversation before any cache is warm. People are routinely shocked. That number occupies context on every turn, though prompt caching bills the repeat turns as cache reads at a tenth of the base input price on most models.
  2. Cut definition bloat before you touch anything clever: fewer, sharper tools; tighter descriptions.
  3. Return resource links instead of big results so the payloads stop riding along on every turn.
  4. If your host supports it, try the code-execution surface on your most tool-heavy agent and measure the delta on a real task. Anthropic's caveat: agent-written code needs a secure execution environment with sandboxing, resource limits and monitoring, and that is part of the price.

The agents that win in 2026 aren't the ones connected to the most MCP servers. They're the ones that can ignore the most servers, cheaply, until the moment a tool is actually needed.

Want the full treatment? This is the heart of our upcoming Context Engineering for MCP course, covering token accounting, code execution, progressive disclosure, and how to measure the savings for yourself, including the numbers in this post.

Further Reading

Sources