DEV Community

hao li
hao li

Posted on Edited on Originally published at github.com

Your MCP servers are eating your context window. I built a CLI to audit the tax

Every MCP server you connect injects all of its tool schemas into every request. Claude Code loads them at session start, and there is no UI to temporarily disable a server you don't need right now. People have measured 41k tokens of pure schema; one estimate says 6 mid-size servers eat 10–15% of the window before the conversation even starts.

Nobody measures this per server. So I built a tiny CLI that does.

mcp-tax

mcp-tax audits the context tax of your MCP servers, then lets you launch Claude Code with the expensive ones switched off — per session, without touching your real config.

Zero dependencies. Python standard library only.

pip install mcp-tax
Enter fullscreen mode Exit fullscreen mode

See what you have configured (reads ~/.claude.json plus ./.mcp.json when present):

$ mcp-tax list
github
    npx -y @modelcontextprotocol/server-github
postgres  [off]
    uvx mcp-server-postgres --db-url ...
2 server(s), 1 disabled
Enter fullscreen mode Exit fullscreen mode

Measure the tax — it handshakes each server over stdio (initialize, then tools/list), counts tools, and estimates tokens:

$ mcp-tax audit
server    tools  schema chars  est. tokens
------  -------  ------------  -----------
github       51       118,203        29,551
postgres [off]    9        12,440         3,110
------  -------  ------------  -----------
TOTAL        60       130,643        32,661
~16.3% of a 200k context window (est. tokens = schema chars / 4)
Enter fullscreen mode Exit fullscreen mode

Toggle servers off/on (persisted in ~/.config/mcp-tax/disabled.json):

$ mcp-tax off postgres
postgres disabled (affects `mcp-tax run`)
$ mcp-tax on postgres
postgres enabled (affects `mcp-tax run`)
Enter fullscreen mode Exit fullscreen mode

Launch Claude Code without the disabled servers:

$ mcp-tax run -- -p "summarize this repo"
Enter fullscreen mode Exit fullscreen mode

This writes a filtered {"mcpServers": ...} config (disabled servers removed) and execs claude --mcp-config <file> --strict-mcp-config with your args forwarded. Note: --mcp-config <file> alone only layers that file's servers on top of the servers from ~/.claude.json / .mcp.json — it doesn't replace them. What actually isolates the session is --strict-mcp-config ("only use MCP servers from --mcp-config, ignoring all other MCP configurations"), which mcp-tax always passes, so disabled servers truly stay disabled. Your real ~/.claude.json is never modified. (Thanks to dev.to reviewer Arhan Canli for catching the inaccurate description.)

What's new in 0.2.0

Schemas tell you what a server could cost. Your transcripts tell you what it actually costs — and who is spending it. That question kept coming up in the HN "Pi" thread: "who is calling this MCP, using what agent, for what project?" So v2 answers it.

