/context over-reports "System tools" by ~25x and under-reports "Messages" — wire-measured (tools[] is 43KB, reported as 263.3k tokens)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 29, 2026

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:

  1. 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.
  2. 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: /context folding 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) — /context over-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

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