DEV Community

Cover image for Your MCP Servers Are Silently Draining Your Context Window: The Hidden Cost of Tool-Heavy AI Agents
𝗝𝗼𝗵𝗻 for Necoz

Posted on AI-assisted

Your MCP Servers Are Silently Draining Your Context Window: The Hidden Cost of Tool-Heavy AI Agents

Schema bloat silently burns tokens

I typed a two-word prompt—"Run tests"—into my AI coding agent, and my API billing dashboard immediately registered 28,400 input tokens. I had not attached a single source file. My conversation history was completely empty. Yet nearly 15% of my model's active context window evaporated before the LLM generated its first character.

The culprit wasn't a massive codebase index or a bloated system prompt. It was the seven Model Context Protocol (MCP) servers I had eagerly configured over the previous month.

Connecting GitHub, Jira, Postgres, Sentry, Slack, Docker, and filesystem MCP servers feels like giving your AI agent superpowers. What looks like a productivity upgrade in your config file quietly turns into a tax on every single turn of your conversation—degrading reasoning accuracy, slowing down time-to-first-token, and draining your token budget.


How Tool Definitions Hijack the Prompt

A common misconception is that MCP tools sit idle in the background until the model decides to call them. That is not how LLM tool calling works under the hood.

For an LLM to know when and how to invoke a tool, the host application must inject the complete JSON Schema definition of every single registered tool into the system context of every API request (via the tools parameter in the Anthropic Tool Use API or OpenAI Function Calling).

When you install an all-in-one MCP server—like a full GitHub or cloud provider integration—you rarely get just one or two tools. You get 30 to 60 distinct endpoints (create_issue, update_pull_request, list_workflow_runs, get_commit_status, etc.). Each tool carries:

  • A verbose description explaining when to use it and when to avoid it.
  • Nested JSON Schema objects defining every parameter, enum, and filter.
  • Validation constraints and usage examples embedded in property descriptions.

The Anatomy of Schema Bloat

Let's look at a single, typical tool definition sent over the wire when using a comprehensive third-party MCP server:

{
  "name": "search_project_issues_advanced",
  "description": "Search and filter project issues across repositories. Use this tool whenever the user asks about bugs, tasks, sprints, or ticket statuses. Supports pagination, sorting by creation or update date, and filtering by assignee, label, milestone, or state.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Full-text search string matching issue title or body markdown."
      },
      "state": {
        "type": "string",
        "enum": ["open", "closed", "all"],
        "description": "Filter issues by their current lifecycle state. Defaults to open."
      },
      "labels": {
        "type": "array",
        "items": { "type": "string" },
        "description": "List of exact label names to filter by (AND logic applied)."
      },
      "sortBy": {
        "type": "string",
        "enum": ["created", "updated", "comments"],
        "description": "Field used to sort the returned issue list."
      }
    },
    "required": ["query"]
  }
}
Enter fullscreen mode Exit fullscreen mode

That single schema costs roughly 180–220 tokens. Now multiply that across a realistic developer setup:

  1. GitHub MCP Server: ~35 tools (~8,500 tokens)
  2. Issue Tracker / Docs MCP: ~25 tools (~6,200 tokens)
  3. Database & Observability MCPs: ~20 tools (~4,800 tokens)
  4. Browser / Filesystem / Git MCPs: ~25 tools (~5,500 tokens)

Before you even ask a question, 25,000 tokens of rigid JSON schemas are injected at the top of your prompt. And because agentic workflows operate in loops—reading a file, running a command, checking an error, editing a line—a 10-step task doesn't pay that 25,000-token penalty once. Even with prompt caching mitigating part of the dollar cost, the model's attention mechanism still has to attend to 250,000 tokens of cumulative schema noise across those 10 steps.


Context Rot and Tool Confusion

Paying for extra input tokens is annoying, but the real damage happens to your agent's intelligence:

  1. Tool Selection Hallucinations: When an LLM is presented with 80+ overlapping tools (e.g., read_file, get_file_contents, inspect_repo_file, search_code), its ability to pick the right tool drops sharply. It starts passing parameters meant for one tool into another, or enters infinite retry loops due to schema validation errors.
  2. Attention Dilution ("Lost in the Middle"): Every token spent describing a Jira sprint filter you never use competes for attention with your actual code, stack traces, and architectural rules.
  3. Uncontrolled Output Dumps: Many monolithic MCP tools return raw, unpaginated API responses directly into the context window—dumping 15,000 tokens of raw JSON metadata when all you needed was a 3-line status summary.

Auditing Your MCP Token Tax in TypeScript

