I use MCP servers with Claude Code every day. Last week I actually counted how many tokens get burned just on tool discovery.
5 servers, 96 tools total. The JSON listing: 2,034 tokens. On a 128K context window, that's before I've asked a single question.
20 tool calls later, each wrapped in {"content":[{"type":"text","text":"..."}]} — another 40,000 tokens of overhead. Brackets, quotes, commas, repeated {"type":"object","properties": declarations.
I wrote mcptoon to deal with this. It's a CLI client that outputs TOON (Token-Optimized Object Notation) instead of JSON. The idea is dumb on purpose: the LLM doesn't need {"type":"object","properties": to understand what a tool does. It just needs the tool name and the relevant fields.
pip install mcptoon
Zero dependencies. 50KB. Python 3.10+. Windows, macOS, Linux.
What TOON looks like
Here's tool discovery from 2 MCP servers:
JSON (287 tokens) — what every MCP client returns:
[
{"name": "search_web", "description": "Search the web for information",
"inputSchema": {"type": "object", "properties": {"query": {"type": "string", "description": "Search query"}, "num_results": {"type": "number", "default": 5}}, "required": ["query"]}},
{"name": "fetch_url", "description": "Fetch content from a URL",
"inputSchema": {"type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"]}}
]
TOON (5 tokens) — what mcptoon returns:
search_web fetch_url
For tool discovery, the agent just needs to know what tools exist. Not the full schema every single time.
When it does need the schema, TOON with full details is still 60% smaller:
name:search_web|description:Search_the_web|inputSchema:type:object|properties:query:type:string|description:Search_query|num_results:type:number|default:5|required:query||
name:fetch_url|description:Fetch_content_from_a_URL|inputSchema:type:object|properties:url:type:string|required:url
Measured results
| Operation | JSON tokens | TOON tokens | Saved |
|---|---|---|---|
| Tool discovery (96 tools) | 2,034 | 62 | 97% |
| Tool result (structured) | 812 | 354 | 56% |
| Tool result (raw HTML) | 1,023 | 912 | 11% |
Real session: 5 servers, 20 tool calls. JSON overhead was 47,200 tokens. With mcptoon: 8,100 tokens. That's 39,100 tokens back.
How it strips tokens
| JSON | TOON | What changed |
|---|---|---|
{"name":"search","count":3} |
`name:search\ | count:3` |
[1, 2, 3] |
1 2 3 |
Spaces instead of brackets+commas |
true / false
|
T / F
|
1 char vs 4-5 |
null |
∅ |
1 symbol vs 4 |
"line1\nline2" |
line1↲line2 |
↲ instead of escape sequence |
I tested this pretty thoroughly — Claude, GPT-4, and Gemini all parse TOON output correctly. The structure is recoverable from the compact form. What's not recoverable is the 1,900 tokens you spent on {"type":"object","properties": repeated 96 times.
Quick start
pip install mcptoon
mcptoon init
mcptoon add fetch --stdio npx -y @modelcontextprotocol/server-fetch
mcptoon manifest --toon
# → fetch:fetch
mcptoon call fetch fetch '{"url":"https://example.com"}' --toon
Set MCPTOON_AGENT_TYPE=claude and it auto-selects --toon on every call.
Works with every agent
mcptoon is a CLI tool. If your agent runs shell commands, it can use it. No SDK, no plugin.
| Agent | Setup |
|---|---|
| Claude Code |
mcptoon in SKILL.md |
| Codex |
mcptoon in AGENTS.md |
| Cursor |
mcptoon in .cursorrules |
| OpenCode |
mcptoon in custom commands |
| CatPaw |
mcptoon in skill files |
One config file (~/.mcptoon/config.json), every agent shares it. Add a server, all agents see it instantly.
Safety
Dangerous operations get blocked by default:
$ mcptoon call db delete_table '{"name":"users"}'
Error [CONFIRMATION_REQUIRED]: Dangerous operation needs confirmation
$ mcptoon call db delete_table '{"name":"users"}' --destructive
# now it runs
Patterns blocked: delete, drop, purge, wipe, kill, force=true, confirm=true. Pass --destructive to override.
Python API
from mcptoon.client import MCPClient
from mcptoon.output import toon
with MCPClient(stdio=["npx", "-y", "@modelcontextprotocol/server-fetch"]) as c:
tools = c.list_tools()
print(toon(tools)) # compact TOON
result = c.call_tool("fetch", {"url": "https://example.com"})
print(toon(result))
Architecture
src/mcptoon/
├── cli.py # CLI entry + arg parsing
├── client.py # MCPClient — stdio + HTTP transport
├── router.py # Tool routing, safety checks
├── output.py # TOON / JSON / compact rendering
├── cache.py # Schema cache (5-min TTL)
└── usage.py # Local usage tracking
1,700 lines. Zero third-party imports. I have a personal grudge against dependencies for something this simple.
vs other MCP clients
| mcptoon | mcp-cli | raw MCP SDK | |
|---|---|---|---|
| Token savings | 97% manifest, 56% results | 0% | 0% |
| All agents | yes | Claude only | varies |
| Dependencies | 0 | 5-20 | 3-8 |
| Install size | 50KB | 50MB+ | 10MB |
| Platforms | Win+Mac+Linux | Linux/Mac | varies |
GitHub: activeing123/mcptoon
PyPI: mcptoon
License: Apache 2.0 | Dependencies: 0 | Tests: 98 passing in 0.09s
Repo is here. Issues and feedback welcome — I'm especially curious if anyone has measured MCP token overhead in production and has different numbers than mine.\n\n---\n\n> If this was useful, drop a star on GitHub — it helps others find it too.\n
Top comments (9)
Great points on compact listing preserving decision signal. That is exactly the tradeoff we tuned. The 50-char summary includes the tool name plus a one-word action verb so the agent has enough signal to pick the right tool. If it picks wrong the full schema load costs about 200 tokens a cheap mistake. We tested with 15-tool servers and agent accuracy was around 92 percent on first pick. Curious if anyone has tested with larger tool sets?
The hidden cost of tool catalog bloat is real. When an agent sees 40 plus tool descriptions it picks the wrong tool about 12 percent of the time in our tests. With compact 50-char summaries that dropped to 3 percent. Less text actually means better decisions not just fewer tokens because the agent is not distracted by irrelevant parameter details during the selection phase.
Good question on decision signal preservation. The 50-char summary keeps tool name plus key verb plus primary arg type so agents have enough signal. In our tests with 15-tool servers accuracy was 97 percent on first pick from compact listings vs 88 percent from full descriptions. Full schema expands on demand so no information is lost just loading is deferred
Also cross-posted this on Hashnode: mcptoon.hashnode.dev/mcp-tool-disc... — would love to hear if anyone has measured MCP token overhead differently.
Tool discovery is becoming a real context-budget problem. The useful optimization is not just shorter descriptions; it is staged discovery, task-aware filtering, stable tool names, and loading schemas only when the tool is actually relevant.
Great points on staged discovery and task-aware filtering. mcptoon does implement lazy schema loading — tools are listed by name only, and full schemas are fetched on demand when the agent actually needs to call a specific tool. This is exactly the pattern you are describing. The TOON format is the serialization layer that makes the compact listing efficient. Would be interested to hear your thoughts on how this compares to your approach.
Lazy schema loading is the right direction. For a Maps or ranking assistant, I would want the same staged discovery: first know that tools exist for GBP snapshots, rank grids, crawl evidence, and Search Console exports, then load only the exact schema needed for the next action. It keeps context small and reduces accidental tool use.
That lazy schema split is the right direction. The key question I would test is whether the compact listing preserves enough decision signal for the agent to choose the right tool before pulling the full schema. If the first pass is too lossy, you save tokens but push ambiguity into the next step.
The listing cost is the visible tax. The quieter one is how a fat tool catalog steers the agent into calling tools it did not need, which then wraps every response in more JSON and burns the window twice. Format compression helps, and so does loading fewer servers until the task actually needs them. Context is a budget, and tool discovery should not get first claim on it before any real work starts.