Who is calling this MCP — who-called mines your local Claude Code transcripts (~/.claude/projects/*/*.jsonl, read-only, nothing leaves your machine) and attributes every MCP tool call to an agent, a project, and a purpose:

$ mcp-tax who-called --server github
time              project    agent       tool                purpose
----------------  ---------  ----------  ------------------  -------------------
2026-10-10 09:03  proj       explorer    github__search_code  fix the github action workflow
2026-10-10 09:01  proj       main        github__get_workflo fix the github action workflow
2 call(s); attribution is heuristic, see README
Enter fullscreen mode Exit fullscreen mode

Main agent vs subagent, which project, and the nearest adjacent text as the purpose hint. --days and --limit bound the lookback.

One audit for all four context sources — MCP schemas aren't the only thing your sessions always pay for. audit --all puts rules, memory, skills, and MCP calls in one metric frame:

$ mcp-tax audit --all
source        count  est. tokens   share
----------  -------  -----------  ------
rules             4          320    8.1%
memory            2          410   10.4%
skills            6        1,200   30.5%
mcp calls        38        2,010   51.0%
----------  -------  -----------  ------
TOTAL                       3,940  100%
Enter fullscreen mode Exit fullscreen mode

CI gate — turn the audit into a budget your CI enforces:

$ mcp-tax gate --max-tokens 50000 --max-calls 200
total: 3,940 tokens, 38 mcp calls (last 30 day(s))
GATE PASS
Enter fullscreen mode Exit fullscreen mode

Exit 1 on breach, with the breach named. Your context budget stops drifting silently.

How this differs from the token-audit skills out there: those are interactive — a chat walkthrough of your usage. mcp-tax is a deterministic CLI: same transcripts, same output, every run, --json on everything, and a gate that fails builds. Skills are for conversations; this is for automation.

mcp-tax vs mcp-audit (my other new tool): complementary axes. mcp-tax is call attribution and cost audit — who calls which MCP, from which agent and project, and what it costs. mcp-audit (PyPI: mcp-runtime-audit) is runtime security — known-CVE checks and one-click fixes for your servers. Cheap is not the same as safe, and safe is not the same as cheap. Run both.

How the token estimate works

For each server, mcp-tax JSON-encodes the full tools/list result (compact, no whitespace) and counts characters. Estimated tokens = round(chars / 4) — the Anthropic tokenizer's ~4-chars-per-token rule of thumb on English/JSON text.

Deliberately crude. Real counts vary with the tokenizer and how Claude Code wraps schemas, so treat it as an order-of-magnitude gauge: good enough to answer "which server is eating my window?", not a billing meter.

Honest limitations

  • stdio servers only. SSE / streamable HTTP servers aren't audited.
  • Audit uses select(2) on the server's stdout pipe — fine on Linux/macOS, not on Windows.
  • The estimate ignores runtime behavior: a 2-tool server can still be expensive if its tool results are huge. This measures schema cost only.
  • The --mcp-config flag for run comes from Claude Code's documented CLI options; it couldn't be verified on the machine where this was built (no Claude Code CLI there). If the flag name ever changes, run prints the filtered config path so you can pass it manually.
  • Attribution is heuristic. who-called reads the transcript format as of Oct 2026; sidechain/agent fields are inferred, not contractual, and the purpose snippet is the nearest adjacent text. An investigator's lead, not a log line.

Links

20 tests pass (11 unittest + 9 smoke), including a fake stdio MCP server that speaks the same newline-delimited JSON-RPC 2.0 framing as real servers — so the audit math is tested against a realistic handshake, including stdout noise and timeout cases.

If you run it against your own setup, I'd genuinely like to know: which server turned out to be your biggest tax?


Try it yourself: pip install mcp-tax — GitHub repo

Top comments (3)

Collapse
 
neelagiri65 profile image
Srinathprasanna N S •

Biggest one on my machine: a single server costing 29,425 tokens at connect for 92 tools. Cloudflare's, for comparison, was 3,570 for 23. Did you find the count tracks the number of tools, or are a few long descriptions doing most of the damage?

Collapse
 
arhancanli profile image
Arhan Canli •

Worth checking before people rely on mcp-tax run: in Claude Code, --mcp-config adds the servers in that file on top of the ones already configured in ~/.claude.json and .mcp.json; it doesn't replace them. The help text says so: --strict-mcp-config is the flag that means "only use MCP servers from --mcp-config, ignoring all other MCP configurations". Without it, the servers you switched off still load from the user config, and the session pays the same tax. Adding --strict-mcp-config to the argv built in runner.py (line 45) should fix it, and the smoke test that asserts argv[:3] would pin it.

A cheap end-to-end test for that: start a session through mcp-tax run with one server off, and check /mcp (or /context, which breaks the window down by MCP tools) to confirm the disabled server is absent. That also gives you a measured number to compare with the chars/4 estimate.

Collapse
 
haoli profile image
hao li •

Thanks for the correction — you're right, I had it backwards. --mcp-config overlays onto the existing config sources, so a disabled server can still sneak back in from ~/.claude.json and the session pays the tax anyway. --strict-mcp-config is the actual isolation flag. I'll fix that in the article, and the smoke test idea (asserting argv[:3]) is a good call — going to add that to runner.py. The /mcp end-to-end check is a solid suggestion too; a measured number beats my chars/4 estimate. Appreciate you catching this.