Here is the fastest way to measure what your MCP tools cost the model. Take the exact tool list you pass to the API, hand it to a token counter, then count the same request with no tools at all. Subtract. That difference is what every single call pays, forever, whether the model uses those tools or not.
Most founders never run that subtraction. They connect GitHub, Slack, Sentry, a database, a calendar, and wonder why the agent got slower, pricier, and worse at picking the right tool all at once. It isn't a mystery. It's arithmetic they haven't done yet.
Why the cost is invisible#
An MCP tool definition is three things: a name, a description, and an inputSchema — a JSON Schema describing its parameters. The protocol spec is explicit about this shape. To let the model choose a tool, the client serializes all three into the context on every request. The model can't select from a menu it can't see, so the whole menu ships every time.
That's the trap. The cost doesn't show up in your code, your logs, or usually your intuition. It sits in the prompt prefix, quietly, and it scales with the number of tools you've connected — not the number you use.
You are billed for the tools you attach, not the tools you call. A connected-but-unused tool is pure overhead on every turn.
Reported overhead runs roughly 550-1,400 tokens per tool depending on how verbose the schema is. MCP issue #2808 puts the working figure near 1,000 tokens per tool, with 20-30 tools occupying 15-30 KB of context before the user has typed a word. But those are other people's tools. Yours could be leaner or far heavier. Measure.
Method 1: the authoritative count (Anthropic)#
If you deploy on Claude, the token counting endpoint is the source of truth — it accepts the same tools array you send to messages.create() and returns input_tokens using the tokenizer of the model you name. It's free and rate-limited separately from inference. Isolate the tool cost by counting with and without the list:
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-opus-4-8" # count against the model you actually deploy
msg = [{"role": "user", "content": "hi"}]
base = client.messages.count_tokens(model=MODEL, messages=msg).input_tokens
with_tools = client.messages.count_tokens(
model=MODEL,
tools=my_tools, # the EXACT list you pass to messages.create()
messages=msg,
).input_tokens
print(f"{with_tools - base} tokens of tool definitions, every call")
The subtraction matters: it strips out the boilerplate of the empty request so you see the tool tax alone. Anthropic's own docs make the magnitude concrete — a bare "Hello, Claude" message counts as 14 tokens, and adding a single get_weather tool with one string parameter takes the same request to 403. One trivial tool, ~389 tokens. Now multiply by your real tool count and real schemas.
Want the per-tool breakdown? Loop and count each tool alone, then sort descending. The worst offenders are almost always tools with deep nested schemas, long enum lists, or paragraph-length descriptions — and those are exactly the ones worth trimming or deferring first.
Method 2: the fast approximation (tiktoken)#
No Claude key, or you just want a quick relative read across providers? tiktoken tokenizes the serialized JSON locally in milliseconds:
import json, tiktoken
enc = tiktoken.get_encoding("o200k_base") # GPT-4o / o-series encoding
def tool_tokens(tool: dict) -> int:
return len(enc.encode(json.dumps(tool)))
for t in my_tools:
print(f"{tool_tokens(t):>5} {t['name']}")
print(f"{sum(tool_tokens(t) for t in my_tools):>5} TOTAL")
Two honest caveats. First, this counts the raw JSON, not the model's exact wire framing, so treat it as a lower bound and a comparison tool, not a billing figure. Second, tiktoken is OpenAI's tokenizer — Claude uses a different one, and the same text can differ by 30% or more. For dollars-accurate numbers on Claude, use Method 1. For "which of my 40 tools is the fat one," tiktoken is perfect.
Interpreting the number#
You now have a token figure. Three ways to read it:
As a share of your context budget. Divide tool-definition tokens by your working context window. A few percent is fine. Once tools are eating a double-digit share before the conversation starts, you're renting space you can't use for reasoning or history.
As a recurring bill. Multiply the tool-def tokens by requests per day by your input price. That's the standing charge for tools attached — and prompt caching complicates but doesn't erase it. Caching stops you being re-billed for a cached prefix, but the schemas still fill the window on the first turn and after any eviction, and they still occupy the model's working memory on every turn regardless of what your invoice says. The token counting endpoint deliberately ignores caching for exactly this reason.
As an accuracy risk. This is the cost the token meter won't show you. Selection accuracy degrades as the visible tool pool grows — the practical cliff tends to arrive somewhere past 30-50 tools in context at once. More tools make the model measurably worse at choosing among them, the same way a 400-item menu makes you order worse. So the token count is also a proxy for a reliability problem.
When the number says act#
Once you've measured, the fix is a menu, and each item cuts a different part of the cost:
If tool definitions are a small share of context and you're under ~30 tools, do nothing — inline is simplest and lowest-latency. If schemas dominate the prefix, defer them: Anthropic's tool search loads only the handful a request needs. If the pain is fat intermediate results round-tripping through the model, not the definitions, that's a different fix — code execution keeps that data in a sandbox. The full decision between them is its own piece: tool search vs code execution, and the deeper trade of code execution vs direct tool calls.
But those are decisions, and you can't make a decision without a number. Run the subtraction first. It takes two calls and it turns a vague "the agent feels slow" into a line item you can act on.


