DEV Community

Cover image for The median MCP server costs 3,150 tokens before your agent calls anything
Aman
Aman

Posted on

The median MCP server costs 3,150 tokens before your agent calls anything

Every MCP server you connect sends its full tool list to the model before the model does anything. You pay for that list on every single turn, whether the agent uses one of those tools or none of them.

I had a rough sense this was expensive. I did not have a number. So I went and measured it.

The method

The MCP directory I maintain has about 10,500 servers with a remote HTTP endpoint. I took a random 60, sent each an unauthenticated tools/list, and measured the JSON that came back.

import json, subprocess

def probe(url):
    out = subprocess.run(
        ["curl", "-s", "-m", "10", "-X", "POST", url,
         "-H", "Content-Type: application/json",
         "-H", "Accept: application/json, text/event-stream",
         "-d", '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'],
        capture_output=True, text=True).stdout

    # streamable-http servers may answer as SSE
    if out.lstrip().startswith("event:"):
        out = [l[6:] for l in out.splitlines() if l.startswith("data: ")][0]

    tools = json.loads(out)["result"]["tools"]
    return len(tools), len(json.dumps(tools))
Enter fullscreen mode Exit fullscreen mode

Token counts below are chars / 4, the usual rough approximation. It is approximate on purpose: JSON with lots of short keys tokenises a bit worse than prose, so if anything these are optimistic.

The first finding was not the one I went looking for

27 of the 60 did not answer at all. Timeouts, connection failures, and auth walls on a method that is supposed to be discoverable. Some of that is legitimate, plenty of servers gate tools/list behind a key. But nearly half of a registry's remote entries not responding to a plain discovery call is its own kind of interesting, and it is worth knowing before you assume a directory listing means a working server.

That left 33 servers with real numbers.

What a tool list actually costs

percentile tools tokens
min 1 174
p25 6 913
median 11 3,150
p75 33 14,113
max 84 19,923

Those two max figures are different servers, which turned out to be the most useful thing I learned.

The distribution is the story. It is not a normal curve with a comfortable middle. Nine of the 33 cost under 1,000 tokens. Ten cost more than 10,000. A third of what I sampled eats five figures of context before your agent has read a line of the actual task.

Tool count is a bad proxy for cost

I assumed the server with the most tools would be the most expensive. It is not close.

  • 84 tools → 7,569 tokens
  • 54 tools → 19,923 tokens

The 54-tool server costs two and a half times more with a third fewer tools. The difference is entirely schema verbosity: long descriptions, deeply nested parameter objects, enums spelled out inline, examples in the schema.

So "I only connected a couple of small servers" is not the reassurance it sounds like. A tidy 10-tool server with chatty schemas can cost more than a 50-tool one with terse ones, and the tool count is the only number most clients show you.

If you loaded all 33, you would spend roughly 215,000 tokens describing capabilities. That is most of a 200k window, gone, before anything happens.

The part that compounds

Nobody connects 33 servers. But three or four is completely normal, and here is where it gets uncomfortable: you pay this on every turn, not once per session. A twenty-turn conversation with four median servers is 3,150 × 4 × 20, about 252,000 tokens spent re-describing tools the model mostly will not call.

At that point the tool definitions cost more than the conversation.

There is a quality cost too, and I think it is the bigger one. More tools in context means more surface for the model to pick wrong. Anecdotally, the failure mode is not "cannot find the tool", it is "confidently picked the tool with the similar name". Twelve overlapping search tools do not make an agent twelve times better at searching.

Three ways out

Prune. The cheapest fix and the one people skip. Most clients let you disable individual servers per project. A repo that never touches your calendar does not need the calendar server loaded.

Filter at the client. Some clients now support allow-lists per server, so you load 3 tools from a 40-tool server. Underrated, and it works today with no new infrastructure.

Load on demand. Do not send definitions up front at all. Give the model one small tool that searches for tools, and fetch the real definition only once it has chosen. You trade a round trip for the context.

That last one is the approach I ended up building on, so treat the next section as an interested party talking.

What we did

fetchbean is one endpoint over a catalog of tools, currently 56 providers and 748 of them. The agent does not get 748 definitions. It gets four meta-tools, and asks:

curl "https://api.fetchbean.com/discover?q=read+a+page+my+fetch+got+403+on"
Enter fullscreen mode Exit fullscreen mode
{ "results": [
    { "provider": "jina", "endpoint": "/read", "title": "Read a URL",
      "blurb": "Clean markdown from any URL.", "params": ["url"] }
] }
Enter fullscreen mode Exit fullscreen mode

Then calls it:

curl -X POST https://api.fetchbean.com/v1/run \
  -H "X-API-Key: $KEY" \
  -d '{"provider":"jina","endpoint":"/read","input":{"url":"https://example.com"}}'
Enter fullscreen mode Exit fullscreen mode

Four definitions in context instead of 748. discover needs no key, so you can try that first curl right now and see what comes back.

The credential side turned out to matter as much as the context side. Every integration wants its own key, and those keys end up in the agent's config, which means your shell history, your repo, and eventually a log. Connected accounts are held encrypted server side and injected at call time, so the agent gets a result and never the credential.

What it does not do: if you need one API, call that API. A wrapper only pays off when you would otherwise juggle five signups, five keys and five error formats. You are also trusting a third party with credentials to services you care about, which is a real tradeoff and not one I want to gloss over. And it is per-call billing, cheap but not free past the first 5,000 calls.

Caveats on the numbers

A random 60 from one directory is not the whole ecosystem, and the servers people actually use skew toward the well-maintained end. The chars / 4 approximation is rough. And the 27 that did not answer are excluded entirely, which probably biases the sample toward servers that are happy to talk to strangers, and those may well be simpler than the ones behind auth.

The script is above. If you run it against the servers you actually have connected, I would genuinely like to see what you get, because the number that matters is yours, not my median.

So

Go look at what you have connected right now and add up the tool counts. My guess is it is more than you think, and that a third of it is stuff your agent has never once called.

What is in your context window that does not need to be?

Top comments (1)

Collapse
 
mads_hansen_27b33ebfee4c9 profile image
Mads Hansen

The catalog idea becomes much safer if tool definitions are treated as versioned, identity-scoped artifacts, not globally cacheable docs. A schema can vary by authenticated principal, tenant, feature flag, or server revision; serving a stale or wrong cached definition can advertise authority the caller does not actually have (or hide a newly removed capability).

I'd bind each discovered definition to {server, schema digest, principal/tenant scope, expiry} and require the execution request to carry the digest it planned against. On mismatch, rediscover instead of guessing. Then measure not only input tokens but stale-schema failures, false-unavailable tools, wrong-tool selection, added round trips, and completion rate.

That makes the load-on-demand tradeoff observable: fewer tokens, without silently turning discovery into a cache-coherence or authorization bug.