p4ni.

Research

Where Claude Code's 39,810 Startup Tokens Go

· 7 min read

On this page

A post titled “Claude Code sends 33k tokens before reading the prompt; OpenCode sends 7k” spent a day near the top of Hacker News with 396 comments, and the comments split the way these always do: half saying that’s outrageous bloat, half saying it’s all cached so who cares. Nobody in the thread was measuring their own machine.

I have 89 Claude Code sessions sitting in ~/.claude/projects/ for this blog’s repo, each one a JSONL transcript with the token accounting still in it. So I measured mine. The answer today is 39,810 tokens before I type anything — and more usefully, I can now tell you exactly which parts of my setup bought which share of it.

Reading the number off your own machine

Every assistant message in a session transcript carries a usage object. The first one tells you what the model was handed before it saw your opening line, and it’s the sum of three fields — fresh input, tokens written to cache, and tokens read from cache:

total = (u["input_tokens"]
         + u["cache_creation_input_tokens"]
         + u["cache_read_input_tokens"])

That distinction matters for the “it’s cached, who cares” argument. Cache reads are cheap, not free — roughly a tenth of input price — and they still occupy context window. The 39,810 is real occupancy regardless of what it costs.

Charted across all 89 sessions, the striking thing isn’t the size. It’s the slope.

DateSessionsPrefillCLAUDE.md
Jul 27first session33,5781,838 B
Jul 2936,4374,801 B
Aug 338,2027,275 B
Aug 739,8079,240 B
Aug 11latest39,8109,240 B

Across 89 sessions: min 30,089, median 37,425, max 44,974. In sixteen days of ordinary work the floor rose 18.6% — same Claude Code version, same model, no new tools installed. The project simply accumulated instructions, and every session since has paid for all of them.

Taking it apart with a difference method

The transcripts tell you the total, not the breakdown. For that you need to change one thing at a time and re-measure, which headless mode makes easy — each claude -p run reports its own usage:

claude -p "hi" --model haiku --output-format json | python3 -c "
import json,sys
u = json.load(sys.stdin)['usage']
print(u['input_tokens'] + u['cache_creation_input_tokens'] + u['cache_read_input_tokens'])"

Run that in an empty directory and you get a baseline. Add one thing, run it again, and the delta is that thing’s price. Everything below is Haiku 4.5 unless noted, repeated 2–3 times per condition. Measurement noise turned out to be a clean ±241 tokens, so anything smaller than that is nothing.

ConditionPrefillDelta
Empty directory26,177baseline
7 MCP servers disabled25,932−241
+ CLAUDE.md (9,240 B)29,640+3,463
+ 5 skills25,9560
+ 40 skills26,248+57

Three findings there, and two of them surprised me.

MCP costs almost nothing now

I have seven MCP servers connected — Context7, Gmail, Google Calendar, Google Drive, Cloudflare, a Three.js viewer, sequential-thinking. The received wisdom, and half of the Reddit threads on this topic, says MCP is the expensive one: every server’s full tool schemas land in your context whether you use them or not.

Disabling all seven changed the prefill by 241 tokens. That’s the noise floor.

The reason is deferred tool loading. Rather than injecting complete JSON schemas at startup, Claude Code now lists tool names and fetches the schema on demand when one is actually needed. Seven servers’ worth of tools reduces to a name list. The “MCP context tax” that everyone writes about was real, and then it was quietly fixed — which is a good reminder that measurements of agent internals expire fast.

Update: “fixed” turned out to be the wrong word. I measured the other half a week later — a tool costs about 15 tokens sitting there and 300–700 the moment something loads its schema. The tax became metered rather than disappearing, and the 241 above is roughly what a name list for those servers should cost.

Forty skills cost nothing — in headless mode

I generated dummy skills with realistic 300-byte descriptions and scaled from 0 to 40. Prefill went from 26,191 to 26,248. Forty skills, 57 tokens, well inside noise.

That looked wrong, so I checked whether they were being registered at all. They were — asked how many lab-widget skills it could see, the model answered 40. But asked what lab-widget-7 actually does, with file tools disabled:

NO-DESCRIPTION-VISIBLE

Same answer with five skills installed. In headless mode Claude Code hands the model skill names, not descriptions — the same progressive-disclosure trick MCP now uses. The description only loads when the skill does.

Interactive sessions are a different story, and that’s where the last measurement comes in.

Headless 30,668, interactive 39,810

Same repository, same model, same day:

ModePrefill
claude -p (headless)30,668
Interactive session39,810
Difference+9,142

Nine thousand tokens buy you the things a headless run doesn’t need: the full text of every skill description, the available subagent roster, the deferred tool name list, and the conversational scaffolding around how to talk to a person. My own skills are a visible line item there — one of them has a description field that enumerates every phrasing that should trigger it, and that field alone runs 2,350 bytes, more than my other nine skills combined.

So the honest version of “do skills cost anything?” is: not in automation, yes in the UI, and the price is set by how verbose you made the description field — the one part of a skill that is always resident. The body of the file, however long, stays on disk until invoked. I’ve written before about what actually lands in context when a skill fires and about auditing the skills you publish; this is the same architecture seen from the cost side rather than the security side.

CLAUDE.md is the one that scales with you

Every other component turned out to be flat or free. CLAUDE.md is linear, and it’s the only thing in my setup that grows every week:

CLAUDE.md sizeAdded tokens
1,000 B+202
2,000 B+578
4,000 B+1,327
6,000 B+2,287
9,240 B+3,463

Roughly 375 tokens per KB at the top end — but note that mine is written in Japanese, which is dense in UTF-8 bytes and expensive in tokens. The first kilobyte, the English-language header, ran about 200 tokens per KB. If your CLAUDE.md is English, halve my numbers. If it’s Japanese or Chinese, don’t.

Either way, this is the file that answers the mystery in the first table. My CLAUDE.md went from 1,838 bytes to 9,240 bytes in sixteen days — every convention I discovered and wrote down, every “don’t do that again” — and it took the session floor up with it, one commit at a time, permanently, for every session afterward.

What I actually changed

Nothing, yet, and that’s the point of measuring: three of the four things I might have optimized turned out to cost nothing.

The instinct after reading a thread like that HN one is to start pruning MCP servers and uninstalling skills. On this machine that would have recovered 241 tokens and 57 tokens respectively, out of 39,810. Meanwhile the file I’d never thought of as a cost center — the one I add to almost daily, in the language that happens to tokenize worst — is worth 3,463 and climbing.

The number worth watching isn’t the total. It’s the slope: 18.6% in sixteen days from nothing but writing things down. At that rate a year of the same habits puts CLAUDE.md alone into five figures. There’s a real tension here, because those instructions are what make an agent useful on a specific codebase — I’m not going to stop writing them, and portability of that setup has its own problems. But it does mean CLAUDE.md deserves the same editing discipline as production code, including deletions. Skills, meanwhile, are nearly free to hoard as long as you keep the description line tight.

If you want your own numbers, both measurements above take about a minute: the Python snippet against your newest transcript in ~/.claude/projects/, and claude -p "hi" in an empty directory for your baseline. The gap between those two is what your project setup costs you, every single session.