Analysis
September 6, 2026
8 min read
Maxime Dalessandro

Claude Code token usage: what Spotify's 90% cut proves

Spotify cut Claude Code token usage 90 percent with two hooks and a cheap worker model. The pattern, routing tool output out of context, goes well beyond files.

#AI agents#AI coding agents#Claude Code#Agent memory#Context layer#token costs#MCP

TL;DR. Spotify's Portal team published a Claude Code plugin, shunt, that intercepts bulk file reads with a PreToolUse hook and hands them to a cheaper worker model, which reads the file in its own disposable context and returns only an answer. Spotify reports mean savings around 90 percent on bulk-read scenarios in a Java monorepo test. The number is self-reported and workload-shaped, but the mechanism is the interesting part: agent context spend is dominated by tool output the primary model never needed to see, and the cheapest fix intercepts that output before it enters the transcript. Files are just the first tool output worth intercepting. Database introspection is a bigger one, and nothing blocks it today.

Spotify's engineering blog published a post this week with a title that reads like a cost tip: Portal by Spotify cut my Claude Code token usage by 90%. The implementation underneath is about 200 lines of hook configuration, two prompts, and no new infrastructure, and it is worth reading as an architecture claim rather than a discount. The claim: a large share of what an agent spends on context is tool output that did not need to reach the expensive model at all, and the right place to fix that is the moment before the output enters the transcript. That claim has consequences for how teams buy agent memory tooling, and it has a sharper version for anyone pointing agents at a database.

Two hooks, two modes, one threshold

The plugin is called shunt and lives in Spotify's open source portal-ai-plugins repository (Apache 2.0), alongside the plugin that connects coding agents to Portal, Spotify's managed Backstage platform. Its moving parts, per the write-up:

Two PreToolUse hooks. The first, check-file-size, fires on every Read call. If the target file exceeds a line threshold (default 350, configurable via SHUNT_MIN_LINES), the hook blocks the read and tells the agent to use a delegation skill instead. The second, check-bash-read, catches the shell escape hatches: cat, head, tail, less, and more on large files. Piped commands like cat file | grep pass through, on the sensible theory that a pipe means the agent is extracting something specific rather than swallowing the file.

Two AiKA modes on the other side of the block. AiKA is the AI assistant inside Portal, and its modes are configured model-plus-prompt bundles reachable from the Portal CLI. The bulk-reader mode takes a question and a list of paths (bulk-read --question "..." --paths ...), reads the files inside its own context, on a cheaper worker model (the write-up uses Gemini Flash), and returns a concise answer. The code-writer mode takes a spec and a reference file and writes boilerplate straight to disk. In the bulk-read case the primary model pays for a summary instead of the file. In the code-write case it pays for nothing at all: the generated code never enters its context as input or output.

The detail that makes this production-grade rather than a prompt trick: the enforcement lives outside the model. A skill description asking the agent to please delegate large reads would be advisory, and agents drift. A PreToolUse hook that blocks the read is a wall. The write-up makes the same point in its degradation analysis: even when the agent never reads the skill description, the expensive read still fails, and the agent routes around it.

Interception beats compression, and the billing model says why

To see why the savings land where they do, follow one file through a session. The Messages API is stateless: the whole transcript is re-sent on every request. A 4,000-line file read on turn 3 of a 40-turn session is input on turn 3 and on every one of the 37 turns after it. Prompt caching softens this, cached prefix tokens re-read at roughly a tenth of the base input rate, but a tenth of a large number, dozens of times, is not small, the cache write itself bills above the base rate once, and cache entries expire. The transcript is a tax base. Everything admitted to it is taxed for the rest of the session.

Two flow diagrams comparing where a bulk read's tokens go. In the default path, a wide band representing tens of thousands of tokens flows from a 4,000-line file read into the primary model context, where it is re-sent with the transcript on every later turn. In the routed path, a PreToolUse hook blocks the read, the wide band is diverted into a cheaper worker model with disposable context, and only a thin band, the answer, continues into the primary model context.

The band widths are the argument: the default path admits the file, the routed path admits the answer.

That framing sorts the context tools into two families. Compression tools operate after admission: compaction summarizes a long transcript, context editing evicts old tool results, memory files persist distilled knowledge between sessions. All useful, all paying the full price at least once before recovering part of it. Interception operates before admission, and its savings compound with session length for the same reason the cost did.

The write-up is candid that the headline number is workload-shaped: the 90 percent is a mean over bulk-read scenarios in one Java monorepo test across four scenarios, and a monorepo where 400-line config files are unremarkable is exactly where a 350-line threshold bites hardest. Treat the number as Spotify's measurement of Spotify's codebase. The direction, though, does not depend on the codebase, because it falls out of the billing model above: tokens kept out of the transcript are saved once per remaining turn, and tokens merely compressed are saved once.

The quiet claim about agent memory products

There is a category of products, Mem0, Zep, Letta, LangMem, Cognee, whose pitch is managing what the agent has seen: extract it, store it, rank it, re-inject the relevant slice. The implicit model is that context overflow is a storage problem, and the fix is a smarter store.

Spotify's result suggests a prior question: how much of what the agent saw should it have seen? A bulk file read that exists only to answer "which config key controls retries" produces one useful sentence and thousands of transcript tokens. No memory layer fixes that efficiently, because by the time memory tooling runs, the cost is sunk. Routing fixes it by making the primary model consume conclusions instead of raw material. The same logic shows up in instruction files that grow and never shrink: once something is in context, machinery to manage it is more expensive than not admitting it was.

