Originally published on ImportStatic.
The issue-writing tool in GitHub's MCP server costs 807 tokens to describe to a model. The help page for gh issue create costs 869. So much for the idea that an MCP tool is inherently heavier than a CLI command.
That comparison comes from a small Go program we wrote after reading a Hacker News discussion of a post about an agent harness adding MCP support. One side said MCP eats context on every turn whether you use the tools or not. The other said it depends on the harness. That's an argument a measurement can settle. Both sides turn out to be right, and the MCP token overhead is easy to measure: GitHub's server with its default settings is 11,207 tokens of tool definitions, and whether those tokens reach the model is now a harness setting.
What we counted, and with which tokenizer
The program starts a server over stdio with the official Go SDK, lists its tools, and serializes each one the way a model API wants it: name, description and input schema. Then it counts tokens.
tiktoken.SetBpeLoader(tiktoken_loader.NewOfflineLoader())
enc, err := tiktoken.GetEncoding("o200k_base")
if err != nil {
log.Fatal(err)
}
count := func(s string) int { return len(enc.Encode(s, nil, nil)) }
for tool, err := range session.Tools(ctx, nil) {
if err != nil {
log.Fatal(err)
}
def := functionTool{Name: tool.Name, Description: tool.Description, InputSchema: tool.InputSchema}
defs = append(defs, def)
r := row{
name: tool.Name,
total: countJSON(def),
desc: count(tool.Description),
schema: countJSON(tool.InputSchema),
}
Two caveats before the numbers. The tokenizer is o200k_base, which OpenAI's tiktoken table maps to the gpt-4o, gpt-4.1, gpt-5 and o-series names. Claude models have their own tokenizer, and the way to count with it is Anthropic's token counting endpoint. It's free and accepts a tools array, but it needs an API key and this machine doesn't have one. So these are OpenAI-encoding counts of compact JSON. Second, a provider is free to render tool definitions into the prompt in its own format, so what the model sees can differ from the JSON you send. Treat the figures as close, not exact.
All four servers were built from source at their latest tagged versions as of October 6, 2026, and started with dummy credentials. tools/list doesn't need real ones.
The MCP token overhead of four real servers
| Server | Tools | Definitions | Per tool |
|---|---|---|---|
gopls v0.23.0 (gopls mcp) |
8 | 878 tokens | 109 |
| terraform-mcp-server v1.3.0 | 9 | 1,718 tokens | 190 |
| github-mcp-server v1.14.0, defaults | 46 | 11,207 tokens | 243 |
| mcp-grafana v1.6.3 | 81 | 20,493 tokens | 253 |
github-mcp-server v1.14.0, --toolsets all
|
91 | 23,206 tokens | 255 |
Small servers are cheap. gopls gives a model eight tools for less than a page of text. The trouble starts when a server mirrors a whole product API: GitHub with every toolset plus Grafana is 43,699 tokens before the user has typed anything.
That lines up with what Anthropic says. Its tool search documentation gives the example of a five-server setup (GitHub, Slack, Sentry, Grafana and Splunk) that "can consume ~55k tokens in definitions before Claude does any work." Different tokenizer, different server versions, same order of magnitude.
And yes, without deferral you pay it on every request. Model APIs are stateless. Anthropic's pricing notes say tool use is billed on input tokens "including in the tools parameter", and that the parameter's tokens are the "tool names, descriptions, and schemas". On top of that sits a fixed tool-use system prompt, which Anthropic lists as 286 tokens for Claude Opus 5.5 and Sonnet 5.5. Next to 11,207, that part is noise.
Most of it is JSON Schema, not prose
Here's the full output for the GitHub server with default settings:
server github-mcp-server v1.14.0
tools 46
definitions 11207 tokens (243 per tool)
descriptions 1368 tokens
input schemas 9287 tokens
output schemas 0 tokens on 0 tools (not included above)
annotations 964 tokens (not included above)
instructions 377 tokens
names only 185 tokens
names + 1st line 1190 tokens
largest tools:
issue_write 807 tokens (description 14, schema 783)
pull_request_read 560 tokens (description 12, schema 537)
list_issues 549 tokens (description 33, schema 505)
smallest tool:
get_me 54 tokens (description 35, schema 9)
The tool descriptions, the part people imagine when they say "tool descriptions waste tokens", are 12% of the total. The input schemas are 83%.
Look at issue_write. Its description is one sentence, 14 tokens. Its schema is 783, because it has seventeen properties and several of them carry a paragraph of usage rules. This is one property out of the seventeen, copied from the server's response:
"parent_owner": {
"description": "Repository owner of the parent issue. Must be provided with parent_repo. Omit both to use owner and repo. Only used when method is 'create' and parent_issue_number is provided.",
"type": "string"
}
Grafana's server leans the other way, with 5,777 tokens of descriptions against 13,601 of schema, but schemas still dominate.
If you write a server, that's where to look. Merging "create" and "update" into one tool with a method argument, as GitHub does here, saves a tool slot and then spends the saving on parameters that only apply to one of the two methods. I wouldn't call that wrong. It's a trade, and it's worth knowing which side of it your tokens are on.
One more line in that output deserves a look: instructions. Servers can return free-text instructions when a client connects. GitHub's are 377 tokens with default toolsets and 1,559 with all of them. Terraform's are 924, more than half the size of its nine tool definitions. If you audit a server, count those too.
A CLI isn't smaller, it's just lazy
The usual counter-proposal is to skip MCP and let the agent run gh. We built gh v2.102.0 from source and counted its help output with the same tokenizer.
| What the agent reads | Tokens |
|---|---|
gh --help |
610 |
gh issue --help |
346 |
gh issue create --help |
869 |
gh issue edit --help |
1,068 |
MCP issue_write (create and update) |
807 |
An agent that starts cold and walks down to gh issue create reads 1,825 tokens of help. The two help pages that cover what issue_write does add up to 1,937. Operation for operation, the MCP definition is the more compact of the two.
What the CLI has going for it is timing. Nothing is loaded until the agent asks, and an agent that never files an issue never reads the page. A model may also know gh well enough from training to skip the help entirely; we can't measure that here, and it won't be true for your internal tools.
So the real difference was never the format. It's eager versus lazy loading. And that is exactly the part harnesses have changed.
With tool search, the bill drops to a list of names
Anthropic's API has a tool search tool. You mark tools with defer_loading: true, and the docs are specific about what that does: it "controls what enters the context window, not what you send in the request." The definitions still travel in the tools array, but the model only sees the ones a search returns, up to five per search by default. Anthropic reports this "typically reduces" definition tokens "by over 85 percent". For MCP servers attached through its connector, you set the flag once for the whole server.
OpenAI's Responses API has the same idea: a tool_search tool, supported on gpt-5.4 and later, plus defer_loading on functions or MCP servers. For a deferred server the model initially sees, in the guide's words, "only the namespace or server name and description". OpenAI recommends keeping each namespace under ten functions.
Claude Code turns tool search on by default. Its docs list the setups that go without: a custom ANTHROPIC_BASE_URL, ENABLE_TOOL_SEARCH=false, and older models on Google Cloud. With it on, the docs describe Claude Code listing a server's tool names to the model, which then goes through a ToolSearch call to use them.
How small is a list of names? For GitHub's 46 default tools our program counted 185 tokens, against 11,207 for the definitions. Each harness formats its own list, so that's a floor and not a prediction. Then add what a search pulls in: five average GitHub tools is about 1,200 tokens, and only on the turns that need them.
That's the answer to the thread. In a harness that defers, MCP's standing cost is a few hundred tokens. In one that doesn't, it's the whole table above on every call.
What to do if your harness loads everything
Find out first. If you call a model API yourself and pass the tools from tools/list straight through, you're loading everything. (The stateless revision of the spec changes how a client fetches that list, not how big it is.)
Then trim at the server. GitHub's README says it plainly: "Enabling only the toolsets that you need can help the LLM with tool choice and reduce the context size." We measured what the flags buy:
| github-mcp-server v1.14.0 flags | Tools | Definitions |
|---|---|---|
--toolsets all |
91 | 23,206 tokens |
| (defaults) | 46 | 11,207 tokens |
--read-only |
27 | 6,492 tokens |
--toolsets issues |
10 | 3,281 tokens |
--tools issue_read,issue_write,list_issues |
3 | 1,693 tokens |
An agent that triages issues doesn't need 91 tools, and a server started with --tools costs it 7% of the full set.
If you can use tool search, use it, and keep the handful of tools you call constantly non-deferred. Anthropic suggests three to five. Its own threshold for bothering is ten or more tools, or more than 10k tokens of definitions, which the GitHub server crosses on defaults.
And measure your own setup instead of trusting anyone's table, including ours. With an Anthropic key, send your real tools array to count_tokens for the model you run; the docs note that Claude Opus 4.7 and later produce roughly 30 percent more tokens for the same text than earlier Claude models did, so an old count is stale. Without a key, a short Go program and a published tokenizer will tell you which three tools are eating your context.
Originally published on ImportStatic. This article was drafted with AI assistance; every claim was checked against the sources linked in it and the code samples were compiled and run.
Top comments (0)