You can already get usage.input_tokens back from an API call. What you can't get is the cost of JIRA-1234 — a multi-turn, tool-calling, cache-heavy agent session that spanned two days and three sittings.
That's the instrumentation gap, and Claude Meter closes it at the layer where you actually work: the CLI/IDE coding loop. This post is the engineer's cut — the cost model, the hook architecture, and the token math that makes a 452K-token session cost 34 cents.
The attribution gap, stated precisely
Token spend is a first-class line item now, but observability for it is stratified by how the model is invoked:
| Invocation surface | Who owns the request loop | Per-work-unit attribution |
|---|---|---|
| Direct API integration | Your code | ✓usage object + your own tags |
| Chat / session status UIs | The vendor UI | ~ Per-session, not per-work-unit |
| CLI / IDE agentic coding | The agent (Claude Code) | ✗The blind spot |
The reason the blind spot exists is structural: in an agentic coding session, you sit above the API. The tool owns the request loop, so you have no natural seam to hang instrumentation on. And the work isn't a request — it's a trajectory:
Prompt
│
▼
┌─────────────┐ tool call (read/edit/bash)
│ Agent turn │ ──────────────────────────────┐
└─────────────┘ ▼
▲ ┌────────────────┐
│ tool result into context │ Tool executes │
└──────────────────────────────────└────────────────┘
│
▼
Final answer
Every loop iteration re-sends accumulated context, spawns tool calls, and burns tokens across several distinct price classes. That trajectory maps cleanly onto a ticket — but nothing connects the token stream to the ticket. Wiring that link is the entire product.
The cost model: how tokens become dollars
The naive mental model — cost = tokens × price — is wrong for agentic sessions, because not all tokens are billed at the same rate. There are (at least) four price classes:
where T_in, T_out, T_cw, T_cr are input, output, cache-write (creation), and cache-read token counts. For a Sonnet-class model the price vector is roughly:
| Class | Symbol | Price ($/M tokens) | Relative to input |
|---|---|---|---|
| Input (fresh) | p_in |
$3.00 | 1.0× |
| Output | p_out |
$15.00 | 5.0× |
| Cache write | p_cw |
$3.75 | 1.25× |
| Cache read | p_cr |
$0.30 | 0.1× |
That last row is why metering matters. In a long coding session the same context gets re-sent on every turn, so without prompt caching your input cost grows roughly quadratically in turns — turn 1 re-sends 1 chunk, turn 2 re-sends 2, ..., turn N re-sends N:
for N turns each adding Δ tokens of context. Prompt caching collapses the re-sent prefix to the cache-read rate — one tenth the price — so the cache hit ratio becomes the dominant lever on session cost:
And the savings from caching are exactly the delta you didn't pay at full price:
A generic billing dashboard reports one blended number. A meter that separates these four classes tells you why a task was cheap or expensive — and whether your context strategy is actually hitting cache. We validate all of this against a real run below.
Architecture: a Stop hook, a state file, a command
Claude Meter is a Claude Code plugin (JavaScript, Node ≥ 18) installed from a marketplace. Naming to keep straight: claude-meter is the marketplace/repo; session-manager is the plugin you install, which exposes /session-manager:meter. Three moving parts:
┌──────────────────────────── Claude Code session ────────────────────────────┐
│ │
│ every turn ──► Stop hook (hooks.json) │
│ │ reads local transcript │
│ │ accumulates 4 token classes + activity │
│ ▼ │
│ .claude/sessions/active.json ◄── live state │
│ │ │
│ end / clear ──────┴────► .claude/sessions/name-id.json ◄── archive │
└──────────────────────────────────────────────────────────────────────────────┘
Slash command ── /session-manager:meter ─► reads/controls state
Skill ── natural-language trigger ─► same underlying logic
-
The
Stophook — registered viahooks/hooks.json, fires after every turn, reads the local transcript, and accumulates the four token classes plus activity counters into the active session. This is why tracking is zero-setup: the hook is the collector, and it's already wired. -
Session state — the live tally lives in
.claude/sessions/active.json;end/cleararchives it to.claude/sessions/name-id.json, so history is preserved and queryable. -
Command + skill — drive it with the
/session-manager:meterslash command, or trigger the same logic via a natural-language skill.
Pricing resolves from the LiteLLM model-pricing list, fetched once and cached locally for 24h. If the fetch is blocked (corporate firewall), it falls back to a hardcoded price table — tracking degrades gracefully and no session data is ever transmitted off-box. Everything above is computed locally against the local transcript.
The data model: what a session actually stores
The archived session is plain JSON you own—no black box, nothing exfiltrated. Every number the report prints is a field you can inspect yourself:
The schema is essentially the cost-model inputs plus provenance:
{
"label": "xyz",
"inputTokens": 0, // T_in
"outputTokens": 0, // T_out
"cacheCreationTokens": 0, // T_cw
"cacheReadTokens": 411505, // T_cr
"turns": 11, // N
"toolCalls": 10,
"bashCommands": 7,
"estimatedCostUSD": 0.3379, // C_session
"cacheSavingsUSD": 1.1111, // S
"gitBranch": "...", // provenance: pins cost to repo state
"gitCommit": "..."
}
Capturing gitBranch + gitCommit at start is the detail that makes a session genuinely tied to a moment in your repo's history — cost attribution with a commit anchor, not just a label.
Reading the meter: a worked example
Here's a real collated report from /session-manager:meter stat xyz:
Let's plug the real numbers into the model and confirm it.
i.e. a blended rate 4× below the fresh-input rate of $3/M — a single scalar that tells you your context strategy is doing its job. That's the difference between a bill and a diagnostic: the meter doesn't just say a task was cheap, it shows you the mechanism (ρ ≈ 0.91) that made it cheap.
Modes and the work-unit lifecycle
Invoke /session-manager:meter with no argument for the menu, or pass a mode directly:
| Mode | What it does | Maps to |
|---|---|---|
start [label] |
Begin a tracked session, labeled with the ticket | task kickoff |
show |
Live metrics mid-work | in-flight monitoring |
end |
Stop + print final report | task done |
clear |
Archive current, start fresh | context reset |
resume <name> |
Continue an archived session | multi-sitting work |
stats <name> |
Aggregate all runs for a name into one total | durable roll-up |
token-breakdown [name] |
Attribute tokens: thinking / replies / tools | root-causing cost |
A typical loop:
/session-manager:meter start "JIRA-1234" # kickoff
/session-manager:meter show # peek at live ρ and cost
/session-manager:meter end # final report
# later, across sittings:
/session-manager:meter resume "JIRA-1234"
/session-manager:meter stats "JIRA-1234" # one honest number for the ticket
Two design choices carry the weight:
-
resume+statsmake the work unit durable: a ticket spanning three sittings across two days still rolls up into a single figure —C_ticket = Σ C_session_iover all runs sharing a label. -
token-breakdownturns the meter into a profiler. Knowing a task spent its budget on thinking tokens vs. tool churn tells you something actionable about how the work was structured — the same way a flame graph tells you where CPU went.
Why quantification compounds
Measurement changes behavior. Once per-task cost is visible, value compounds on two timescales.
Short term
-
Immediate per-task cost. The instant
endruns, you have tokens, dollars, and wall-clock for the ticket — no monthly-invoice lag. -
Live course-correction.
showmid-session catches a runaway while it's running: if a "simple" fix is 5× the tokens you expected, that's a signal (bad context, thrashing, over-broad prompt) before you burn more. -
Like-for-like comparison. Session ≙ work unit, so "this refactor cost 2× that one — why?" becomes answerable via
token-breakdown. - Zero-friction adoption. Tracks out of the box, stores locally, adds no external dependency — the cost of starting to measure is ~0.
Long term
-
Baselines per task class. Aggregate enough sessions and a bug fix trends at
Xtokens, a migration atY. Estimates become distributions, not vibes. -
Defensible ROI. Pair
C_sessionagainst elapsed time: this task cost $C and saved H hours. That's the sentence finance actually wants. -
Real optimization targets. Attribution → attack: surface token-hungry task classes, tune prompt/context strategy (drive up
ρ), and measure whether the number moved. - Forecasting. Per-work-unit history turns "what will AI-assisted dev cost next quarter?" into a projection grounded in your own tickets.
- Cultural shift. When engineers can see the meter, token efficiency gets scoped in — the way visible latency dashboards made teams performance-aware.
You cannot optimize what you cannot measure. Claude Meter's contribution is narrow but foundational: it makes token cost measurable at the one place it was invisible — the CLI/IDE coding session — and pins that measurement to the unit of work the rest of the org already speaks in.
Try it
Claude Meter is open source under the MIT license. Start a session named after your next ticket, end it when you're done, and read the receipt.
Repo, install instructions, and docs → github.com/ankan4445/claude-meter
If this was useful, a ❤️ or 🦄 helps, and I'd love to hear how per-task metering changes the way you scope work.








Top comments (0)