To see exactly how much context my tool definitions were eating, I wrote a zero-bloat TypeScript auditor that measures the serialized token footprint of tool schemas and compares a bloated multi-server setup against a minimal, focused toolkit.

You can run this script directly using Node.js native TypeScript execution (node --experimental-strip-types audit-tools.ts on Node 22.6+ or directly node audit-tools.ts on Node 23.6+):

// audit-tools.ts
export interface ToolDefinition {
  name: string;
  description: string;
  inputSchema: Record<string, unknown>;
}

export interface AuditReport {
  toolCount: number;
  rawBytes: number;
  estimatedTokensPerTurn: number;
  tokensPer10StepLoop: number;
}

/**
 * Estimates LLM token usage for JSON schemas (~3.6 chars per token for structured JSON)
 * and calculates cumulative context pressure across multi-step agent loops.
 */
export function auditToolFootprint(tools: ToolDefinition[]): AuditReport {
  const serialized = JSON.stringify(tools);
  const rawBytes = Buffer.byteLength(serialized, "utf8");
  const estimatedTokensPerTurn = Math.ceil(serialized.length / 3.6);

  return {
    toolCount: tools.length,
    rawBytes,
    estimatedTokensPerTurn,
    tokensPer10StepLoop: estimatedTokensPerTurn * 10,
  };
}

// Simulate a bloated 45-tool MCP setup vs. a focused 3-tool micro-setup
const bloatedSetup: ToolDefinition[] = Array.from({ length: 45 }, (_, i) => ({
  name: `enterprise_integration_action_${i + 1}`,
  description:
    "Comprehensive enterprise integration endpoint supporting filtering, pagination, sorting, metadata expansion, and audit logging across workspaces.",
  inputSchema: {
    type: "object",
    properties: {
      workspaceId: { type: "string", description: "Target workspace UUID" },
      filterQuery: { type: "string", description: "Lucene-compatible filter syntax" },
      includeMetadata: { type: "boolean", description: "Include full audit trail" },
      limit: { type: "number", description: "Max records to return (1-500)" },
      cursor: { type: "string", description: "Pagination cursor from previous call" },
    },
    required: ["workspaceId", "filterQuery"],
  },
}));

const microToolSetup: ToolDefinition[] = [
  {
    name: "run_focused_check",
    description: "Run local project verification and return concise failure lines only.",
    inputSchema: {
      type: "object",
      properties: {
        scope: { type: "string", enum: ["types", "tests", "lint"] },
      },
      required: ["scope"],
    },
  },
];

console.table({
  "Bloated MCP Stack (45 tools)": auditToolFootprint(bloatedSetup),
  "Focused Micro-Tool (1 tool)": auditToolFootprint(microToolSetup),
});
Enter fullscreen mode Exit fullscreen mode

Benchmark Results

When I compared my default "connect everything" profile against a stripped-down, task-specific workflow across 50 real debugging sessions, the difference was immediate:

Metric Monolithic MCP Stack (52 Tools) Focused Micro-Tools (4 Tools) Impact
Baseline Schema Overhead (Turn 1) 24,600 tokens 640 tokens 97% less baseline noise
10-Step Agent Loop Footprint 246,000 tokens 6,400 tokens Massive context savings
First-Try Tool Selection Accuracy 78% (frequent parameter mix-ups) 99% Near-zero tool hallucinations
Avg Time-to-First-Token (TTFT) 3.4 seconds 1.1 seconds 3x faster agent response

Small, Composable Micro-Tools

Instead of loading massive, general-purpose MCP servers that expose an entire REST API to your LLM, build and use small, single-purpose open-source tools designed specifically for agent consumption.

1. Prefer Focused CLI Scripts Over Bloated Schemas

If your agent already has a single bash/terminal execution tool, it does not need a 30-tool GitHub MCP server. A 40-line TypeScript CLI utility that wraps your exact workflow, strips out JSON noise, and prints only actionable lines saves thousands of tokens on both input (zero schema bloat) and output (curated stdout).

#!/usr/bin/env node
// scripts/pr-summary.ts — Zero-bloat alternative to a full GitHub MCP server
import { execFileSync } from "node:child_process";

const prNumber = process.argv[2];
if (!prNumber) {
  console.error("Usage: pr-summary <pr-number>");
  process.exit(1);
}

// Fetch only the exact fields the AI actually needs to reason
const raw = execFileSync(
  "gh",
  ["pr", "view", prNumber, "--json", "title,state,body,files"],
  { encoding: "utf8" }
);

