Here are two numbers that should ruin your morning coffee: 71,929 tokens versus 123 tokens. Same 255 tools. Same machine. Same day.
The first number is what your agent pays every single session when 255 tools from 50 MCP servers load as raw JSON schemas into its context window. The second is what the same tool listing costs when discovery happens through a CLI instead.
That is a 300-page book versus a sticky note, every session, before your agent has answered a single question. If you are running multiple MCP servers in Claude Code, Cursor, or anything similar, you are paying the book price right now and probably do not know it.
The Problem: Every Tool Ships Its Entire Resume
When an agent connects to an MCP server, the server hands over a tool catalog. Each entry looks like this:
{
"name": "search_repos",
"description": "Search GitHub repositories by query",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
},
"per_page": {
"type": "number",
"description": "Results per page (default 30)"
}
},
"required": ["query"]
}
}
That is one tool. Multiply by every parameter, every description, every nested properties block, and then by 255 tools. The protocol’s answer to “what can you do?” is a full API reference document (types, defaults, prose descriptions and all) injected wholesale into the context window.
The schema only matters twice per session: once when the model picks a tool, and once when it fills in arguments. The other 99% of the time, that 71K-token wall just sits there, occupying prime real estate while your actual code, conversation, and diffs fight for scraps.
Where the Tokens Go
| Component | Token Cost | Why It Hurts |
|---|---|---|
| JSON structure | 15-20% | Braces, quotes, commas for every property |
| Property descriptions | 30-40% | Natural language prose for each parameter |
| Type definitions | 20-25% | "type": "object", nested schemas, enums |
| Required arrays | 5-10% | Redundant list of mandatory fields |
| Protocol handshake | 10-15% | Server metadata, capability negotiation |
The MCP protocol was designed for correctness and discoverability, not for token efficiency. Every tool gets a full JSON Schema definition because the protocol assumes the agent needs complete type information upfront. In practice, most agents only use a handful of tools per session, but they pay for all 255 in advance.
Why CLI Wins (and Where It Loses)
A CLI-based tool discovery flow looks like this:
$ agent-tools list
search_repos - Search GitHub repositories
create_issue - Create a new issue
list_prs - List pull requests
...
The agent sees tool names and one-line descriptions. When it wants to call search_repos, it runs:
$ agent-tools schema search_repos
That returns the full schema for one tool, not 255. Total cost: 123 tokens for the list, plus 200-500 tokens per schema fetch, only when needed.
CLI advantages:
- Lazy schema loading
- No JSON wrapping overhead
- No protocol handshake per session
- Human-readable output doubles as agent input
CLI disadvantages:
- No type safety until runtime
- Extra round-trip per tool call
- No server-side state management
- Harder to version or hot-reload tools
Mitigation Strategies
1. Schema Pruning
Strip descriptions and optional fields from the initial catalog. Send only:
{
"name": "search_repos",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"per_page": { "type": "number" }
},
"required": ["query"]
}
}
This cuts token cost by 40-60%. The agent can request full schemas on demand if it needs clarification.
2. Batched Discovery
Instead of loading all tools at session start, load them in waves:
- Wave 1: Core tools (file ops, search, git)
- Wave 2: Domain tools (database, API clients)
- Wave 3: Specialized tools (ML, infra)
The agent only pays for Wave 1 upfront. Subsequent waves load when the agent asks for capabilities it does not have.
3. Hybrid CLI Fallback
For high-frequency, low-complexity tools (list files, run command, read file), skip MCP entirely. Route those calls through a CLI shim that returns plain text. Reserve MCP for tools that need structured input validation or server-side state.
4. Tool Clustering
Group related tools under a single MCP endpoint with a dispatch parameter:
{
"name": "github",
"inputSchema": {
"type": "object",
"properties": {
"action": { "enum": ["search_repos", "create_issue", "list_prs"] },
"params": { "type": "object" }
}
}
}
This reduces 50 GitHub tools to one catalog entry. The agent pays for one schema, then uses action to route internally.
Implementation: Schema Pruning in Practice
Here is a minimal MCP server that strips descriptions on first load:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "pruned-tools", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
const fullSchemas = {
search_repos: {
name: "search_repos",
description: "Search GitHub repositories by query",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "The search query" },
per_page: { type: "number", description: "Results per page" }
},
required: ["query"]
}
}
// ... 254 more tools
};
server.setRequestHandler("tools/list", async () => {
const pruned = Object.values(fullSchemas).map(tool => ({
name: tool.name,
inputSchema: {
type: tool.inputSchema.type,
properties: Object.fromEntries(
Object.entries(tool.inputSchema.properties).map(([k, v]) => [
k,
{ type: v.type }
])
),
required: tool.inputSchema.required
}
}));
return { tools: pruned };
});
server.setRequestHandler("tools/get", async (request) => {
const { name } = request.params;
// In production, validate that name exists in fullSchemas before returning
return { tool: fullSchemas[name] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
The agent gets a 15K-token catalog instead of 71K. When it needs full details for search_repos, it calls tools/get and pays 200 tokens for that one schema.
Observability: Measuring Your Own Overhead
To measure token cost in your own setup:
- Install tiktoken:
pip install tiktoken - Capture the MCP
tools/listresponse - Tokenize it:
import tiktoken
import json
with open("tools_list.json") as f:
payload = json.load(f)
enc = tiktoken.encoding_for_model("gpt-4")
tokens = enc.encode(json.dumps(payload))
print(f"Token count: {len(tokens)}")
Run this before and after pruning. If you are over 10K tokens for fewer than 100 tools, you have overhead to cut.
Technical Verdict
MCP’s token overhead is real and measurable. A 255-tool catalog costs 71,929 tokens versus 123 for CLI-based discovery. That is 584x more expensive for the same information.
Use MCP when:
- You need structured tool contracts with strong type validation at the protocol layer
- Tools have complex nested schemas (database queries, API clients with many parameters)
- Server-side state management is required (sessions, transactions, connection pooling)
- You are building a multi-tenant tool marketplace where discoverability matters more than per-session cost
- Your agent workload involves deep interaction with a small subset of tools per session
Avoid MCP when:
- You have 50+ simple tools with flat parameter lists (file ops, shell commands, basic CRUD)
- Most tools are used once per session or less
- You are optimizing for cost per session over developer experience
- Your agent already has a CLI or REST fallback path that works
- Token budgets are tight and you cannot afford 10K+ tokens of overhead per session
If you are paying 71K tokens per session, implement schema pruning to cut overhead by 60-80%, or switch to CLI-based discovery with lazy schema loading. The protocol is not broken. It is optimized for a different set of constraints. Choose the tool that matches your cost model.