MCP's Context Tax and the Multiplexer Fix

MCP tool schemas can consume 50% of your context window before the agent does anything. Multiplexers like mcp-multiplexer cut that by 95% with lazy schema discovery. Here's how it works and why it matters.

You open a fresh Claude Code session. You haven't asked it to do anything yet. You run /context and see the warning: Large MCP tools context (~82,000 tokens > 25,000).

This is the MCP context tax. Every MCP server you connect pushes its full tool schemas into the model's context at session start. Ten servers with 40–50 tools each means 40,000–80,000 tokens gone before your first prompt. On a 200k context window, that's 20–40% of your budget spent on definitions the agent may never use.

The problem is real and widely documented. Scott Spence measured 81,986 tokens across seven MCP servers in a single Claude Code session. The MCP specification issue #2808 notes that tool definitions consume 5–15× more tokens than minimal schemas. Claude Code issues #13717 and #7172 track the consolidation request. A Reddit user on r/LangChain reported 50k tokens per run just from preloaded MCP tools.

Why Tool Schemas Are So Expensive

An MCP tool definition isn't just a function signature. Each tool carries:

  • Name and description
  • Full JSON Schema for input parameters (including nested objects, arrays, enums, constraints)
  • Annotations for UI hints (readOnly, destructive, idempotent)
  • Server-level instructions and metadata

A typical tool schema runs 300–800 tokens. Multiply by 50 tools and you're at 15,000–40,000 tokens. The MCP spec issue shows that a minimal type-only schema for the same tool is 5–15× smaller. The verbosity is by design—rich schemas help the model understand what tools do—but the cost is paid upfront for every tool, used or not.

The Multiplexer Pattern: Lazy Schema Discovery

The fix is architectural: don't load schemas until the model actually needs them. This is what mcp-multiplexer (by JohGirard) implements. Instead of connecting each MCP server directly, you point your client at one multiplexer server. The multiplexer fronts all upstream servers but exposes only 7 meta-tools:

Meta-toolPurpose
list_serversOverview: name, status, tool count, instructions
list_tools(server)Tool names, one-line descriptions, annotations — no schemas
search_tools(query, server?, limit=5)Matching tools with full input schemas
describe_tool(server, tool)One exact tool's full input schema
call_tool(server, tool, arguments)Proxied call; results returned verbatim
refresh_tools(server?)Reconnect and rebuild the tool index
authorize_server(server, pasted_url?)Start/complete OAuth login for a server

The model discovers tools lazily: list and search first, fetch a full schema only when it's about to call. The tool index is cached at ~/.cache/mcp-multiplexer/index.json for instant startup, and upstream servers connect lazily.

Measured savings: The project's --stats command reports: without mux ~16,333 tokens (46 tools), with mux ~553 tokens (meta-tools), saved ~15,780 tokens (96%). The marketing claims "up to 95%" — the measured number is slightly better.

How the Model Actually Uses It

You might wonder: does the model know to use these meta-tools? Yes. The multiplexer's instructions tell the model the protocol:

To use tools, first call list_servers or search_tools to discover what's available. Then call describe_tool to get the full schema for a specific tool. Finally call call_tool to execute it.

In practice the flow looks like:

  1. Model calls search_tools("read file", limit=3) → gets 3 matching tools with full schemas
  2. Model picks one, calls call_tool("filesystem", "read_file", {path: "..."})
  3. Multiplexer proxies the call to the upstream filesystem server, returns result verbatim

This is 2–3 tool calls instead of 1, but the token cost of those 2–3 calls is tiny compared to loading 50 schemas upfront. The tradeoff is a discovery hop for a massive context saving.

Hybrid Mode: Expose Your Favorites

For servers you call every session, many times per session (e.g., Serena for code navigation, a database server you query constantly), the discovery hop is pure overhead. The multiplexer supports "expose": true on a per-server basis:

{
  "mcpServers": {
    "serena": {
      "command": "uvx",
      "args": ["serena", "start-mcp-server"],
      "expose": true
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"]
    }
  }
}

With expose: true, that server's tools mount directly as first-class server__tool tools (e.g., serena__find_symbol), schema and all, next to the meta-tools. The model calls them like any directly-connected tool — no search, no describe, no proxy hop. The server stays reachable through the meta-tools too, and allow/deny still apply.

Rule of thumb: multiplex the fleet, expose the favorites.

Governance and OAuth Built In

The multiplexer isn't just a token saver. It adds two features that matter for team and production use:

Allow/deny governance. Per-server allow (exact names or prefix* globs) and deny lists let you restrict what the model can see and call. deny always wins. This is a control plane for agent tool access without modifying upstream servers.

OAuth 2.1 brokering. Remote servers that speak the MCP authorization spec work with "oauth": true on a URL server. First use fails with an authorization URL; open it, approve; a temporary localhost listener completes the exchange. Tokens live in ~/.cache/mcp-multiplexer/tokens.json (mode 0600), refresh automatically, and survive restarts. The model can also drive this itself via the authorize_server meta-tool.

Claude Code Integration

The project is also a Claude Code plugin. Install it and it migrates your existing servers into a multiplexer config:

/plugin marketplace add JohGirard/mcp-multiplexer
/plugin install mcp-multiplexer@mcp-multiplexer

Then ask Claude to "set up mcp-multiplexer" (or run /mcp-multiplexer:setup). The plugin installs the binary, rewrites your config (secrets become ${VAR} references, OAuth servers get flagged), rewires your client config, and verifies.

With the plugin installed, /mcp-multiplexer:gain shows the token savings report in chat.

What It Doesn't Do (Non-Goals)

The README is explicit about non-goals, which is refreshing:

  • MCP resources and prompts (tools only)
  • Upstream sampling, elicitation, and roots
  • No truncation of tool results — returned verbatim
  • OAuth for URL servers only (stdio servers use env for secrets)
  • No tools/list_changed notification forwarding — use refresh_tools

This scope discipline is why the binary is small, fast, and reliable.

Related Work

mcp-multiplexer isn't the only project attacking this problem. The pattern goes by several names:

Claude Code itself has started addressing this: recent versions defer MCP tool schemas by default and load on demand through tool search (Geotoolbox, wmedia.es). But the multiplexer approach works across all MCP clients (Cursor, Zed, custom clients) and adds governance/OAuth on top.

Should You Use It?

If you run 3+ MCP servers in Claude Code (or Cursor, Zed, etc.) and see context warnings, yes. The install is one binary, the config is your existing mcpServers with one wrapper, and the plugin handles migration. The token savings are measurable and immediate.

If you run 1–2 servers with few tools, the overhead of the discovery hop may not be worth it. But as your tool fleet grows, the multiplexer pattern becomes the default architecture — it's the difference between an agent that can think and an agent that's already out of context before it starts.


If this was useful, you can support my open-source work on Ko-fi or check out my services.