There are two ways to let an agent use your product.
A) Ship an MCP server. Define tools, describe them, run a process, the model calls them.
B) Ship a CLI. yourtool do-the-thing --json. The agent runs it in a shell like a human would.
For about a year the answer was obviously A. In 2026 a lot of teams quietly went back to B — and the ones who did aren't posting "MCP is dead" takes, they're posting latency graphs.
Here's what's actually happening. Five costs, in the order they hit you.
1. You pay for every tool, every turn — before the model does anything
An MCP server's tool definitions are loaded into the context window up front. Not when a tool is used. Always.
- 40 tools × a real JSON schema with descriptions ≈ tens of thousands of tokens
- That's paid on turn 1, turn 2, and turn 60
- The model has done zero work at this point
A CLI costs you one line in the system prompt. If the agent needs the details it runs yourtool --help — once, only when it matters, and the output can be 30 lines instead of 40 schemas.
Anthropic measured the extreme version of this: a workflow that burned about 150,000 tokens passing tool definitions and intermediate data through the model dropped to about 2,000 tokens when the same tools were exposed as code the agent calls — a 98.7% cut. That gap is not a micro-optimisation. That's the whole bill.
The rule: tool definitions are rent, not purchase. You pay it on every single turn until you delete the tool.
2. A CLI composes. MCP tools don't.
Watch what a shell lets the model write in one action:
yourtool list --json | jq '.[] | select(.status=="failed") | .id' | head -20
One round trip. One result in context.
Now the MCP version:
-
list_items→ 800 rows come back into the context window - model filters them in its own head
-
get_item× 20 - model assembles the answer
Four round trips minimum, and step 1 already poisoned the window with 780 rows nobody wanted.
The shell has had composition for fifty years — pipes, redirection, xargs, exit codes. MCP has a list of function calls. Every "combine two tools" case becomes the model's job, done in tokens, done badly.
The rule: if your users would chain your tools, a protocol with no chaining primitive is the wrong shape.
3. Every byte travels through the model
This is the one that actually shows up as slow.
An MCP tool result goes into the context window. There is no other destination. So:
- A 5 MB JSON response doesn't just cost money — it evicts everything useful and you get the classic mid-session quality slide
- The model then re-reads that blob on every subsequent turn
- You cannot "process and discard"
In a shell, intermediate data can just… stay on disk.
yourtool export --json > /tmp/out.json # 5 MB, never enters context
jq '.summary' /tmp/out.json # 3 lines do
The agent sees three lines. The five megabytes never existed as far as the context window is concerned.
The rule: MCP has no
> file. Every result is a broadcast to the model. Design return values like you're paying per character, because you are.
4. A protocol hop that buys you nothing (on a machine with a shell)
Stack them up:
| CLI | MCP |
|---|---|
| exec a binary | spawn/connect a server |
| argv | JSON-RPC over stdio or HTTP |
| exit code | protocol errors + tool errors + transport errors |
| stdout | structured content blocks |
| — | handshake, capability negotiation, lifecycle |
Every row is latency and a failure mode. And they're failure modes your agent handles worse than a non-zero exit code, because "the server disconnected" is not something the model can retry intelligently.
The 2026 spec revision made this concrete for a lot of people: protocol-level sessions were dropped in favour of a stateless core. Sensible for enterprise scale-out — and it broke every server that had quietly built state on top of the session. Nobody's --help output broke that week.
The rule: a transport you don't need is not free. It's latency plus a category of error your caller can't reason about.
5. The model already knows git. It has never heard of create_document_v2.
This is the underrated one.
git, curl, psql, ffmpeg, gh, jq — the model has seen millions of examples of these. It knows the flags, the idioms, the error messages, and what to do when one fails.
Your bespoke tool surface has zero training examples. So you compensate with description text — which is cost #1 — and you still get the classic failures:
- two tools that overlap 80%, picked by coin flip
- a tool chosen because its name was closest, not because it was right
- the model inventing a parameter that reads plausible and doesn't exist
A CLI that follows Unix conventions inherits all that prior knowledge for free. --json, --dry-run, non-zero exit on failure, errors on stderr. The model has strong priors about every one of those.
The rule: conventions are pretrained context you don't pay for.
The honest part: when MCP is clearly right
None of the above is an argument that MCP is bad. It's an argument that MCP is being used in the one environment where it's weakest — a coding agent that already has a terminal.
Keep the MCP server when:
- There is no shell. ChatGPT, Claude's chat surfaces, a mobile app, an embedded assistant. This is the real answer and it's a big one. A CLI is worth nothing to a user who has no machine to run it on.
-
You don't want to hand the model a shell. MCP is a permission boundary you control, tool by tool.
bashis not. -
Your users aren't engineers. They will never
brew installanything. - You need what the protocol actually provides — resources, subscriptions, sampling, an auth flow that a CLI would have to reinvent badly.
- You're a hosted service. There's no binary to install in the first place.
Delete it — or shrink it hard — when:
- Your consumers are coding agents that already have a terminal
- Your MCP server is a thin wrapper over your own CLI or public API
- You have more than ~20 tools and no progressive disclosure
- Your tools' most common use is being chained together
The middle path (what most teams should actually do)
You don't have to choose. The pattern that's winning:
-
Keep MCP thin. Three or four tools —
search,fetch,execute— not forty. - Expose the rest as code, not schemas. Let the agent discover and call your API from a sandbox instead of loading every definition up front. That's where the 150k→2k number comes from.
- Ship the CLI too. It's usually a day of work over an API you already have, and terminal agents will prefer it without being told.
-
Make results small by default.
--jsonplus a summary field, with full data behind a flag or a file path.
The 60-second audit
Ask these about your own server:
- How many tokens are my tool definitions? (Count them. Actually count them.)
- What's my p50 result size? What's my p99?
- Are two of my tools doing the same job?
- If I deleted the server today, could a competent agent do this with
curland my docs?
If the answer to the last one is yes and your users have terminals — you're paying protocol rent for nothing.
MCP's win was never that it was faster. It was that it made your product reachable from surfaces that have no shell. Use it for exactly that, and stop paying for it where it isn't buying anything.
Top comments (0)