Two weeks ago the MCP server behind my orchestrator exposed 11 tools. It exposes 4 now. Nothing the agent does changed, because three of those 11 were already doing 91% of the work and I had never bothered to count.
The orchestrator is a small thing I run for content operations: it plans slots, drafts copy, schedules what passes review. Every step talks to the same MCP server, which I maintain, which means every bad decision in it is mine. I went looking because of the token bill. I stayed because of the errors.
What the log said
I turned on one structured line per tool call and left it for 16 days. 427 runs, 1,318 calls.
jq -r 'select(.event=="tool_call") | .tool' calls.ndjson \
| sort | uniq -c | sort -rn
612 get_content_brief
402 save_draft
187 list_pending_slots
54 get_brand_voice
33 publish_draft
18 schedule_post
7 get_channel_stats
3 search_published
2 update_slot_status
Nine lines for eleven tools. archive_draft and get_competitor_feed never showed up at all, which is the kind of absence uniq -c will never tell you about. The top three add up to 1,201 of 1,318 calls, a bit over 91%.
All eleven definitions ride along in every request. I ran the tool block through the tokenizer the model actually uses and got a bit over 6,400 tokens. The three tools doing the work account for about 1,900 of those. So on each of those 1,318 calls I paid to re-send roughly 4,500 tokens of JSON schema describing tools the model mostly ignored. Somewhere near 5.9 million tokens of context spent on a menu.
Then the failures. 37 calls came back with argument validation errors, and 29 of them were the model reaching for publish_draft when it meant schedule_post, or the reverse. I put the two descriptions side by side and I would have gotten it wrong too.
[
{ "name": "publish_draft", "description": "Publish a draft to its channel." },
{ "name": "schedule_post", "description": "Publish a draft to its channel at a given time." }
]
The second one is the first one with an extra argument. Blaming the model for that is cheap.
I am not the only person running this math this month. A post here about cutting an MCP server from 15 tools to 4 pulled 76 comments against 61 reactions, a ratio you almost never see on this site, and more argument than applause usually means nobody agrees on where the line goes. InfoQ reported this week that Uber put a gateway in front of more than 800 MCP servers and over 5,000 tools, with incremental discovery so an agent never sees the whole catalogue at once. Same problem, four orders of magnitude up.
The fix I tried first and threw away
My first move was the clever one: collapse everything into a single call tool with an action string and a free-form payload, dispatched server side. The tool block dropped to a little over 300 tokens and I was briefly very pleased with myself.
Then the agent started inventing actions. create_brief. draft_save. Names that read exactly like my own convention and did not exist. Across 90 runs, something like 1 in 6 calls needed a retry, because an enum the model cannot see is an enum the model will guess. The schema was not overhead, it was the contract. I put it back and went after the list instead.
The three questions
Does it write? Then what undoes it?
Every write tool in the registry has to name the tool that reverses it, as a required field. If I cannot name the inverse, the agent does not get the tool. That killed archive_draft in about four seconds: the unarchive was me running an UPDATE by hand, which is not an undo the agent can reach.
type ToolSpec = {
name: string;
mode: "read" | "write";
undo?: string; // required when mode === "write"
owner: string; // a person, not a team alias
secret?: string; // credential id this tool uses
rotatedAt?: string;
};
for (const t of tools) {
if (t.mode === "write" && !t.undo) {
throw new Error(`write tool ${t.name} has no documented undo`);
}
if (t.mode === "write" && !t.owner) {
throw new Error(`write tool ${t.name} has no owner`);
}
}
It fails at boot, not at runtime. The secret and rotatedAt fields are there for the question nobody asks until something leaks: each tool is a credential with a blast radius, and an agent that holds nine of them holds nine keys. Mine had two tools sharing one API key whose last rotation I could not date.
Reading only the descriptions, would I pick the right one?
Hide the names, read the one-liners, choose. If two tools differ only by an argument, they are one tool. publish_draft and schedule_post became one publish_draft with an optional at. Over the following 16 days, wrong-tool errors went from 37 to 3, and two of those three were my planner sending a bad payload.
This is the question that pays fastest, and it costs nothing but reading your own manifest as a stranger would.
Does it earn its schema?
Calls per week against tokens of definition, every request, forever. get_channel_stats: 7 calls in 16 days, around 480 tokens riding along 1,318 times. It did not earn it.
None of the deleted tools disappeared. They moved behind a second call. list_capabilities returns names and one-line summaries, and the full schema for a group arrives only when the agent asks for it. The planning step sees 4 tools. The rare paths pay their own cost at the moment they are used, which is the same trick Uber's gateway does with a much bigger catalogue.
Where it landed
Four tools in the default surface: get_content_brief, save_draft, list_pending_slots, publish_draft. Tool block down from a bit over 6,400 tokens to about 2,050. Wrong-tool errors from 37 to 3 over a comparable window. The agent has asked for the discovery group 9 times in 16 days, and get_competitor_feed is still at zero, so it will probably come out of the registry entirely.
One number I will not claim: latency. The p50 of the planning step went from around 9s to under 7s, but I changed the tool list and the prompt in the same week, so attributing that to the schema would be a story, not a measurement.
And one thing I genuinely do not know how to scale. The undo rule works because the registry has one maintainer. With nine teams publishing tools into a shared gateway, somebody is going to type undo: "manual" and move on, and the boot assertion will pass while the guarantee quietly stops existing.
So: hard cap on the default surface, or discovery from day one? And what do you do with a write tool whose only inverse is a human with database access?
Originally published on the Revin blog.
Top comments (4)
On the write tool whose only inverse is a human: does the agent see it at all today, or only behind an approval step a person answers?
We build Auten (I'm on the team), an MCP server that drives real screens, so most of our actions are writes with no undo: send, buy, delete. What held up for us is a short instruction shipped with the server to ask before those, with the client's own approval prompt as the hard gate. The instruction alone isn't a guarantee, for the same reason your
undo: "manual"worry describes.I work on Tokenade, so I count the bill per turn. Each turn re-sends the tool schemas, so the 8 tools that took 9% of the calls still cost on every request they sit in. Put schema tokens per turn next to the call counts, or the rarely used tools stay invisible.
For write tools where the only inverse is a human with database access, I stopped treating them as direct execution tools.
If an action cannot be rolled back cleanly via code, I split it into two: a write that saves a local staging record, and an execution step that flips a trigger flag. The agent can write, inspect, and patch the staging record as many times as it needs. If it hallucinates arguments or drops a field, nothing escaped into production, and fixing it is just rewriting that record before the trigger fires.
On discovery vs hard cap: hard cap for the three or four tools in the core loop, deferred schemas for the rest. If a tool runs once a week, paying 500 tokens of schema on every request is just paying rent on empty space.
pinning only the two or three tools a workflow needs in the server url cut the schema cost a lot for us with no change to the agent
curious if the 37 argument errors dropped too once the two lookalike tools were merged