The Proxy I Added to Measure Tokens Tripled Them

← hexisteme · notes · 2026-08-09

I added a local capture proxy to see exactly what my coding-agent CLI sends per request. The proxy itself changed what got sent: the documented base-URL override needed to route through it silently disabled the CLI's lazy-loading of MCP tool schemas, inlining a full catalog of 373 tool definitions into every request and roughly tripling input tokens. Three independent verifiers reproduced the inflated numbers exactly — and were still wrong about why, because reproducing a number isn't the same as checking the interpretation attached to it. Two discriminating experiments, not more verification, found the real cause.

I run a small fleet of coding agents, and one number had been bothering me for weeks: spawning a general-purpose sub-agent cost roughly 70,000 tokens before it did anything. I knew the rough shape of where that went — system prompt, built-in tool schemas, some kind of project configuration, and whatever the MCP layer was contributing — but not the breakdown, and "roughly 70k, mostly overhead" isn't something you can act on.

So I built an instrument: a tiny local HTTP proxy that sits between my machine and the vendor's API, forwards every request unmodified, and writes the request and response bodies to disk before passing them through. Deliberately, it never touches headers — those go straight through without being logged. I pointed my coding-agent CLI at it using the documented base-URL environment override, the one meant for routing through a gateway, and ran a batch of headless sessions through it.

The first captures looked like a different bug

The captures came back showing roughly 240,000 tokens per sub-agent spawn — a general-purpose spawn at 246,525 tokens, a restricted-tool explorer spawn at 212,736. More than three times what I expected. Real, un-proxied session transcripts for the same spawn types showed 70,733 and 40,810 respectively — off by roughly 3x in both cases.

My first interpretation: the headless entrypoint I was capturing through must not defer MCP tool schemas the way the interactive path does. My CLI has a lazy-loading behavior for its MCP tool catalog — instead of inlining every tool definition into every request, it defers most of them until a tool is actually about to be used. If that deferral simply wasn't wired up on the code path I was capturing from, the gap made sense: the full catalog, on my setup, runs to 373 tool definitions across roughly 21 connected MCP servers — about half a megabyte of schema text — and inlining all of that would triple the token count. It was a clean story, and it matched the ratio.

Reproducing a number is not the same as checking an interpretation

I ran the finding through independent adversarial verification — three separate passes, each re-measuring from the raw captures. All three reproduced the numbers exactly: same ~240k figures, same ~70k anchors, same ratio.

For the numbers, that was real confirmation. But three verifiers agreeing that a number is correct tells you nothing about whether the story attached to that number is correct — all three were checking arithmetic against captures made the same way, through the same proxy, on the same headless entrypoint. If something about that setup was the actual cause, independent people re-deriving the same average from the same contaminated inputs will agree with each other and still be wrong about why. I treated 3-for-3 as settling the question for about half a day, until an experiment designed to test the theory rather than confirm it.

Experiment one: it isn't headless-specific

The discriminating test was to hold everything constant while changing one variable. First: is this actually about headless execution? I drove a full interactive session — the kind a person runs at a terminal, via a pty — through the same proxy setup, and watched its main turns.

288,259, then 289,372, then 291,366 tokens per main turn — and the sub-agents it spawned came back at 244,142 (general-purpose) and 213,420 (explorer). Just as inflated as the headless captures, on a session type unrelated to how I'd been driving those headless runs. That ruled out my working theory.

Experiment two: the proxy is the variable

The next candidate was the one thing every inflated capture had in common: the proxy itself, specifically the base-URL override needed to route through it. I ran the identical headless command again, unchanged, with the override removed — talking straight to the vendor's API.

First turn: 79,761 tokens. Normal, in line with the real transcript anchors I'd been comparing against all along.

That closed it. Setting the base-URL override — required to route through any gateway or capture proxy — silently disables the tool-schema deferral. With the override in place, the CLI inlines the full catalog into every request instead: 241,659 tokens for the same headless first turn that ran 79,761 without the override, roughly 3x. The proxy hadn't been passively observing the requests my CLI would normally send. Its presence changed what those requests were.

What the deferral is actually worth

The interactive run's proxied general-purpose spawn cost 244,142 tokens (its headless twin was 246,525 — both regimes inflate to nearly the same shape); the real, un-proxied equivalent ran 70,733. The difference — 173,409 tokens — is what MCP tool-schema deferral saves on a single sub-agent spawn, on a setup with roughly 21 connected MCP servers and a full catalog of 373 tool definitions. That's overhead the deferral mechanism was already quietly absorbing on every spawn, invisibly, because it worked.

It also meant the proxy could never observe the thing I actually cared about. With the base-URL override set, deferral-on assembly is unobservable by capture in principle — not because the proxy was built wrong, but because the override that lets it see traffic at all is the same override that turns deferral off.

