/context over-reports "System tools" by ~25x and under-reports "Messages" — wire-measured (tools[] is 43KB, reported as 263.3k tokens)
Summary
/context reports the "System tools" category at roughly 25× its true size, and correspondingly under-reports "Messages". The displayed total is approximately right, so this is a category-attribution error rather than phantom tokens.
Measured on the wire, not estimated: every /v1/messages request body for the session was captured by a local proxy, so the comparison is against what Claude Code actually sent.
Evidence
/context output (Claude Code 2.1.220, Linux, claude-opus-5[1m], 428 MCP tools registered across 8 servers, all deferred):
Total: 337.2k / 1m tokens (34%)
System prompt: 3.0k (0.3%)
System tools: 263.3k (26.3%) <-- reported
Custom agents: 1.3k (0.1%)
Memory files: 6.0k (0.6%)
Skills: 6.2k (0.6%)
Messages: 58.1k (5.8%) <-- reported
The same session's actual request bodies:
| component | measured | /context says |
|---|---|---|
| tools[] | 15 tools, 43,312 bytes | 263.3k tokens |
| system | 9,427 bytes | 3.0k tokens |
| messages | 1,228,694 bytes (387 messages) | 58.1k tokens |
The core claim needs no tokenizer assumption: tools[] is 43,312 bytes on the wire. For that to be 263.3k tokens would require ~0.16 bytes per token, which no tokenizer produces. The reported figure is wrong by at least an order of magnitude regardless of how tokens are counted.
Using a rough 4-bytes-per-token estimate for scale (approximate, and only used for the split): tools ≈ 10.8k, messages ≈ 296k. Totals then reconcile with the displayed 337.2k — so the total is roughly right while the split between the two largest categories is inverted.
Deferred tools are working correctly
Across 378 captured requests carrying a tools[] field:
| tools in request | requests |
|---|---|
| 9–15 | 377 |
| 428 | 1 |
The single 428-tool request had 1 message and no system prompt — a token-counting probe, not a conversation turn. Every real turn carried 9–15 tools. So on-demand MCP tool loading is behaving exactly as designed; the 428 registered tools are not being sent, and the over-report is display-only, not wasted context.
For completeness: messages[0] contains zero mcp__ strings, and the deferred-tool name listing injected into messages[1] is 40,671 bytes (~10k tokens). Even attributing that listing entirely to "System tools" — a defensible reading, since it is tool overhead transported inside messages — gives ~21k, not 263.3k.
Proxy excluded as a cause
The capture runs through a local MITM proxy that does mutate requests, so that was checked rather than assumed:
- The capture point is pre-pipeline (confirmed in the capture extension's source), so the measured bodies are Claude Code's own output, before any local rewriting.
- A replay gate over the same captures reproduces the recorded forwarded-body hash byte-for-byte for all mutated requests — 348/348 fidelity matches, 0 safety, 0 sequence, 0 order violations.
Both would have to be wrong simultaneously for tools[] to be anything other than 15 tools / 43,312 bytes.
Relationship to existing issues
- #80527 (open) reports the inverse:
/contextfolding tool-schema tokens into "Messages", with no separate System-tools row, and requests that such a row be added. This session has System tools / MCP tools rows — so that request appears to have been implemented, and the new attribution now over-counts in the opposite direction. This may be a regression introduced by that fix. - #82260 (open) — "Messages" containing tokens with no corresponding text; same family, different quantity.
- #71301 (open) —
/contextover-counting memory files and custom agents in 2.1.191 vs 2.1.179; same class of accounting regression. - #9425, #41181 — both closed as not-planned by the inactivity bot; earlier reports of per-tool overcounting and Messages exceeding the total.
What I cannot explain
Where 263.3k comes from. It is not the size of tools[], not the deferred-tool listing, and not the two combined. I have no mechanism to offer, only the measurement that the figure is wrong by roughly 25×.
Impact
Users reading /context to decide what is consuming their window are pointed at the wrong culprit. In this session the real driver was conversation length (~90% of usage), while the display attributed 26.3% to tool definitions — which invites pointless MCP-server pruning that would save nothing, since those tools are deferred and never sent.
Environment
- Claude Code 2.1.220, Arch Linux,
claude-opus-5[1m](1M context) - 8 MCP servers, 428 tools, all deferred / loaded on demand
- Session at ~337k reported / ~324k measured
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