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.
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
| 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.
| 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.
pip install mcptoon
mcptoon init
mcptoon add fetch --stdio npx -y @modelcontextprotocol/server-fetch
mcptoon manifest --toon
mcptoon call fetch fetch '{"url":"https://example.com"}' --toon
Set MCPTOON_AGENT_TYPE=claude
and it auto-selects --toon
on every call.
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.
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
Patterns blocked: delete
, drop
, purge
, wipe
, kill
, force=true
, confirm=true
. Pass --destructive
to override.
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))
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.
| 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.