← All articles
From the Workshop

MCP, second attempt: 48 tools, three channels. What we learned in October.

In September I wrote that MCP is slow, and that this is not a bug but the blueprint. Back then with 33 tools. In October we built, today it is 48, and the dose got smaller along the way, not bigger. Dirk Rottmar said it under the article in one sentence: great things in small doses, the rest deterministic. That sentence moved me to document where we stand: what we learned in October, what 500 measured runs say about it, and the one question we now ask of every tool.

Ok, in order: in September I wrote that MCP is slow, and that this is not a bug but the blueprint. In October we then built, 33 tools became 48, and along the way we learned something I did not know back then: the dose gets smaller, not bigger. I would probably have written that down at some point. Then Dirk Rottmar, a software architect for AI systems, left a comment under the article that says exactly that in two lines: "MCP is tooling that should be used very deliberately and sparingly. It is magic! As with everything in life: enjoy the great things in small doses. The rest is deterministic, absolutely consistent, even when it hurts." That sentence moved me to document where we stand today. Here it is.

The sentence that sums up October

What I like about the sentence is the "even when it hurts". Because that is exactly where it stuck in October, with every single rebuild. Anyone who has once seen a model turn a vague request into the right tool call wants that everywhere. It feels like progress. And every time you rebuild such a spot into a fixed function call, it feels like a step back. It is not. It is the point where you stop paying the model for things it does not need to decide.

What I promised in September

In September six points stood at the end of the article: load tools only when they are needed. The model writes a script instead of thirty rounds. No state in the server. Small, clear tools. Filter data before the model sees it. Timeouts and a script that cleans up. I wrote then that this is what we are rebuilding. To be fair, that was a plan, not a state. Now, six weeks and fifteen tools later, I can say what of it has held.

Someone has measured it since

The best thing that has happened since: someone measured the debate instead of having it. In spring, Arize ran 500 trials, same model, same agent, same 25 GitHub tasks in four difficulty tiers, only the tooling different: the official GitHub MCP server against the command line with a skill, in two variants, and as a fourth arm the bare shell with nothing at all.

ResultMCPShell + skill
Correctness, all tiers0.830.83
Cost, hardest tierabout six times higherbaseline
Time, hardest tierabout five times longerbaseline
Calls per hard taskabout 12about 5
Stayed within allowed tools33%over 99%

Correctness is the same. Everyone gets there. But on the complex tasks, "for each milestone the completion rate, then the most common label pairing", MCP costs six times as much and takes five times as long. Not because the server is bad. Because a server with fixed endpoints cannot compose. There is no endpoint for that question, so the model splits it into twelve calls, each comes back as verbose JSON, each carries the whole previous context along again. The shell can compose, with pipes, grep, sort. And the finest finding: the MCP agent was instructed to use MCP only. In two thirds of the hard cases it ignored that, wrote the JSON to disk and carried on in bash. The model itself noticed that the protocol was not enough.

But, and this is the part the "MCP is dead" camp leaves out: on "create a branch and open a pull request", MCP wins clearly, eight calls instead of 22, a third of the cost. Because exactly the right endpoints exist for that. The shape of the task decides, not the protocol. And the fourth arm, the bare shell without a skill, was actually best on correctness, because the model knows the GitHub command line from its training data. That does not hold for your own company interface. No model knows that one. There you need either a skill or MCP, or you give up.

Fact is: in 500 runs MCP was neither dead nor the standard. It was the right tool when the task mapped exactly onto an endpoint, and the six-times-more-expensive one when the task had to be composed. That is no longer an opinion. It is measured, with open data.

The one question per function

What we have made of September and of these numbers is a single question we now ask of each of the 48 tools before it gets a channel: does the model have to decide something here, or is it just typing the call?

For most functions the answer is uncomfortably clear. "Read ticket", "set status", "run tests", "check commit": the model decides nothing there. It knows what it wants and phrases the call. Every time it does that through a protocol, we pay context for the description, a round for the call, and context for the answer. Paid three times for a decision that was not one. Those functions belong in the process, as a normal call, deterministic. That is the "even when it hurts".

Three channels instead of one protocol

Three channels follow from the question. This is what I would advise anyone with more than twenty tools today, and this is how we keep it:

1

Deterministic, without a model. Everything where the decision has already been made by the time the function is called. Read, set, check, log. A function call in the process, rules before model. Costs no context, has no latency, cannot hallucinate. This is the largest channel, and it grows fastest, because every new function lands here first and only moves away once it proves it needs a model.

2

Script instead of rounds. Everything that has to be composed: fetch, filter, join, summarise. The model writes ten lines once against a typed library, the script runs without a model, and what comes back are the three lines that count, not the ten thousand. This is exactly where MCP collapsed at Arize, and exactly where the model fled into bash by itself. We give it bash right away, but with a railing: our own library, a fixed folder, a timeout, no network to the outside.

