MCP tool-schema floor — committed baseline (#3230)
Part of epic #3229 (shrink the always-sent context budget). This is the measured baseline the reduction levers ratchet down.
What this number is
Every tool fak’s MCP server advertises ships its full JSON schema in every
tools/list, and that set is re-sent on every turn — the model pays for it
whether or not the tool is ever called. That fixed per-turn tax is the tool
floor. fak footprint prices it offline and deterministically, reusing the
same estimator the agent request footprint uses (agent.RequestFootprint, via
internal/mcpfootprint), so the number can never drift from
EstimateAnthropicTokens.
Regenerate at any time:
fak footprint # human table, largest-first
fak footprint --json # schema fak-mcp-footprint/1
fak footprint --top 8 # just the heaviest N
Baseline (measured)
mcp-footprint: 26 tools · floor 5888 est. tokens (23554 bytes, ESTIMATED)
Trimming description prose reduced the measured whole-schema floor by the same 888 estimated tokens, from 6776 to 5888 (13.1%), without changing tool names or parameter schemas.
Heaviest contributors (the cold-schema deferral targets for #3231/#3232):
| rank | est. tokens | bytes | tool |
|---|---|---|---|
| 1 | 566 | 2264 | fak_trajquery |
| 2 | 558 | 2234 | fak_memory_run |
| 3 | 508 | 2033 | fak_memory_explain |
| 4 | 305 | 1222 | fak_context_change |
| 5 | 257 | 1028 | fak_admit |
| 6 | 249 | 999 | fak_adjudicate |
| 7 | 236 | 946 | fak_feature_query |
| 8 | 230 | 921 | fak_context_restore |
The full 26-tool breakdown is what fak footprint prints; only the head is
pinned here so a drift is legible in review.
The gate (#2924)
Measuring the floor does not keep the core narrow — a number that cannot refuse a
change is still just taste. internal/mcpfootprint.CheckFloor gates the measured
floor against a committed ceiling, FloorBudgetTokens (currently 5888), as a
one-way ratchet:
| Direction | Reason | What it means |
|---|---|---|
| measured > budget | FLOOR_BUDGET_EXCEEDED |
a new tool, or a fattened description/JSON-Schema, grew the per-call tax |
| measured < budget − 250 | FLOOR_BUDGET_STALE |
a deferral won headroom that was never banked into the constant |
How to justify growth. Raise FloorBudgetTokens in the same commit as the tool
that grew it, and re-pin the baseline table above. That is the whole mechanism: the
new per-turn tax becomes a diff line a reviewer sees, bound to its cause, instead of
being discovered a quarter later. Prefer deferring a cold schema (#3231/#3232) over
paying the floor.
The 250-token slack absorbs incidental churn (a reworded description) while still
forcing a real reduction to be banked — the same discipline internal/pythongate
applies to the tools/*.py baseline: the ratchet only ever tightens. The gate fails
closed: an empty registry prices as 0 tokens and refuses as FLOOR_BUDGET_STALE
rather than greening on a measurement of nothing.
The description budget (#3608)
The floor gate above prices the whole schema (name + description + JSON-Schema parameters). But the description is the one slice with no machine consumer — the parameters are validated, the name is dispatched on, but the description is pure prompt-prefix prose the model reads, so it is the slice most prone to silently fattening into a per-turn tax. #3231 defers the cold schemas; this keeps the hot (always-sent) descriptions lean, and the two compose.
internal/mcpfootprint.CheckDescriptions gates the summed always-sent fak_*
description tokens against DescriptionBudgetTokens — the same one-way ratchet, priced
through the same estimator (a description-only ToolDef carries no name or parameter
bytes, so the number never drifts from EstimateAnthropicTokens).
always-sent fak_* description floor: 1966 est. tokens across 26 tools
This measured floor is 888 estimated tokens (31.1%) below the previous 2854-token baseline, with the same 26 tool names and parameter schemas. The values are estimator-derived, not provider-billed token counts.
Heaviest description bodies (the trim targets — fak footprint ranks the full schema;
PerToolDescription ranks the prose slice):
| rank | est. tokens | tool |
|---|---|---|
| 1 | 130 | fak_memory_run |
| 2 | 120 | fak_admit |
| 3 | 106 | fak_tools_search |
| 4 | 104 | fak_session_reset |
| 5 | 96 | fak_context_change |
| 6 | 90 | fak_memory_drivers |
| Direction | Reason | What it means |
|---|---|---|
| measured > budget | DESC_BUDGET_EXCEEDED |
a fattened (or newly-added) description grew the per-call prose tax |
| measured < budget − 200 | DESC_BUDGET_STALE |
a trim won headroom that was never banked into the constant |
Justify growth the same way as the floor gate: raise DescriptionBudgetTokens in the
same commit as the description that grew it, and re-pin the number above — the new
prose tax becomes a diff line a reviewer sees, bound to its cause. The 200-token slack
absorbs a reworded sentence while still forcing a real trim to be banked. Trimming the
hot descriptions to fit a lower budget is the standing follow-on this gate makes safe
and measurable (tool-search still resolves a tool from a leaner description).
Witness
internal/mcpfootprint.TestRealFakMCPFloorprices the real registry and asserts the floor is a faithful partition (floor bytes == sum of per-tool bytes) and non-trivial — the number above is reproducible, not hand-typed.internal/mcpfootprint.TestPricePartitionsExactly/TestPerToolSortedDescendinglock the estimator invariants.cmd/fak.TestMCPFootprintVerbJSONwitnesses thefak footprint --jsonshape.internal/mcpfootprint.TestCommittedBudgetMatchesMeasuredFloorproves the ceiling above is a measurement, not a hand-typed number that drifted from the registry;TestFloorGateRefusesGrowthwitnesses the refusal firing on a registry grown by one tool, andTestFloorGateDemandsBankedWinwitnesses the ratchet refusing an unbanked reduction.TestFloorGateBandBoundariespins the exact admit/refuse edges.internal/mcpfootprint.TestDescriptionBudgetPassesAtHEADprices the real registry’s description slice and asserts it is underDescriptionBudgetTokens;TestDescriptionBudgetRefusesGrowthwitnesses the refusal firing on a synthetic over-budget description edit,TestDescriptionBudgetDemandsBankedWinthe unbanked-trim refusal, andTestDescriptionIsDescriptionOnlyproves the number is the prose slice alone (strictly less than the full schema floor).
Cross-links
- #3229 — epic: shrink the always-sent context budget (this baseline is its measurement floor).
- #3233 — the live gateway footprint (same estimator, measured on a real request rather than the static registry).
- #3231 — defer cold
fak_*MCP schemas (drives the number above down). - #3232 — gateway tool-floor deferral (the 10× lever on this same floor).
- #3234 — the userland analogue:
fak skill footprintfor the resident.claude/skillsdescription floor. - #5444 — that userland floor’s own one-way ratchet, built to this page’s
pattern: resident skill-description floor
(
SKILL_DESC_BUDGET_EXCEEDED/SKILL_DESC_BUDGET_STALE).