None of this makes memory products pointless. Cross-session facts, user preferences, and knowledge that outlives any single transcript still need a store, and routing does nothing for them. What shrinks is the middle of the category: context compression as a paid service is in trouble when a hook plus a cheap model substitutes for it inside an afternoon's configuration. The boundary between the two is worth drawing before buying either.

The database version of a bulk read

Now point the same lens at agents that touch databases, which is where this stops being a coding-agent story.

An agent connected to Postgres through an MCP server starts almost every task the same way: figure out what is there. In practice that means schema introspection, and introspection output is the database's bulk read. A \d-equivalent dump of a few hundred tables runs to tens of thousands of tokens. An information_schema sweep, a pg_stat_statements pull, an EXPLAIN ANALYZE on a hairy plan: each is a large, mostly irrelevant payload fetched to answer a question that has a one-paragraph answer. The ERP schemas our ICP lives with make the Java monorepo look tidy; SAP-style systems put the table count in the tens of thousands, with names like T001 and MSEG that force the agent to read even more to learn less.

The shunt pattern transfers directly, because the shape is identical: a PreToolUse-style hook on MCP tool results, a size threshold, and a worker that consumes the dump in disposable context and returns the answer. Nothing about the pattern is file-specific. As far as public examples go, nobody has shipped the database version yet.

But the file case also hides an advantage the database case lacks. A worker model summarizing a source file has everything it needs, because code mostly explains itself. A worker model summarizing a schema dump does not: nothing in the catalog says which of four customer-ish tables is authoritative, that amount_v2 superseded amount in 2024, or that the orders table joins to shipments through a mapping table with a misleading name. Summarizing raw catalog output produces a confident tour of surface structure with the institutional knowledge missing, which is precisely the knowledge the agent needed. For databases, the bulk-reader needs to read from something that already resolved what the schema means, not from the catalog itself. Routing only decides where tool output gets processed; the meaning that was never in the output has to come from somewhere.

Where the savings stop

Four boundaries worth naming before copying the pattern.

The summaries are lossy, and the loss is invisible. A worker answers the question it was asked; if the interesting fact in the file was one the primary model did not know to ask about, it is gone. When the primary model gets suspicious and re-reads the file itself, the saving evaporates. Spotify's threshold design concedes this by letting targeted reads through untouched.

The worker's answer enters the transcript as trusted text. Its provenance is a cheaper model reading content the primary never saw, which makes the worker a fine target for the instruction-bearing content problem every agent pipeline has: anything that can seed the files or dumps the worker reads now has a summarization layer between the payload and the model with the judgment.

The savings arithmetic has a floor. Delegation adds a second model call and its latency, and below the threshold the overhead beats the saving, which is why the threshold exists.

And the pattern is only as portable as the interception surface. This works in Claude Code because Claude Code exposes hooks that can block tool calls before execution. An agent framework without that surface can imitate it only by begging the model in prompts, which is the advisory version the hook exists to replace. Interception points, not model quality, are what to evaluate when choosing where agents run.

The general reading: context engineering is converging on admission control. Admission control needs two things, an interception point in the harness and something authoritative for the worker on the other side to read. The first is a plugin. The second, for databases, is the harder and more interesting build.

Where Datapace fits

A bulk-reader for your database only works if there is a resolved layer for it to read, and Datapace is building exactly that layer: schema meaning resolved and validated by the people who own the data, workload evidence kept on the same graph (cost, performance, usage and freshness, lineage), and a policy gate over what an agent may do and access, served to agents over MCP. In the routing terms of this post, that is the difference between a worker that summarizes information_schema and a worker that answers from ground truth: the agent gets the one-paragraph answer about T001 without ever paying for the dump. If your agents burn their context rediscovering your schema every session, book a call.

Sources

  1. Spotify Engineering, Portal by Spotify cut my Claude Code token usage by 90%, September 2026 (shunt plugin, check-file-size and check-bash-read hooks, 350-line default threshold, bulk-reader and code-writer modes, Gemini Flash worker, mean savings on bulk-read scenarios in a four-scenario Java monorepo test; savings figures are Spotify's own measurements).
  2. Spotify, portal-ai-plugins repository (Apache 2.0; portal and shunt plugins, AiKA-mode delegation via the Portal CLI actions registry).
  3. Spotify for Backstage, AiKA x Portal (AiKA as Portal's AI assistant, MCP connectivity).
  4. Anthropic, Claude Code hooks reference (PreToolUse hooks and tool-call blocking).

Frequently asked questions

How do I reduce Claude Code token usage?
Attack tool output before it enters context. Block bulk file reads with a PreToolUse hook and delegate them to a cheaper model that returns a summary, keep reads targeted (line ranges, grep, piped commands), and let prompt caching handle the stable prefix.
What is the shunt plugin for Claude Code?
An open source Claude Code plugin from Spotify's Portal team (Apache 2.0, in the spotify/portal-ai-plugins repo). It registers PreToolUse hooks that intercept bulk file reads and boilerplate generation and route them to AiKA modes running on a cheaper worker model via the Portal CLI.
What is a PreToolUse hook in Claude Code?
A hook that runs before a tool call executes and can block it. Because the block happens outside the model, it works even when the model ignores instructions: shunt uses one to stop reads of files over a line threshold and point the agent at a delegation skill instead.
Doesn't prompt caching already solve large tool outputs?
It discounts them, to roughly a tenth of the base input rate for cached prefix tokens, and the write is billed above the base rate once. But a cached transcript still carries the bulk read on every later turn, and cache entries expire. Routing removes the tokens instead of discounting them.

Keep reading

Ready to let agents touch production, safely?

Bring a use case. We will show you what agents can do on your live data, inside your guardrails.