●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Allocating the 200K Context Window in Claude API — Budgeting System, Tools, Memory, and History in Production
Treat Claude API's 200K context as a budget rather than an open shelf. A TypeScript-backed allocation architecture that carves system, tools, memory, history, and headroom into explicit envelopes — built and tuned in a wallpaper app earning real ad revenue.
In March 2026, the Claude-powered chat I had embedded in one of the healing wallpaper apps I run started showing a slow upward drift in input_tokens from one evening on. Conversation quality was fine. Reviews were trending positive. That is exactly why I left it alone for ten days.
When I finally opened the Anthropic Console, the surprise was not in the messages array. It was that system and tools were each carrying roughly twice the tokens I had budgeted for. I had landed the chat-history compression project the previous month, so I assumed the bill was solved. The bill was not solved. The context window was being treated as an open shelf, and a different door was leaking tokens.
Since 2014 I have shipped personal apps to a cumulative 50 million downloads, and I have walked into more than a few server-bill landmines along the way. But Claude API has a particular failure mode: things look healthy because the model still answers well, while a side channel quietly grows. This post is the production write-up of the architecture I now use to carve the 200K window into five explicit envelopes, with both static and dynamic allocation policies. The code is reconstructed from the version I run on Cloudflare Workers with TypeScript.
The 200K Is a Budget, Not a Window
Sonnet 4.6 and Opus 4.6 both ship with 200,000-token windows. On day one that feels like more room than you could possibly need. In production, five distinct demands flow into that same envelope every request.
System prompt — persona, guardrails, output format directives
Memory tier — retrieved episodes, entities, user preferences
Conversation history — the recent messages array
Answer headroom — what you reserve for max_tokens on output
My mistake in the wallpaper app was treating those five as a single pile. In reality, tool definitions were silently shipping 18,000 tokens per request, RAG-driven memory was averaging 22,000, history about 9,000, and the system prompt 4,800. The remaining 145,000 was unused. The problem was never "we're not using enough." The problem was that no one had drawn the envelopes, so when something needed to be cut, there was no rule for what to cut.
Draw the Five Envelopes First
The architecture I run today is two-tier: a static budget that is the source of truth, with a dynamic reallocator on top. With Sonnet 4.6 or Opus 4.6, I treat 192K as the per-request budget (200K minus up to 8,000 tokens of answer headroom).
Static budget per category, with a soft cap that may be reached only when other categories shrink:
system: base 5,000 / cap 6,000 — persona, guardrails, output format
tools: base 15,000 / cap 22,000 — full JSON Schema for every tool
memory: base 30,000 / cap 60,000 — RAG, entities, preferences
history: base 40,000 / cap 80,000 — last N turns of the conversation
reserve: 24,000 fixed — failsafe buffer
The cap is a ceiling other categories may borrow against; reserve is the one envelope I never touch. It exists for the days when an MCP server is added without warning, or when retrieval hits explode, so headroom does not get devoured.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A TypeScript BudgetAllocator that partitions the 200K window across five categories with both static and dynamic policies
✦How I uncovered 18,200 tokens hidden in tool definitions and recovered 23% of monthly input cost
✦Splitting the memory tier between Sonnet 4.6 and Haiku 4.5 cut monthly API spend from ¥86,000 to ¥38,000 with no quality regression
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
This is the core of the TypeScript module, lifted from the version I run in production. It works on Cloudflare Workers and Node alike.
type Category = "system" | "tools" | "memory" | "history" | "reserve";interface BudgetPolicy { base: Record<Category, number>; max: Record<Category, number>; totalBudget: number; // 192_000 for Sonnet 4.6}const DEFAULT_POLICY: BudgetPolicy = { base: { system: 5000, tools: 15000, memory: 30000, history: 40000, reserve: 24000 }, max: { system: 6000, tools: 22000, memory: 60000, history: 80000, reserve: 24000 }, totalBudget: 192_000,};export class BudgetAllocator { constructor(private policy: BudgetPolicy = DEFAULT_POLICY) {} // Fast path: return the static budget unchanged staticAllocate(): Record<Category, number> { return { ...this.policy.base }; } // Look at actual demand and reallocate the elastic envelopes dynamicAllocate( requested: Partial<Record<Category, number>>, ): Record<Category, number> { const out = { ...this.policy.base }; const { base, max, totalBudget } = this.policy; // 1) Lock in system and tools first (up to their caps) for (const k of ["system", "tools"] as const) { out[k] = Math.min(requested[k] ?? base[k], max[k]); } // 2) Memory and history share the elastic remainder const fixed = out.system + out.tools + base.reserve; const flexBudget = totalBudget - fixed; const memReq = Math.min(requested.memory ?? base.memory, max.memory); const histReq = Math.min(requested.history ?? base.history, max.history); if (memReq + histReq <= flexBudget) { out.memory = memReq; out.history = histReq; } else { // On contention, history wins (cutting recent context hurts UX more) out.history = Math.min(histReq, flexBudget - base.memory); out.memory = flexBudget - out.history; } return out; }}
Two things matter here. First, system and tools are resolved before anything else, because they rarely change across requests and locking them in simplifies every downstream decision. Second, when memory and history compete for the elastic remainder, history wins. Losing recent conversation context degrades the user experience visibly; memory loss tends to degrade quality gradually, and the model adapts.
The Tool-Definitions Blind Spot
This was the actual culprit behind the March incident. I had three MCP servers wired up, and the combined tool definitions were 18,200 tokens — every request. The Anthropic Console shows input_tokens as one number, so to break it down you have to measure it yourself.
Pair a countTokens call with and without tools, and the delta is your tool overhead. In my case the Slack MCP carried verbose descriptions that ran 700–1,200 tokens per tool. Trimming descriptions to essentials and pruning unused enum values brought the total from 18,200 down to 9,400 tokens. That single change recovered 23% of monthly input cost.
Splitting the Memory Tier Across Two Models
The 30,000-token memory envelope is not homogeneous. In my wallpaper app it holds three kinds of recall, and each has a different precision requirement.
Summaries of the last five sessions (medium precision, extractive) — generated by Haiku 4.5
Structured preference tags (high precision, already structured) — fetched directly from KV, no model required
Haiku 4.5 took the medium tier because its input pricing runs roughly a quarter of Sonnet 4.6 and the quality delta on summarization tasks was not perceptible in user testing. The long-term abstraction layer stayed on Sonnet 4.6 because missed meaning there is fatal, and prompt_caching keeps the recomputation cost in check.
After this split, monthly API spend dropped from ¥86,000 to ¥38,000. DAU was flat, Day 7 retention moved +1.8%, and I have not yet seen evidence of quality regression.
Production Gotchas I Hit
Three potholes that only became visible after I shipped.
(1) Prompt-caching hit rate breaks budget assumptions
I had carved envelopes assuming system and tools would land in cache. With a five-minute TTL, low-frequency users missed cache on every request, and cache_creation_input_tokens showed up as its own line item I only spotted at billing time. Switching to a one-hour TTL alongside the default lifted hit rate from 41% to 78%.
(2) Dynamic tool sets corrupt history coherence
If a tool used in a past turn disappears from the current request, the assistant turn that referenced tool_use is left dangling. I now treat the tool set as immutable for the duration of a session. Tool pruning happens only at session boundaries.
(3) Hard-coding max_tokens at 8,000 truncates good responses
For requests that expect long-form explanations, an 8,000 ceiling clips the tail. I keep the headroom budgeted, but I borrow from reserve to lift max_tokens dynamically up to 16,000 when the demand profile justifies it, and I repay that borrow by shrinking the history envelope on the same request.
Recommended Allocations by Scenario
There is no universal answer. Here are the three profiles I actually run, which I recommend as starting points.
Profile A: Short-form support bot (DAU 200–500)
system 4,000 / tools 6,000 / memory 10,000 / history 20,000 / reserve 152,000
Few tools, heavy on history. A thick reserve absorbs occasional long answers. Recommended when tool variety is low.
Profile B: Multi-agent coding assistant
system 8,000 / tools 25,000 / memory 50,000 / history 70,000 / reserve 39,000
Tool definitions are heavy and codebase search lands in memory. I recommend enabling prompt_caching here unconditionally.
Profile C: Personalized chat (the wallpaper-app pattern)
system 5,000 / tools 9,000 / memory 30,000 / history 40,000 / reserve 108,000
Within memory, 18,000 goes to Haiku 4.5 summaries and 12,000 to KV-backed structured preferences. Recommended when the workload is highly stateful and per-user personalized.
Across all three profiles, the constant is leaving reserve alone. The day you trim it is the day an unexpected MCP server or a retrieval-heavy query overflows the window.
The Indie Developer's Pragmatic Take
The apps I run survive on a small economic loop of AdMob revenue and modest in-app purchases. Unpredictable API costs swing gross margin sharply. At the same time, trimming context too aggressively degrades the experience, drives down reviews, and ultimately lowers AdMob eCPM through reduced session quality.
The two-tier discipline — fix the static budget, reallocate dynamically — is what reconciles those pressures. It mirrors how I work as an artist: 17 international art awards have not changed the fact that the work which holds up is the work built on a skeleton I refuse to compromise on. Code is no different. Define the skeleton first, then improvise within it.
If you take one action from this article, measure the token cost of your system and tools with countTokens today. In my experience, most of the slack hides there, not in your conversation history. Thank you for reading.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.