Cost optimizationGuide
Claude Code token usage: keep tool output out of context
How a PreToolUse hook and the context-mode plugin keep test logs out of Claude Code's context, plus a session cost estimate with and without filtering.

On this page
Your agent ran the test suite three times, and the bill is bigger than the task. The reason is rarely the code it wrote. It's the test logs: once a tool result lands in Claude Code's context, every later request carries it again. The fix is to shrink tool output before the model sees it. The official way is a PreToolUse hook on Bash; the third-party way is the context-mode plugin. Both are below, with a cost estimate for a long session on a CheapTokens model.
Why one log is paid for many times#
Per the Claude Code costs page (https://code.claude.com/docs/en/costs, checked 23 October 2026), Claude Code sends the full conversation with every request, and each tool use triggers another request that carries the tool results. Prompt caching re-reads the history at a cheaper cached rate, but the tokens are still there.
Say the agent runs npm test and gets a 10,000-line log. The docs say that kind of log can be tens of thousands of tokens. Assume 30,000 tokens. Whatever you do next, those 30,000 tokens are part of the input of step 11, step 12, step 13, and so on until you /clear or compact. And that's the catch: the log is useful for one step, and you pay for it for thirty.
Fix 1: a PreToolUse hook that filters test output#
The same docs page suggests hooks as preprocessing: a PreToolUse hook on Bash rewrites test commands (npm test, pytest, go test) so they pipe through grep -. The agent then sees only failures plus a few lines of context, not 10,000 lines of green dots. The docs say this can turn tens of thousands of tokens into hundreds.

This is the script from that docs page (checked 23 October 2026). It needs jq. Create the folder with mkdir -, save the script as ~/ and run chmod +x on it:
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
pattern='^(npm test|pytest|go test)( [^;&|<>`$#\\[:cntrl:]]*)?$'
if [[ "$cmd" =~ $pattern ]]; then
filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
echo "$input" | jq --arg filtered "$filtered_cmd" '{hookSpecificOutput: {hookEventName: "PreToolUse", updatedInput: (.tool_input + {command: $filtered})}}'
else
echo "{}"
fiThen register it in ~/:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/filter-test-output.sh" }]
}
]
}
}To check it works, open /hooks inside Claude Code and look for the PreToolUse entry, or start with claude - and ask the agent to run npm test. When the hook has rewritten the command, the file has a modified tool input keys line with the command field. If the tests pass, the output is empty, so the agent gets a quiet success; adjust the pattern if your runner prints failures differently.
Fix 2: context-mode for big tool results#
A hook only covers commands you can pipe. Page snapshots from Playwright or a list of twenty GitHub issues come from MCP tools. That's what context-mode targets. According to its README (https://github.com/mksglu/context-mode, checked 23 October 2026; it shows no version or date), it is an MCP server and plugin that keeps raw tool output out of the context window, stores session state in SQLite and routes tool calls through hooks. It needs Claude Code v1.0.33 or newer, and its license is Elastic License 2.0, which is not an OSI open-source license.
Install inside Claude Code:
/plugin marketplace add mksglu/context-mode
/plugin install context-mode@context-modeRestart or run /reload-plugins, then verify with /. The MCP-only variant, claude mcp add context-, adds the tools but has no automatic routing.
The project reports big reductions: 315 KB becomes 5.4 KB (98%), a 56 KB page snapshot becomes 299 bytes, 986 KB becomes 62 KB. Those are the author's own numbers: treat them as the ceiling and measure on your own session with /context before and after.
What a long session costs, with and without filtering#
Here's the arithmetic on one assumed session. It's a 40-step coding session on a 20,000-token average base context, 500 output tokens per step, and three test runs, at steps 10, 20 and 30. Each raw log is 30,000 tokens; the filtered version is 500.
- A log from step 10 is resent in the 30 requests after it: 30 × 30,000 = 900,000 tokens. Step 20: 20 × 30,000 = 600,000. Step 30: 10 × 30,000 = 300,000. Extra input: 900,000 + 600,000 + 300,000 = 1,800,000.
- Filtered: (30 + 20 + 10) × 500 = 30,000 extra input.
- Base input: 40 × 20,000 = 800,000. Output: 40 × 500 = 20,000.
- So the session is 800,000 + 1,800,000 = 2,600,000 input tokens unfiltered and 800,000 + 30,000 = 830,000 filtered, with 20,000 output tokens either way.
The calculation counts every input token at the full rate. Caching lowers the real number, but it lowers both cases by the same logic. Live prices for MiniMax-M2.7 (MiniMaxAI/, 180K context, up to 8,192 output tokens), which is the model that suits input-heavy agent loops like this one, are in the chart below.
Assumptions: 40 steps, 20,000-token base context, 500 output tokens per step, three test runs of 30,000 tokens (filtered: 500).
On MiniMax-M2.7 the session costs $0.133 unfiltered and $0.0445 filtered, and in total, with input and output counted together, it is cheaper than OpenRouter's reference price. Roughly two thirds of the input in the unfiltered case is logs the agent read once.
The point isn't the exact dollars. It's that the filter removes about 1,770,000 of 2,600,000 input tokens (2,600,000 − 830,000 = 1,770,000) on any model, and a cheaper model lowers the price of every token that's left.
Run Claude Code on a CheapTokens model#
Claude Code talks the Anthropic Messages protocol, and CheapTokens supports it. The base URL for Claude Code has no /v1. Full setup is in how to connect Claude Code to CheapTokens. The short version, with a placeholder key:
export ANTHROPIC_BASE_URL=https://api.cheaptokens.dev
export ANTHROPIC_AUTH_TOKEN=YOUR_CHEAPTOKENS_KEY
export ANTHROPIC_MODEL=MiniMaxAI/MiniMax-M2.7
claudeUse the full id from the docs; short names from price tables are not accepted by the API. Prices for every model are on the pricing page. For the other habits that cut a Claude Code bill (/clear, /compact, /mcp, subagents), see How to make Claude Code cheaper. To see how input and output are counted on a real request, read How many tokens does an API request use.
Quick checklist#
- Run
/contextin a long session and see what is taking space. - Add the Bash hook for test commands; confirm the rewrite in
/hooksor the debug log. - If MCP tools return big snapshots, try context-mode and compare
/contextbefore and after. /clearbetween unrelated tasks, so old logs stop riding along.



