DEV Community

MCP Token Saver
MCP Token Saver

Posted on

I counted the MCP tools in my Cursor setup: 882,846 tokens of tool definitions, re-sent every turn

882,846

That's not my download count. Not my traffic either. It's the full text of the 209 MCP tool definitions I have wired into Cline, Claude Desktop, Cursor, and Hermes Agent. Added up, token by token, on my own machine.

Then it hit me. That whole block gets re-sent on every single turn.

The cost isn't the server, it's the spec sheet

People assume an MCP server is heavy. It isn't.

A server is just a process. What costs tokens is the tool definition it advertises: every tool's name, arguments, argument types, and description text. Every time the model has to pick a tool, it reads that spec sheet first.

So the real question is simple. How thick is the spec sheet, and how many times does it get read?

My answer: 209 tools, ~880k tokens, once per turn.

Then I noticed the formatting itself was 43% of the bill

Before optimizing anything, I checked how much of that 882,846 was just whitespace.

The same tool list, serialized compactly instead of pretty-printed with 2-space indentation:

Serialization Tokens
Pretty (2-space indent) 882,846
Compact (no whitespace) 502,038
Name index only 726

Just stripping the pretty-printing drops it to 502,038, a 43.1% cut for free, with no behavior change at all.

But the real prize is the last row. Swap the full definitions for a plain name index, and the same 209 tools go from 882,846 down to 726 tokens. That's a 99.9% reduction, with nothing deleted. Every tool is still there. Every argument is still there. They're just not pre-read on every turn. You fetch a tool's full spec only when you actually call it.

The bill is wildly concentrated, too

I sorted the tools by size to see where the weight sits.

The top 10 tools alone are 166,858 tokens, which is 18.9% of everything. The top 20 are 268,209, or 30.4%.

The single most expensive one, compare_web_captures, is 30,853 tokens by itself.

That kills the obvious idea. "Just install fewer servers" doesn't help, because the bill scales with how thick each tool's definition is, not with how many servers you run. Uninstall a handful of lightweight servers and you save less than one heavy tool costs.

Two things not to misread

One: this saves the pre-read, not the call. When you actually invoke a tool, its argument spec still has to be sent. Those are two separate bills. Don't blend them.

Two: these are measured numbers from one machine. The basis is the raw JSON from my local tool manifest, tokenized whole with tiktoken's cl100k_base. Swap the model or the tokenizer and the absolute numbers move. The structure does not. A few heavy tools eat most of the bill.

When you should just skip this

If you only run a dozen tools totaling a few thousand tokens, don't bother. The savings won't cover the setup time. Don't optimize for the sake of it.

A project I found while searching

Later I went looking on GitHub for anything that already does this, and ran into mcptoon. A couple hundred stars. Not widely known.

It does exactly the thing I derived above: keep the tool directory as a name index by default, fetch full specs on demand. It isn't tied to one client either. Cursor, Cline, Claude Desktop, and Hermes all work.


So: how many MCP tools do you have loaded, and have you ever counted what they cost you per turn?

Top comments (1)

Collapse
 
arhancanli profile image
Arhan Canli •

The arithmetic checks out (166,858 is 18.9% and 268,209 is 30.4% of 882,846), and the skew is the useful part: 209 tools at ~4,200 tokens each on average means a few giant schemas, not tool count, drive the bill. One thing I'd separate: tokens re-sent is not the same as tokens billed. With prompt caching the repeated block is read at a fraction of the input price, so the dollar saving from a name index is smaller than 99.9%, while the context-window saving (room left for the actual work) is the full amount.

The part I'd measure next is accuracy. A bare name index works when names are distinctive; for names like get_data or search the model can't choose from the name alone. In my own work on this, the failure was in the lookup step, not the size: a stemmer turned "rates" into "rat" and the search returned the wrong tool, so the fix was in matching, not in compression.

Did you test how often the model picks the right tool from the name-only index versus the full spec, say on 50 held-out requests?

For context, I built canli-mcp, which keeps 306 finance tools behind three front tools at 1,109 tokens of definitions per request (find_tool puts the right one first for 46 of 48 held-out requests). It's free, MIT, no key: npx -y canli-mcp, repos at github.com/arhancanli/canlicapital and github.com/arhancanli/alphac.