const data = JSON.parse(raw);
console.log(`PR: ${data.title} [${data.state}]`);
console.log(`Changed Files (${data.files.length}):`); for (const file of data.files) { console.log(` - ${file.path} (+${file.additions}/-${file.deletions})`);
}
Enter fullscreen mode Exit fullscreen mode

2. Design "Narrow Waist" MCP Tools

When you do build or use an MCP server, follow the Unix philosophy:

  • Consolidate CRUD sprawl: Instead of exposing 12 separate tools for listing, filtering, and reading issues, expose 1 or 2 tightly scoped read-only tools.
  • Trim property descriptions: Write concise, high-signal schema descriptions. The LLM does not need a three-paragraph essay explaining what a limit integer does.
  • Truncate and format outputs at the boundary: Never return raw 50 KB API payloads to the model. Filter and format the response inside your TypeScript tool handler before returning it to the agent.

How to Equip Your AI Agent

Bookmark this matrix to decide whether a workflow belongs in an MCP server, a lightweight CLI script, or a project skill:

Approach Upfront Token Cost Output Noise Best Use Case Anti-Pattern Risk
Monolithic 3rd-Party MCP 🔴 Very High (5k–25k tokens) 🔴 High (raw API JSON) Quick non-coding exploration Leaving 5+ servers enabled globally across all projects
Focused Micro-MCP (1–3 tools) 🟢 Low (300–800 tokens) 🟢 Low (curated text) Stateful integrations, DB read-only queries, custom team workflows Adding every edge-case flag to inputSchema
Zero-Bloat TypeScript CLI ⚡ Zero (uses existing shell tool) 🟢 Minimal (custom stdout) Git automation, test runners, log parsers, CI triage Failing to provide clear --help output on error
On-Demand Skills / Prompts ⚡ Zero until loaded 🟢 Low Domain-specific rules, release checklists, code generation templates Stuffing all instructions into the global system prompt

Pre-Flight Checklist for Lean AI Workflows

Before starting your next coding session, run through this 60-second audit:

  • [ ] Disable global MCP servers: Never load Jira, Slack, or database MCPs globally. Enable them per-project or per-session only when actively needed.
  • [ ] Cap active tools at 15: If your agent has more than 15–20 tools active at once, audit and disable the ones you haven't triggered in the past week.
  • [ ] Filter read vs. write tools: Disable destructive or write-heavy MCP endpoints (delete_repo, merge_pr, drop_table)—they waste context tokens and introduce security risks.
  • [ ] Replace chatty MCPs with lightweight TypeScript CLIs: If a tool just wraps a REST endpoint or local command, turn it into a small, composable script that returns clean, summarized text.

The Takeaway

The power of an AI agent isn't measured by how many tools you attach to it—it's measured by how much clean, unpolluted context remains for actual reasoning. By ditching bloated, monolithic MCP servers in favor of small, sharp, single-purpose TypeScript tools, you cut token costs, eliminate tool hallucinations, and make your agent noticeably faster.

How many MCP tools are currently active in your editor right now, and have you noticed your agent getting slower as you added more integrations?


Connect with Us

If you found this article helpful, check out our open-source tools and let's connect!

johnnylemonny GitHub Banner

Top comments (15)

Collapse
 
nyaomaru profile image
nyaomaru Necoz •

Really good article, John! 😸🙌

I especially liked the point that “more tools” doesn’t automatically mean “more capable agent.”

It’s really easy to keep adding MCP servers because each one looks useful in isolation, but once all of those schemas and outputs start competing with the actual code and task context, the trade-off becomes much less obvious.

The distinction between focused micro-tools, small CLIs, and on-demand skills also makes a lot of sense to me.

“Small, sharp, single-purpose tools” feels very close to how I want to design developer tooling in general too. 😸

Collapse
 
johnnylemonny profile image
𝗝𝗼𝗵𝗻 Necoz •

Thanks so much, nyaomaru! 🙌

That "looks useful in isolation" trap is exactly how bloat creeps into modern workflows. Keeping tools small, sharp, and single-purpose really is the sweet spot. Both for human developer experience and for keeping AI context windows clean.

Really glad this resonated with your philosophy on building developer tooling.

Collapse
 
gapmine profile image
Balance •

Good breakdown. One distinction I’d make in the table: 25k is per-request context occupancy; 250k over a 10-step loop is cumulative input processed, not 250k sitting in one context window. Both matter, but labeling them separately makes the cost-versus-window tradeoff much clearer.

Collapse
 
johnnylemonny profile image
𝗝𝗼𝗵𝗻 Necoz •

Fair point - that is a really helpful distinction.

Separating single-turn window occupancy (which drives attention dilution) from cumulative input processed across the loop (which drives latency and API spend) definitely makes the tradeoff clearer.