Closing the decomposition indirectly

I still wanted the original answer. With direct capture off the table, I closed it by combining the shared components visible in the proxied captures with the real usage anchors from un-proxied transcripts. The pieces that don't depend on deferral status — system prompt, built-in tool schemas, per-session configuration injection — are visible and consistent in both regimes; only the tool-catalog portion swaps between "full schema" and "deferred stub."

The reconstruction: a 70,733-token general-purpose spawn breaks down as roughly 2.4k tokens of system prompt, about 19.9k tokens of built-in tool schemas, around 20.1k tokens of injected user/project configuration, and approximately 28.2k tokens covering the deferred tool catalog stub plus skills, sub-agent definitions, and MCP server instructions carried along with it.

What this is actually about

None of this is a claim that anything was broken. The deferral-disabling behavior on a custom base URL is, as far as I can tell, an intentional tradeoff — some capability isn't available when requests are routed through an arbitrary endpoint, so the CLI falls back to sending everything inline instead of trusting a remote party to have the deferred-tool machinery. That's reasonable. It just means the instrument built to measure normal behavior cannot see normal behavior.

Three things transfer to any similar instrumentation effort. First, an observability tap on an LLM pipeline is not passive by default — anything that requires changing how a client talks to its backend is a configuration change to the system under test, not a window into it. Second, validate the instrument against an un-instrumented baseline before trusting any absolute number it gives you; a single control run would have caught this before a verification cycle got spent on the wrong theory. Third, reproduced numbers can carry a confounded interpretation, and only a discriminating experiment — one designed to separate two candidate causes, not just recheck arithmetic — kills the wrong reading. Both "headless doesn't defer" and "the proxy disables deferral" predicted the exact same captures; only removing the proxy and holding everything else fixed could tell them apart.

The one-time cost of this investigation, proxy captures included, came to about 2.7 million cache-creation tokens — spent entirely on measuring measurement.

FAQ

Q. Why did adding a capture proxy triple the token count of every request?
Because the proxy required setting the documented base-URL environment override to route traffic through it, and that override silently disables the coding-agent CLI's lazy-loading ("deferral") of MCP tool schemas. With deferral on, most tool definitions are left out of a request until the model is actually about to use that tool. With the override set, the CLI falls back to inlining the full tool catalog into every request instead. On this setup, that meant roughly 241,659 tokens for a headless first turn through the proxy versus 79,761 tokens for the identical command with the override removed — roughly 3x.

Q. Why didn't three independent adversarial verifiers catch that the proxy was the cause?
Because all three were re-measuring from the same captures, made the same way, through the same proxy. They correctly confirmed that the numbers in those captures were arithmetically right — the same ~240k-token figures reproduced exactly every time. But reproducing a number from one measurement method only tells you the arithmetic was done correctly; it says nothing about whether the interpretation attached to that number is correct. Only a discriminating experiment that varied the proxy itself, rather than re-checking the same captures, could separate "the entrypoint doesn't defer" from "the proxy disables deferral" — both explanations predicted identical captures.

Q. How much does MCP tool-schema deferral actually save per sub-agent spawn?
173,409 tokens per general-purpose spawn, measured as the difference between a proxied spawn with deferral disabled (244,142 tokens) and the same spawn type in a real, un-proxied transcript with deferral working normally (70,733 tokens). That saving scales with the size of the connected tool catalog — on the setup measured, 373 tool definitions across about 21 connected MCP servers.

Q. What discriminating experiments isolated the proxy as the cause, rather than the headless entrypoint?
Two. First, driving a full interactive session through the same proxy via a pty produced main turns of 288,259, 289,372, and 291,366 tokens — just as inflated as the headless captures, which ruled out "headless-specific" as the explanation. Second, running the identical headless command a second time with the base-URL override removed produced a normal first turn of 79,761 tokens, matching real transcript anchors. Only the proxy configuration differed between the inflated and normal runs, which isolated it as the cause.

Q. If the proxy disables deferral, how was the original 70,733-token spawn preamble ever decomposed?
Indirectly. Once the base-URL override is set, deferral-on assembly is unobservable by direct capture in principle, because the same override that lets the proxy see traffic is the override that turns deferral off. The decomposition was closed by combining the request components that don't depend on deferral status — system prompt, built-in tool schemas, injected configuration — visible and consistent across both proxied and real captures, with the real un-proxied usage anchors for the total. The result: roughly 2.4k tokens of system prompt, 19.9k of built-in tool schemas, 20.1k of injected configuration, and 28.2k covering the deferred tool catalog plus skills, sub-agent definitions, and MCP instructions.

Related notes

← hexisteme · notes · CC-BY 4.0