Context-tax prices what Claude Code sends before you type

On one real machine, a single Claude Code built-in tool took up 11,402 tokens of a 47,700-token first request. That works out to about 24% of everything sent before the user typed a word. The figure comes from the context-tax README on GitHub. The author got it by running an interactive session once with a permissions.deny rule naming that tool and once without. The CLI is built to find line items like that one in your own setup.
You run it with npx context-tax. There is nothing to install, no account, no telemetry, no LLM call and no context-tax server for anything to be sent to. It works with Claude Code only. The author describes the gap it fills like this: every article about MCP context bloat ends with "audit which tools your agent actually calls, and prune the rest," and none of them automate that sentence. One caution before the numbers: every figure in the README's sample report is made up and comes from an invented config. The screens themselves are real renderer output, generated from a fixture, so the docs can't drift from the code.
Read from transcripts
context-tax doesn't guess what a client probably sends. It reads what the client did send. A recent Claude Code writes the prompt's contents into the session transcript: the skill listing as it went out, the tool names, each instruction file, and its own tools and system prompt. The tool reads that record and keeps only sizes and names. It drops the text on the same line, and a test checks that it does. It also reads the same transcripts for tool_use blocks to count what you actually called. The per-turn total comes from the usage your API calls were billed on. By default that is the median first request across your 10 most recent sessions, and you can change the number of sessions with --window.
So the total is exact and the rows are estimates. Rows are measured in characters and converted to tokens at chars/4. The author says this ratio was set in advance and then checked by running it: adding one MCP server to a real Claude Code prompt cost 912 tokens, where chars/4 predicted 948. Whatever the rows don't account for goes into its own "unattributed" row instead of being spread across the others. If the rows ever add up to more than the billed total, the run says so and exits non-zero.
The report has two columns per MCP server because of how current Claude Code handles schemas. According to the README, it doesn't put a server's tool schemas in the prompt. It sends the tool names and the server's instructions, then loads a schema when something reaches for it. "tokens" is what every turn carries. "deferred" is the schema cost, paid only when something loads it. For scale, davidmoneil measured seven MCP servers taking 67,300 tokens of tool definitions on claude-code issue #11364, filed November 10, 2025 and titled "Lazy-load MCP tool definitions to reduce context usage." That was 33.7% of a 200k window, with GitHub MCP alone at about 18k tokens for 27 tools. According to MCP.Directory, Tool Search is already on in Claude Code v2.1.7 and later.
In the invented example, Claude Code's 14 built-in tools take 22,000 of the 40,000 tokens sent per turn. All MCP servers together come to 2,600, about an eighth of that. Most of the client's own block can't be switched off: the system prompt, the tool name list and the session details have no lever. The tools are the exception. A permissions.deny rule takes a tool out of what is sent, which is how the 11,402-token figure was measured.
To measure what an MCP server weighs, context-tax has to start it. It runs the same initialize and tools/list handshake your agent does, over stdio, streamable HTTP or legacy SSE. It reports which hosts it contacted. It caches schemas for a week, keyed on the names of your environment variables and never their values. It won't start a project's servers from a directory you aren't standing in. --no-spawn starts no servers at all and works from the cache plus a bundled table of well-known servers.
Cost per use
The author expects the "per call" column to be the one that changes behaviour. In the sample, sentry costs 400 tokens a turn and was called once in 96 sessions. Re-sent across 42,900 turns, that adds up to 17,160,000 tokens of standing cost for that single call. The README's point is that 400 tokens is not much on its own. The cost comes from sending it on every turn so it can be used once. In the same sample, linear costs 300 tokens a turn for 3 calls, or 4.3M tokens per call.
Per-call numbers are only meaningful if the denominator is right, and most of the README's rules are about getting it right:
- No server is called unused without the window behind that verdict, printed alongside it: "0 calls in 96 sessions since it was configured", never a bare "unused".
- claude mcp remove <name> -s user removes a server from every project on the machine, so the tool never recommends it just because one project is quiet. Machine-wide levers are judged on machine-wide history. A .mcp.json server exists in only one project, so it is judged only on that project.
- Server age comes from git log or the plugin install record, never from file modification times. git checkout rewrites those times and would make every server on a fresh clone look new. Servers in ~/.claude.json are reported as unknown age, so their per-call figure counts as an upper bound.
- A server that couldn't connect shows 0 with "could not connect" and gets no finding. A server that is configured but switched off shows "-", because it was never started to be weighed.
- Claude Code writes one API reply as several transcript lines, each repeating the usage. Versions before 0.4.0 counted lines, so their turn and per-call figures came out at about twice the true value.
No dollar figure is printed. The README's reason is that "a wrong dollar figure is more convincing than a wrong token count."
Skills that arrive blank
Only one finding is about what your agent is missing rather than what you're overpaying for, and it concerns skills. Claude Code gives the skill listing a budget of 1% of the context window, counted in characters. That is about 8,000 characters on a 200,000-token window and 40,000 on a million-token window. Every skill keeps its name. Descriptions compete for whatever room is left, and the ones that lose are sent as a bare name, which gives the model nothing to choose by. A skill you installed last week can be invisible in every session since, and nothing on screen tells you.
context-tax counts these one by one from the listing a session sent, so this number is a count and not an estimate. It groups them by owner, because the usual cause is one large plugin crowding out your own skills. In the sample, 9 of 31 skills went out without a description: 7 of 12 from a design-kit plugin and 2 of the user's own 14. The listing needed about 11,640 characters against a cap of about 8,000. The window size isn't assumed either. The tool works it back from the listing, which is why the screen says "about." When a listing fits under its budget, no window or percentage is claimed. This ties in with our September 18 piece on a linter that found 69% of 215 public skills had descriptions that wouldn't reliably trigger. A description that never reaches the model can't trigger at all.
The tool prices both ways out. context-tax fix --restore-descriptions sets skillListingBudgetFraction so that every description is sent. In the sample it sets it to 0.015, for about 910 more tokens per turn. It is never part of a plain fix, because it is the only change that makes every turn more expensive. The other way out is to switch skills off. Past the budget, though, that saves close to nothing, because the freed room goes to another description that was being dropped. In the sample fix run, the two skills it changes add nothing to the 700 tokens recovered. The two servers account for all of it.
What fix touches
context-tax fix is a subcommand, not a flag, so adding one word to a report command can't trigger it by accident. It shows the literal diff instead of a summary and asks "Write this file? [y/N]". Before writing, it backs up whatever it replaces to ~/.cache/context-tax/backups/ (directory 0700, files 0600). It writes to a temporary file, renames it into place, and prints where the backup went. Restoring is a cp. It refuses to write when stdin isn't a terminal unless you pass --yes. It won't rewrite a settings file that doesn't parse, and running it twice changes nothing the second time. After a fix, once a new session has started, the report's first lines show your first request before and after, using the same median on both sides.
Each finding goes to the setting that actually turns that thing off:
- A server from <repo>/.mcp.json is added to disabledMcpjsonServers in .claude/settings.local.json.
- For a server from ~/.claude.json, the tool prints claude mcp remove <name> -s <scope> for you to run yourself.
- A plugin gets enabledPlugins["<plugin>@<marketplace>"]: false, scoped to the project.
- A skill is set to off. If you type it but the model never picks it, it is set to user-invocable-only instead: the slash command keeps working and only the description leaves the prompt.
- An agent that was never called gets Agent(<name>) in permissions.deny. The agent listing has no cap, so this saving is real.
- A claude.ai connector appears in no file on the machine, so the finding tells you to switch it off in your claude.ai settings.
MCP has no per-tool switch. In the sample, 9 of the github server's 26 tools were never called and cost 415 tokens per turn. The advice there is to ask the server for a narrower tool set or drop the server. Removing anything you have actually used requires a level you choose. safe, the default, covers what you don't use. lean adds Claude Code's own tools and plugins used in under 1 session in 20. max extends that to under 1 in 5. In the sample, --level lean denies Workflow (2,300 tokens) and ScheduleWakeup (2,000 tokens), for 4,300 fewer tokens per turn. Each stops working until its rule is removed.
The tool never writes to ~/.claude.json and never backs it up. That file sits next to your API keys and bearer tokens, and a backup would be a second copy of every credential. Earlier tools made a different choice: Scott Spence writes that McPick, the server toggle he built, creates backups and modifies .claude.json. The diff also shows one change you didn't ask for. fix rewrites the whole file with two-space indentation, so an allow array written on one line comes back with one element per line.
Memory files and connectors
The output also states what it can't measure. It can tell you what your CLAUDE.md and memory files cost, 1,600 tokens per turn in the sample, but not which instructions in them the agent actually used. The README puts it this way: "Nothing in a log can: that needs the model's attention, not your history." claude.ai connectors show up only because a session sent them. In the sample, claude_ai_Notion costs 900 tokens per turn for 2 calls, and no file declares it. Running /mcp in a session shows where it connects from.
The shareable screens carry no names. context-tax receipt prints the totals. context-tax session --svg draws one session as a sawtooth that never drops below its fixed prefix: 41,200 tokens across four compactions in the sample. Each pass is also exported as a library, including planFixes and applyFixes. According to the development notes, every guard was mutation-tested. Three of the bugs this caught were plausible-looking zeroes, found only by running the tool on real projects.
To see the changes without making them, run npx context-tax fix --dry-run. It shows every line that would change and then stops, without touching your settings. It still starts your servers to measure them, the same as the report does.
Up next
Claude Code team hands most code reading to Claude
Anthropic's Claude Code team moved most code reading to machines, and merged PRs more than doubled from June to September while the share of reverts fell by more than 40%. Would you let machines do most of your code reading, or keep a human on every diff?
Related stories
- Claude Design can draw with your real components
- 57% of public Claude Code subagents inherit Bash
- Toolog keeps a forensic log of Claude Code tool calls
- Stemma compiles agent rules into CLAUDE.md and AGENTS.md
- Spotify's shunt blocks Claude Code reads over 350 lines
- Context Engineering Kit adds judge agents to Claude Code
Comments
No comments yet. Be the first.
Join the conversation
Sign in to leave a comment. It appears after moderation.