Appreciate you calling that out for readers going through the numbers.

Collapse
 
gapmine profile image
Balance •

Glad that helped. The reason I started labeling them separately is pretty practical: the occupancy number is what I use to decide whether to unload a server mid-session, and the cumulative number is what I look at when a bill looks off. I mixed them up a couple of times and ended up fixing the wrong one.

Collapse
 
marieeee profile image
Marie •

This highlights an important design principle that I think will become increasingly relevant as AI agents evolve: more capabilities don't automatically translate into more intelligence. We often equip agents with every possible integration, assuming that greater access means better results. But there's a hidden trade-off: every additional tool competes for context, attention, and the model's ability to distinguish what actually matters.

I particularly like the shift toward focused micro-tools. It mirrors good software architecture: small, composable components with clearly defined responsibilities are often easier to maintain, understand, and optimize than massive systems trying to do everything.

I'd add one more consideration: tool selection should be dynamic, not just minimal. An agent working on a database issue shouldn't need to carry the entire GitHub, Slack, and Jira tool catalog throughout every reasoning step. Loading capabilities when they're actually needed could become just as important as optimizing the model itself.

We're entering an era where building better AI agents may involve giving them fewer tools, not more. The real measure of an agent isn't how much it can access, but how effectively it uses the context it has available.

Excellent reminder that context is an engineering resource, not an unlimited container.

Collapse
 
iictp profile image
IICTP •

The dynamic angle is a great addition — loading tools per task phase rather than per session feels like the natural next step after capping the count. The 78% to 99% first-try accuracy jump in the article already shows how much selection quality matters, and swapping tool sets as the task shifts could push that further. I wonder how you'd decide when to swap — a planner step up front, or letting the agent request a tool set as it goes?

Collapse
 
johnnylemonny profile image
𝗝𝗼𝗵𝗻 Necoz •

Thanks so much, Marie. I love the framing that context is an engineering resource rather than an unlimited container.

Your point about dynamic capability loading really nails the next step here. An agent debugging a database issue has zero reason to carry Slack, Jira, or GitHub schemas through every reasoning loop, so loading tools just-in-time is quickly becoming just as critical as keeping individual tools small and composable.

Appreciate you bringing such a thoughtful architectural perspective to the thread.

Collapse
 
maxbuildsdev profile image
Max Kaya •

The 28,400-token figure for a two-word prompt is a good concrete baseline. One thing I've run into that makes this worse than the schema count suggests: the same MCP server can expose overlapping tools (list_workflow_runs vs get_commit_status vs search_issues), and the model spends real tokens on the description of the near-miss before picking the right one. Trimming the server list to two or three tools per domain — or wrapping the server with a thin "search" tool that returns the schema for the real tool on demand — cut my per-request overhead by roughly the same 60-70% range you're implying.

Did you measure the hit on the model's actual picks, or only the token cost? With seven servers, I'd expect the bigger loss to be in the model calling a plausible-but-wrong tool from the list and having to recover from the error.

Collapse
 
johnnylemonny profile image
𝗝𝗼𝗵𝗻 Necoz •

Your intuition is spot on. The recovery loops from picking a "plausible-but-wrong" tool hurt far more than the static schema tax itself.

In my benchmark runs, first-try tool selection accuracy dropped to 78% with the full 52-tool stack (compared to 99% with the 4-tool setup), and every misfire burned an extra full turn of context just to recover from validation errors or wrong endpoints. Wrapping servers behind a thin on-demand schema search tool is a great way to sidestep that overlap entirely.

Appreciate you sharing those numbers and insights from your own setup.

Collapse
 
kolmopdf profile image
KolmoPDF •

The schema overhead math tracks with what we see in document extraction pipelines too, though tool return payloads often dwarf the definition tax. An unguarded PDF or web-fetch MCP tool that dumps raw layout JSON or an entire 40-page document into stdout burns tens of thousands of tokens on the very first turn, pushing relevant code or instructions into the middle of the context window.

Returning a compact structural outline first (headings, table counts, and page ranges) and letting the agent request specific sections or page slices on demand keeps both the schema surface small and the tool outputs bounded.

Collapse
 
johnnylemonny profile image
𝗝𝗼𝗵𝗻 Necoz • • Edited

Spot on. The output payload blowup is often the bigger silent killer in real-world pipelines.

Dumping raw layout JSON or a 40-page document on turn one immediately triggers that "lost in the middle" degradation. Returning a lightweight structural outline first and letting the agent fetch specific slices on demand is such a clean pattern for keeping context bounded on both ends.