3

MCP, in small doses. What is really a tool: remote, with its own sign-in, with state across several steps, not in the training data. And what requires the model to really choose which of several tools fits. Here the protocol is good, because it brings auth, discovery and permissions that a command line does not have. These are the few cases where the magic is worth the price.

New function → does the model have to decide? → no: channel 1, call in the process → yes, but compose: channel 2, script → yes, choose between tools, remote, auth: channel 3, MCP

What we learned in October

Four things that were not in the September article, because I did not know them yet.

First: the dose falls while the count rises. From 33 to 48 tools, and the share that runs through the protocol has got smaller, not larger. That was not planned. It happened because every new function had to answer the question above, and most answered no. In September I expected we would learn to use the protocol better. Instead we learned to use it less often.

Second: tool selection is a job for a small model, not the big one. In September I wrote that a small model should pick the two to four fitting tools from the request before the big one reads. The principle holds, and it has a side effect I did not expect: once the selection happens before the big model, it hardly matters how the tools behind it are connected. The big model sees two descriptions instead of 48, and whether those two arrive via MCP or as a function, it does not notice. The context eater was never the protocol. It was the list.

Third: the script is the better protocol, but only with a railing. A model that writes code against our library is faster and cheaper than any sequence of calls. It is also more dangerous if the folder the script runs in is not cleanly fenced off. Since the Cursor incident a week ago, our rule is: fixed folder, no network connection to the outside, timeout, and the script is logged before it runs, not after. The railing costs an afternoon. Without the railing, channel 2 is not a channel but a hole.

Fourth: the model itself knows when the protocol is not enough. The Arize finding that the agent ignored the instruction in two thirds of the hard cases and went into bash matches what we see in our logs. When a model starts building detours around a tool, that is not a discipline problem of the model. It is a hint that the tool has the wrong shape. We now read those detours as findings, not errors, and rebuild the function.

My experience: every spot where I wanted to keep the magic turned out to be a spot where the model decided nothing. The rebuild hurts every time. And every time it was right.

What the spec can do now

In fairness: the protocol has moved. The revision of 28 July is now stable, and it cleans up exactly where I described problem three in September. No more sessions at protocol level, no session header, any request can be answered by any server instance. Long-running tasks are an extension with polling instead of blocking calls. Roots, sampling and logging are deprecated. Full JSON Schema support for inputs. That is the protocol built for the server, no longer for the laptop.

It changes nothing about the dose. A stateless MCP is still one call per round with a description in the context. It is now a better tool for channel 3. It is no argument for pulling channels 1 and 2 back in.

What remains

The September article was right in the diagnosis and too early in the therapy. MCP is slow because it was built for a few tools with someone watching; that still holds. But the answer to that was not to use the protocol better. The answer was to use it less often and to ask the question Dirk had in one sentence and I had in twelve minutes: does the model have to decide here? Mostly not. Then deterministic. Consistently. Even when it hurts.

To close

48 tools, three channels, one question. The largest channel needs no model. The second needs it once, for ten lines of script. The third is MCP, and it is small, and that is good. The magic in small doses. The rest deterministic. Dirk said it in one sentence, October taught it to us, and 500 runs measured it.

Sources

Arize, May 2026 (updated October). MCP vs. CLI Skills for agents: what our eval found. 500 trials, Claude Opus 4.6, Claude Agent SDK, 25 GitHub tasks in four tiers, four arms. Correctness 0.826 to 0.845; hardest tier MCP over six times more expensive, five times slower, about twelve calls against five; tool fidelity MCP 0.33 against over 0.99; counter-example branch and PR. Data open on GitHub.

MCP specification, revision 2026-07-28. blog.modelcontextprotocol.io. Stateless transport without session ID, tasks as an extension with polling, roots/sampling/logging deprecated, JSON Schema 2020-12.

The debate. MCP is dead, long live the CLI (February 2026), Milvus: Is MCP dead? as the counter-voice.

My article from September. MCP is slow. That is not a bug, that is the blueprint. The six points at the end are the plan I measure against here.

Status and caveat. 7 November 2026. The Arize numbers come from a measurement the company ran itself, with open data; I have not recalculated them. Dirk Rottmar's comment is quoted with his consent, translated from German, with thanks. The statements about NeoCoder are our own, as of today, and the number of tools keeps growing.

How this text was made

Written by me. The thoughts, the values, the learnings, the mistakes: all mine. Grammar and spelling are corrected by a twin model of my own, trained on my texts. Sometimes that turns into a mix. Of course I read everything again, but nobody is infallible. Whatever stays in is mine too.

Read more All articles

Honest thinking.
Straight to your inbox.

One or two emails a month. No gloss, no spam.