What your plugins cost your agent context window, and how to trim it
Everyone says trim your tool descriptions. We measured our own two plugins: 47 tools, 13,232 tokens loaded before you type a word, and 78% of one plugin's cost is parameter schema, not description. Here is the audit and the two changes that took 14.2% back.
TL;DR
Installing both of our published plugins costs an agent at least 13,232 tokens of tool definitions before the user types a word, measured across 47 tools on 2026-08-25. Read that as a floor: we counted with an OpenAI tokenizer, which undercounts Claude, so the real bill on a Claude host is higher. The surprise is where the cost sits: 78% of what our klodi plugin costs is parameter schema, not description, so the standard advice to write tighter tool descriptions is aimed at the cheaper half. Two mechanical changes that remove no tool and narrow no API recovered 1,883 tokens, 14.2% of the combined surface and 20.9% of klodi on its own.
You installed a third plugin last week and your agent got worse. It hesitates, it picks a tool you would not have picked, and it runs out of room in a conversation that used to fit. You went looking for advice and found two suggestions: write tighter tool descriptions, and install fewer plugins.
Both are folk remedies. Neither one is a measurement, and until you have the measurement you do not know which of your plugins is the expensive one. We ran the audit on our own two published plugins and the result was not what we expected to write.
What a plugin actually costs you, and where the cost sits
The unit of cost is not the plugin. It is the tool definition, and there are three parts to each one: the name, the description, and the parameter schema. All three are in front of the model on every turn, in every conversation, whether or not the tool is ever called. That is the always-on budget, and it is what you are actually buying when you install something.
Almost everything written about trimming that budget is about descriptions. So we measured ours, expecting to find bloated prose. We found the opposite.
Where the tokens actually are, by plugin
1,217
klodi: descriptions
14% of its surface
6,760
klodi: parameter schemas
78% of its surface
1,883
sil: descriptions
41% of its surface
2,418
sil: parameter schemas
53% of its surface
In our klodi plugin, the descriptions are 14% of the bill. The schemas are more than five times that. If you spend an afternoon rewriting descriptions you are optimising the part that was already cheap, and you will conclude that trimming does not work.
The audit: measure your own surface in about ten minutes
You do not need a profiler. You need the definitions exactly as the model receives them.
Stub your host's plugin API so registerTool pushes into an array instead of registering anything. Import your plugin's built tool modules and call each register export against the stub. You now hold every tool definition your plugin ships. Serialize each one as an object with name, description and input_schema, then count the tokens.
Sort by cost, descending. Two things will be true, and they were true for both of ours.
The cost is concentrated. Our sil plugin's two most expensive tools are 42% of its entire surface; its top five are 72%. The cheapest tools in both trees cost between 54 and 102 tokens, which is close to free. You are not going to fix this by shaving a little off everything. You are going to fix it by finding the two tools that are the problem.
Tool count is the wrong audit. sil averages 381 tokens per tool across 12 tools. klodi averages 247 across 35. The plugin with nearly three times the tools has the cheaper average tool. "Too many tools" is a diagnosis people reach for because tools are easy to count, and it would have pointed us at the wrong plugin.
Then compute one ratio per tool: description tokens to schema tokens. Past roughly 1:10, the tool is mostly shape and barely explains itself.
On the tokenizer, and why these numbers are floors
We counted with tiktoken's o200k_base encoding. That is an OpenAI tokenizer, and it is the wrong one for a Claude-hosted agent: Anthropic's own guidance is that tiktoken undercounts Claude tokens by roughly 15 to 20 percent on ordinary prose, and by more than that on code. A JSON Schema is code. So every absolute number in this piece is a floor, and the real always-on cost on a Claude host is higher than what we report, not lower.
We are publishing the floors rather than re-running the audit because the argument does not rest on the absolute numbers. Every tool was measured with the same encoder, so the ratios, the shares and the rank order are unaffected, and those are what the piece actually claims. If you want exact counts for your own host, count against it directly: Anthropic exposes POST /v1/messages/count_tokens, which accepts the tools array itself, so you can price a tool surface without guessing.
What we found in our own tree, including the parts that embarrass us
Both plugins are Apache-2.0 and you can open them: sil-openclaw at 0.4.6, and klodi-plugin at 0.3.12. Every number here came out of those two trees.
The worst tool we ship is klodi_list_update, at 1,114 tokens. Of those, 57 are the description and 1,032 are the schema. That is one token of explanation for every eighteen tokens of shape. The model is handed an enormous specification of what it may send and almost nothing about when it should send it, which is a strange thing to have paid for on every single turn.
It is not alone. klodi_offer_mine is 16 tokens of description against 253 of schema. klodi_searches_create is 43 against 579. klodi_search is 52 against 648. klodi_list_create is 84 against 938. Five of our thirty-five tools are past 1:10.
Nobody decided this. It is what you get when the parameter types are generated from the same source that validates the request, the descriptions are written by hand, and no one ever looks at the two side by side in the units the model pays in.
Two changes that cost you nothing
Neither of these removes a tool, renames anything, or narrows what a caller may send.
Collapse closed value sets. Our schema generator expands a set of allowed strings into a list of single-value alternatives. The compact form says exactly the same thing.
As we ship it
~50 tokens
Compact enum
~10 tokens
about 5x cheaper, same meaning
The verbose form is anyOf holding one object per permitted value, each with its own const and its own type. The compact form is a single type of string with an enum array. Collapsing every such block in our trees saved 784 tokens in klodi, across 11 tools, and 73 in sil.
Delete regexes the model cannot use. One UUID pattern appears 19 times across 17 of klodi's 35 tools, at 69 tokens per appearance. That is 1,311 tokens, 15% of the plugin's whole surface, spent on the same regular expression over and over.
The claim worth checking here is that the regex is in the prompt at all, because if it is not, the rest of this section is worthless. It is. The fragment is a single shared TypeBox definition in packages/tool-catalog/src/schemas.ts, carrying a description and a pattern side by side:
export const Uuid = Type.String({
description: "UUID v4 identifier",
pattern:
"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
});
That object is the tool's parameter schema. It is handed to the host verbatim as the parameters field of registerTool, and the host forwards it as the tool's input schema, which is rendered ahead of the system prompt and the messages on every request. There is no step in that path that strips unknown JSON Schema keywords. You are paying for the pattern on every turn.
What the regex is not doing is any validation that would otherwise go missing. The same expression is independently duplicated as a runtime guard in adapters/openclaw/src/lib/guards.ts, and deliberately kept in lockstep in the Python and Rust clients, with the source comment calling that duplication "the parity invariant". The publish path checks it again before a value is interpolated into a subject. Delete the pattern from the schema and every one of those checks still runs; the request is rejected exactly as it was before, just not by the copy the model was reading.
So the model gets no judgment from it. It does not derive an identifier from a regex, it copies an id that a previous call handed it. Keep the human-readable description, drop the pattern, and 1,026 tokens come back.
Recovered in klodi
Recovered in sil
Collapse closed value sets to enum
784 tokens
Collapse closed value sets to enum
73 tokens
Drop the repeated UUID pattern
1,026 tokens
Drop the repeated UUID pattern
0 tokens
Combined
1,810 tokens (20.9%)
Combined
73 tokens (1.6%)
Collapse closed value sets to enum
Recovered in klodi
784 tokens
Recovered in sil
73 tokens
Drop the repeated UUID pattern
Recovered in klodi
1,026 tokens
Recovered in sil
0 tokens
Combined
Recovered in klodi
1,810 tokens (20.9%)
Recovered in sil
73 tokens (1.6%)
Across both plugins that is 1,883 tokens, 14.2% of the combined surface, for changes a reviewer would approve without discussion.
When a tool is expensive on purpose
Cheaper is not the goal, and a piece that stops here teaches the wrong lesson.
Our sil plugin has five catalog tools where one tool with a mode parameter would obviously be smaller. That was a decision, and the reason is written in the source file:
A model picks a tool by name at the moment of use; a flag hides the intention inside a parameter it will not read.
One of those five performs a registry write that nothing in the product can undo. Folding it into a shared tool would leave that write one mistyped enum value away from an ordinary lookup. The extra tokens buy a property: the irreversible act has its own name, and the model has to choose it deliberately.
So the test is not what a tool costs. The test is whether the cost buys something. A 1,032-token schema that buys the model no additional judgment is waste. A few hundred extra tokens that keep an undoable write behind its own name is insurance.
The lever that is bigger than either trim
Everything above is the always-on budget. There is a second budget, and moving work between them beats trimming either.
A skill body is not always on. The host reads the name and description from the frontmatter and loads the body only when the skill is actually invoked. Our two skills carry 5,399 tokens of guidance between them, and advertise it for 270.
270
Tokens always in context
Both skill frontmatters, name and description only.
5,399
Tokens loaded on demand
Both skill bodies, pulled only when invoked.
20x
Ratio
What deferring the prose is worth.
That is the structural answer to how to organise a plugin. Anything that reads like documentation, procedure, worked example or house rules belongs in a skill body, where it costs nothing until it is needed. What stays in the tool definition is the part the model needs at the instant it chooses: what this tool is for, and when to reach for it rather than the one next to it.
This is also the connection to the selection problem. If your agent is picking the wrong tool, the fix is editorial, and we wrote that up separately in why your agent chooses between tool descriptions rather than tools. That piece is about what the model reads. This one is about what it costs to keep it readable. The same audit surfaces both, because both start by looking at your surface the way the model gets it.
What we are still getting wrong
Neither trim is shipped. Both numbers above are measured against the currently published versions, which means as you read this our klodi plugin is still charging you for 19 copies of a regular expression. The audit found the problem in our tree before it found it in anyone else's, and the fix is queued rather than done.
We also want to be exact about what we did not measure. We can tell you what a tool surface costs relative to itself, which is what the ranking and the shares above are. We cannot give you an exact Claude-side count, because we counted with the wrong tokenizer and have said so rather than quietly rounding it away. We have not run a controlled study on the point where a growing surface starts to degrade a model's judgment, and we are not going to imply a threshold we cannot show you. What we can say is that the budget is finite, the surface is on every turn, and 1,883 tokens of it in our own tree was buying nothing.
Run the audit on what you have installed. Sort by cost. Look at the two tools at the top, and at the description-to-schema ratio on each. That is ten minutes, and it will tell you more than any advice about description length, including ours.
Nobody should have to hold this in their head. That is the whole point of a well-built plugin: you install it, it behaves inside your budget, and you stop thinking about it. We are not all the way there yet, and the numbers above are how we know.
Sources
Everything above is measured from two open trees and one vendor document. Nothing is quoted second hand, and every file named here is one you can open.
The measured artefacts. sil-openclaw at version 0.4.6, 12 tools, and klodi-plugin at version 0.3.12, 35 tools, both Apache-2.0. Measured 2026-08-25. Method: stub the host's plugin API so registerTool collects instead of registering, import each plugin's built tool modules, serialize every captured definition as {"name", "description", "input_schema"}, and count.
For the claim that the repeated UUID pattern is real, and repeated. packages/tool-catalog/src/schemas.ts, the shared Uuid definition quoted above. It reaches the model through the parameters argument of registerTool, whose type is declared in adapters/openclaw/src/types/openclaw.d.ts.
For the claim that dropping it removes no validation. adapters/openclaw/src/lib/guards.ts holds the same expression as a runtime guard, with a source comment recording that it is intentionally duplicated into packages/nats-client-py and packages/klodi-rust-host and that a change requires updating all three in lockstep. packages/nats-client-ts/src/publish.ts checks it once more before subject interpolation.
For the counter-example, that some tools are expensive on purpose. src/tools/catalog.ts in sil-openclaw, whose header comment is the quoted line and which registers exactly five tools.
For the tokenizer correction. Anthropic's token-counting guidance, which states that tiktoken undercounts Claude tokens by roughly 15 to 20 percent on typical text and by more on code, and directs callers to POST /v1/messages/count_tokens instead. That endpoint takes the tools array, so it prices a tool surface directly.
What we could not verify, and are therefore not claiming. We did not re-measure with count_tokens against a specific Claude model, so we do not publish exact Claude-side token counts. We have not run a controlled study on where a growing tool surface starts to degrade a model's judgment, and we do not imply a threshold.
Read the surfaces, including the bad ones
Both plugins are Apache-2.0. Every tool definition measured in this piece is in the open, at the versions named.
FAQ
Measure before you cut. Capture your installed tool definitions as the model receives them, name plus description plus parameter schema, and count tokens per tool. The cost is concentrated: in our two plugins the top five tools were 49% and 72% of each surface. Fix those, and move anything that reads like documentation into a skill body, which loads only when invoked. Our two skills hold 5,399 tokens that way and advertise them for 270.