Appreciate you sharing this practical insight from document extraction workflows.

Collapse
 
mp3sss31 profile image
mp3sss •

This matches the tax we see when a client eagerly registers every capability up front: each MCP server's tool schemas are resent on nearly every turn, so a long server list compounds into a steady per-turn token bill even before the model acts. A practical mitigation is lazy, context-scoped tool exposure — register a narrow toolset for the current task and pull in the rest only when the workflow calls for it, and trim descriptions to the minimum the model needs to pick the right call. On the desktop side we apply the same rule: don't preload every module and permission surface at startup; load what the active view actually needs.

Collapse
 
anbowork profile image
AnBoWork •

Useful breakdown, especially the split between the schema tax and the accuracy drop at 52 tools vs 4.

One thing I'd add to the dynamic-loading discussion above: tool definitions sit at the very start of the prompt, so swapping tools mid-session usually invalidates the prompt cache for everything after them. With a static toolset, the schema cost is mostly cached and cheap. With per-phase swapping you trade that for a cache miss each time. So "fewer tools" and "load on demand" have different cost profiles. Caching helps the bill and latency, but it does nothing for the tool-selection accuracy problem.

Did your benchmark run with caching on? Curious how much of the 3.4s vs 1.1s gap survives with a warm cache.

Collapse
 
cj_e49fbe4085948741f2f23d profile image
CJ •

This is an excellent article, and I think you're highlighting one of the most underestimated problems in the current AI agent ecosystem: we've become so focused on expanding what our agents can do that we're forgetting to consider the cognitive and computational cost of giving them access to everything.

The example of a two-word prompt generating 28,400 input tokens before the model even begins responding is particularly striking. It illustrates how easily a seemingly harmless productivity improvement can become a hidden performance and cost problem. Connecting more integrations feels like adding capabilities, but every additional tool definition competes for valuable context that could otherwise be used for source code, error messages, architectural decisions, and actual reasoning.

What I find especially interesting is the distinction between token consumption and reasoning quality. The problem isn't simply that developers are paying for unnecessary input tokens. It's that excessive tool definitions introduce noise into an environment where context selection and attention are already critical. When an agent has dozens of overlapping tools available, choosing the correct one becomes another reasoning challenge. More capabilities can actually produce less predictable behavior.

I particularly appreciate your recommendation to use focused micro-tools and lightweight TypeScript CLIs. It reminds me of an important principle from traditional software engineering: good abstractions should reduce complexity, not merely move it somewhere else. An agent doesn't necessarily need access to an entire GitHub API when all it needs to do is inspect a pull request, summarize changed files, or retrieve a CI failure.

The same applies to database access, observability platforms, and project management integrations. A small tool that performs one well-defined operation and returns carefully filtered results can be significantly more useful than a massive integration exposing every possible endpoint.

I'd also emphasize that tool output optimization deserves as much attention as schema optimization. Even a perfectly designed MCP server can overwhelm an agent if it returns enormous JSON payloads, irrelevant metadata, or entire log histories. Keeping the input schema small is only half the battle; the output needs to be equally intentional.

One additional architectural approach worth exploring is dynamic tool discovery. Instead of registering every available tool at the beginning of every conversation, an agent could initially receive a small set of core capabilities and discover additional tools only when a task requires them. This would make tool availability contextual rather than global, potentially reducing both schema overhead and tool-selection ambiguity.

There is also an important security dimension here. Every additional integration potentially expands the agent's attack surface, permissions, and opportunities for unintended actions. Reducing the active toolset is therefore not just a token optimization strategy; it can also become a meaningful least-privilege security practice.

One small distinction I'd add to the benchmarking discussion: multiplying the schema footprint by ten agent steps is useful for illustrating cumulative request overhead, but it isn't necessarily equivalent to ten times that many simultaneously occupied context-window tokens. Context reuse, prompt caching, conversation history management, and provider-specific tool serialization all influence the actual cost. Separating cumulative token consumption from peak context occupancy would make an already compelling analysis even stronger.

Ultimately, I think your central argument points toward a broader principle for building effective AI agents:

The goal should not be to maximize the number of tools an agent can access, but to minimize the amount of information and capability it needs to accomplish a particular task reliably.

We're entering a phase where context engineering deserves the same architectural attention we've traditionally given to database optimization, API design, and resource management.

Excellent contribution. This is exactly the kind of practical discussion the AI developer community needs.

I'm curious about one thing: Do you think the future of MCP will move toward dynamic, task-aware tool loading, where agents discover and activate capabilities on demand, rather than keeping dozens of integrations permanently registered? I suspect that architectural shift could be just as important as the tools themselves.