Sitemap

The battle for context: why MCP vs. CLI is the wrong fight

13 min readJun 4, 2026

First post in a while! Buckle up for a social media discourse rabbithole, plus my “Roman Empire” obsession of late. I’ve been working on pushing the frontier of AI agents, and have done a lot of work with MCP.

As of mid-2026, the Model Context Protocol is still far from perfect. But it solves a real problem, and the loudest fix making the rounds on social media (“just use a CLI”) quietly rebuilds most of the solution with extra steps.

Rumors of MCP’s demise are greatly exaggerated, but the comparison against CLIs isn’t the right conversation to be having anyway. Let’s dive in!

Some background: agents and how they touch the world

A large-language model (LLM) can’t actually do anything. It predicts tokens. When ChatGPT runs a web search or Claude Code edits a file, the model can only reach out to the world indirectly. The LLM emits a sequence of tokens that the software around it (the “harness”) recognizes as a request. The model writes something that amounts to web_search("Valkyries score last night"), the harness pauses, runs ordinary deterministic code to do the actual search, and feeds the result back into the model's context window. Then the model predicts more tokens, now with the answer in hand.

The interesting part is that web_search call. A lab could train a model to emit a fixed menu of special tokens that only its own harness understands. But there's a large developer community building on these model APIs, and they need agents to do a vaster set of work. That's where tools come in: each tool is a capability the agent gains to act on the world outside its training data. Cursor's coding agent is a loop around tools like "edit file" and "run command." Without these, you might have a very articulate model, but it can't change a single line of your code without you tediously copy-pasting it.

The next problem showed up quickly. A handful of labs can ship a handful of built-in tools, but they can’t ship the stack you depend on. What if you want an agent in your Notion workspace, your Figma files, your Stripe account, or your Google suite? Every SaaS company has its own surface, and circa 2024, the pressure to connect those surfaces to agents became impossible to ignore all around.

Anthropic introduced the Model Context Protocol on November 25, 2024 to close that gap: “a new standard for connecting AI assistants to the systems where data lives.” It launched with servers for Google Drive, Slack, GitHub, Postgres, and a few others, and it caught on quickly. Home hackers wired up their Bevi machines and Philips Hue bulbs. Prosumers connected their productivity suites and started doing real knowledge work by chat. Engineers plugged their coding agents into monitoring and docs and shipped faster. MCP became the default answer to “how does this third-party thing reach the model” (or, flipping the perspective to the client: “how does my model talk to my external tools”).

Fast-forward to when the CLI discourse caught fire

In early 2026, a take went viral: MCP is bloated ceremony, and you should just give the agent a command line. The argument had been building for months. Armin Ronacher laid the groundwork in mid-2025 with “Tools: Code Is All You Need” and “Your MCP Doesn’t Need 30 Tools: It Needs Code”. Cloudflare shipped “Code Mode”, arguing models are better at writing code that calls MCP than at calling it directly. By November, Simon Willison was writing that he didn’t use MCP at all anymore when working with coding agents: CLI utilities did the job. By late February, “MCP is dead, long live the CLI” was a front-page event.

This perspective comes from very reasonable footing, and it’s worth digging in meaningfully before blanket-disagreeing. CLIs have decades of history. They live on your system and compose natively: bash is a programming language whose verbs happen to be programs, so you can pipe and loop and filter without a rigid schema. Models have been trained on oceans of real shell and real code, far more than they’ve seen of any synthetic tool-call format. And a bloated MCP server can dump dozens of tool definitions into context before the agent has done anything at all, contributing to a class of problem I often call “context pollution”.

Sidenote: if you’re thinking “who cares; these days models have 1M token context windows!”, in my experience, it does matter! Low-signal context dilutes quality, slows down your inference, costs more, and leaves you less room to get the model’s best output, which is often in the first ~100k tokens or so.

Anthropic’s engineering team measured the code-execution rewrite against the naive approach: 150,000 tokens collapsed to 2,000, a 98.7% cut. Cloudflare’s follow-up claimed a 99.9% reduction: a full 2,500 endpoint API expressed in about a thousand tokens. When the people who build the protocol publish numbers like that, the critics aren’t wrong about the symptom.

My take: they’re wrong about the diagnosis.

“MCP vs. CLI” is the wrong fight

Pit a well-designed CLI — one somewhat biased, shameless plug is for Notion’s ntn — against a well-designed MCP server, and ask what’s actually different (it’s not that much at a high level.)

Three main areas decide whether an agent gets quality work done, and none of them are in the realm of the protocol or binary:

Context and tools. An agent is its tools plus what it can see. Both MCP and CLIs are ways to hand the model capability and route the results back. That’s the same job.

Context pollution. Every token a tool spends describing itself, or echoing output the model doesn’t need, is a token stolen from reasoning. This is the actual problem the CLI crowd identified, and the fixes all converged on the same idea from both sides. Anthropic’s Agent Skills put procedural knowledge on the filesystem and load it progressively: a name and a one-line description upfront, the full instructions only when needed. MCP added code execution so tools are code you can import instead of pre-loaded definitions. Cloudflare’s Code Mode does the same trick. These aren’t arguments against MCP. They’re MCP and its neighbors learning the lesson that loading everything eagerly is the mistake, and the lesson applies the same way to a CLI with a sprawling --help tree.

Model competence at composing tools, which is trainable. Whatever gap exists between calling a tool and writing code to call a tool is a gap in the model, not the medium. It’s all tokens. Anything the model does better with one shape, you can more-or-less close with RL or a LoRA. “Code Mode” works because it reshapes MCP calls into the function calls models already know, which proves the medium isn’t the ceiling.

So my sharp, hot take when this topic comes up a little too much: a good CLI is MCP with extra steps. To make a command line work well for an agent you end up reinventing, by hand, most of what MCP gives you for free: progressive disclosure (a noun/verb hierarchy instead of a flat wall of flags, like the Stripe CLI), consistent auth, token-efficient I/O, and a layer of skills to bind it all together and tell the model how to drive it. Build all of that and you haven’t properly escaped MCP. You’ve rebuilt it without the schema, the registry, or the shared vocabulary and governance. And as a bonus, you have to maintain those areas yourself.

That skills layer is where the CLI story gets expensive. A well-crafted, hierarchical CLI still needs a pile of help text, or RAG over docs, or keyword search; really, something the model can lean on and discover. Those skills have to be written, kept in sync with the underlying commands, and distributed somehow. It’s a new surface to maintain and a new thing a careful operator has to scrutinize. MCP gives the model structured tools with typed fields and routing baked into the descriptions, and lets the model’s training own the rest. The CLI asks you to assemble that yourself, correctly, forever.

More machinery the “just use a CLI” narrative waves away

Most of the “CLIs are simpler” case rests on the local case: one developer, one trusted machine, credentials already sitting in the environment. That’s the easy world. The moment the agent isn’t you sitting at your laptop — say, a cloud agent acting on your behalf — the simplicity evaporates, and the parts MCP standardizes turn out to be essential pillars.

Auth is the clearest example. With MCP, the harness holds your credentials and attaches them out-of-band; the model emits tool arguments and sees tool results, never the secret itself. With a raw CLI, the model is the thing constructing the commands, which means the credential has to be reachable in the environment the model is manipulating. On your own machine, that’s fine. In a remote agent, that’s a credential sitting inside the exact context a prompt injection is trying to hijack. You can system-prompt against exfiltration, but that’s a mitigation, not a guarantee. The honest way to close the gap is to put a layer in front that holds the credential and rewrites the model’s calls. At that point, again, you’ve likely reinvented MCP with more steps.

EDIT (2026–06–04 22:26 UTC): Jonathan Clem (Notion) astutely pointed out that newer sandbox APIs like Vercel Sandbox — and I believe Anthropic’s Claude Managed Agents environment — offer vault APIs for non-MCP secrets, such as CLIs, so some of this is quickly becoming outdated!

But… being precise here, since the strong version of my auth claim is flat wrong, and the security folks would say so. MCP does not make you safe from prompt injection. Poisoned content comes back through tool results (or metadata, if the third-party server or its distribution is even somewhat compromised), and those reach the model’s context whether the tool was an MCP server or a shell command. Essentially, the injection surface is the content, not the transport. What MCP changes is credential custody: the model can’t leak a key it never holds. That shrinks the credential-exfiltration surface. It does nothing for the confused-deputy problem, where a model that’s allowed to call an authenticated tool gets tricked into misusing it. Just elaborating since it’s worth keeping those two straight so I don’t blend them into an overconfident stretch.

Auth flexibility is also one spot where MCP is still behind, to be fair. RFC 8628, the OAuth Device Authorization Grant (the flow behind logging into a smart TV, and the one gh uses to log you into GitHub from a terminal) is a finished, published standard. MCP's auth spec doesn't adopt it; the stable flows are all auth code + PKCE, and mainstream clients tend to implement only that path (the device-flow request is still open). So this is a genuine point for the CLI side: a model can learn an unfamiliar CLI's auth dance by trial and error in a way MCP's stricter spec doesn't yet allow.

This “industry meets reality” theme has come up lots in the last few years and will continue to. Every time it does, the discourse quickly somehow shifts to throwing away the baby with the bathwater — “why don’t we get rid of MCP?” But the fix to a problematic system that requires coordination isn’t always throwing out the whole system like the X/Twitter bandwagon might lead you to believe. In this case, I’d argue it’s us working together to do the hard work and close the gaps in the spec, while making room in the discourse for multiple options to coexist.

Standards should move faster, so let’s try to do that

If this is all convincing so far, then the deepest valid critique of MCP isn’t token consumption or auth. It’s that standards are slow, and large companies and fantastic startups aren’t twiddling their thumbs waiting. The pattern repeats: the standards makers and the wider ecosystem disagree about what matters, so people invent their own thing, and the ecosystem fractures into grassroots conventions (the xkcd’s “(n+1)th competing standard” comic comes to mind.)

MCP’s auth story is a clean example, cutting toward “evolve faster” over “abandon ship.” MCP recently classified servers as OAuth resource servers rather than authorization servers, a real correction to an early design mistake, made openly. More pointedly: the spec now recommends Client ID Metadata Documents (CIMD) as its default client-registration mechanism, sitting at SHOULD above Dynamic Client Registration’s MAY. CIMD is a good idea: your client_id is just an HTTPS URL the auth server fetches, no registration round-trip. But it is an adopted IETF working-group draft, not a published RFC. MCP promoted a generally non-ratified draft to its recommended default. You can read that two ways: standards-body purists wince, but it's also exactly the kind of move that lets a protocol keep pace with a field moving faster than the RFC process. I lean toward the second read, especially with experts from Auth0 and other peer companies at the helm, with eyes open about the risks.

The fracturing shows up in discovery too, and this is where I’ve watched the sausage get made. When you paste a URL into an MCP client, how should it know whether that domain even has a server worth connecting to? There’s no ratified answer yet, so Notion and Cursor shipped a convention: a mcp.json file at /.well-known/mcp.json mapping a domain to its server. You can curl [<https://www.notion.com/.well-known/mcp.json>](<https://app.notion.com/.well-known/mcp.json>) right now and watch it show mcp.notion.com/mcp. Meanwhile the official spec has its own discovery proposals in flight, server cards at a different well-known path, an IETF discovery-URI draft, with none of them agreeing on the path yet. That's the fracturing in one paragraph: a real need, a grassroots convention that works today, and the slower formal process (Anthropic / Linux Foundation / etc. coalition & open-source contributors) catching up behind it.

Yet, the answer to fracturing is coordinating more. The whole value of a standard is that the Stripe server and the Notion server and the next thousand servers speak one language, so a client can connect to all of them without bespoke glue per integration. There’s a real MCP Registry now, built as an upstream source of truth that sub-registries and enterprise gateways like Runlayer sit on top of. The marketplace, distribution, and trust questions (who vouches for a server, how you detect a malicious one) are truly unsolved and high-risk. But my pitch is that “unsolved inside a governed standard” beats “unsolvable across a thousand bespoke CLIs,” because the standard gives you a place to solve it once.

What this looked like from inside Notion

I’m not neutral here. I helped build Notion’s hosted MCP server, and the thing that surprised me most was how much of the work was exactly the “machinery” the CLI argument treats as overhead.

We shipped the open-source server in early April 2025 and the hosted version a couple of months behind it, staffed deliberately to get out ahead of where the industry was heading. Token efficiency was in the design, and an ongoing bugbear we had to stay on top of. We shipped a custom Notion-flavored Markdown so the model got more content per token and needed fewer round-trips than the open-source server, which “hit context token limits pretty quickly because of the back and forth tool calls.” Auth wasn’t an afterthought: one-click OAuth across ChatGPT, Claude, Cursor, and VS Code, with actions attributed to the actual user instead of a shared bot, for good governance and tracking. From the very early days, we worked directly with Cursor’s engineering team on a streamable-HTTP connection experience and kept SSE around for clients that needed it. The discovery convention above came out of that same collaboration: Notion and Cursor “leading the way on new conventions that make MCP easier to discover, more secure, and more dependable.”

None (or at least very little?) of that work was fluff. It made the difference between a demo (hello, WorkOS MCP Night last year!) and something an enterprise will actually turn on for their workspace members. You could rebuild every piece of it on top of a CLI (and more recently, Notion did, since CLIs have a true, valuable home in the ecosystem despite everything I said!) But with CLIs, you write more or less the same auth flow, the same token-dense output format, the same discovery handshake, but without anyone else’s server speaking your dialect, and having to vendor your skills and/or stumble through some incorrect guesses for args and flags.

The TUI-versus-GUI rerun

We’ve seen this movie before. The fight over the dev environment keeps producing the same story: someone declares the modern thing too heavy, returns to first principles, builds something lean and pure. This slowly adds back the missing pieces until they’ve reinvented the heavy thing with a different name. The terminal purists rediscover, one feature at a time, why the GUI existed (like, being able to see the UX affordances of the app right in front of you, visually, on your color computer screen?!) And then the same companies ship Codex.app and Claude.app anyway, and the market for Composer opens up. Because it turns out a large demographic of people want visual interaction and feedback, and maybe to use the mouse for its intended purpose.

“Just use a CLI” is that same instinct pointed at agents. Strip away the protocol, enjoy the simplicity, and then bolt on progressive disclosure, consistent auth, structured output, and a skills layer (maybe some plugins) until you’ve built MCP in a trench coat. The instinct to simplify is good. The error is forgetting whose shoulders you’re standing on, so I personally feel biased toward reflection and improving what’s there and doing better in the community and society I live in.

So: my love letter

MCP is glue, and glue is unglamorous. It’s also far from finished: auth flexibility lags behind, discovery is a patchwork, the trust-and-distribution layer is wide open, and the spec moves slower than the field around it. Innovations are distributed extremely unevenly across the industry.

A lot of it is, it’s hard to drive consensus, navigate human and corporate politics, and align on tech specs that make everyone (or enough parties) happy. At the end of the day, none of that is a reason to walk away. It just inspires moving through the to-do list as best we can. The protocol’s real win is that it’s malleable: a shared surface that a whole industry can push on at once, fix in the open, and improve without everyone re-deriving the same machinery in private. Progress isn’t felt instantly, but is felt broadly by a very leveraged, valuable part of society: how agents can mould the world and make people more productive.

“MCP versus CLI” is a leading framing that asks you to pick a winner. Both are valuable and deserve to be part of the developer ecosystem. The better question is what an agent actually needs to function well, where you’ll notice that a serious CLI and a serious MCP server are converging on many of the same answers. One of them just gets to share more of the work.

Even if every serious company decided today that MCP is a waste and stopped investing in it, millions of customers still depend on it, often without even realizing it, so it’s not dying anytime soon. How often does the average layperson connect their files to ChatGPT to proofread a report or analyze a spreadsheet, never thinking about the fact that MCP is what makes that possible? There’s no higher praise for a protocol than its ability to disappear from your life and act like it was never there.

I look forward to continuing to use MCP heavily to plug into the world’s context, and I’m excited to keep advancing the ecosystem any way I can. Congrats (and thank you!) to all the wonderful people in & out of tech who I’ve met, talked to, worked with, and celebrated with over the last few years.

--

--

Kenneth Sinder
Kenneth Sinder

Written by Kenneth Sinder

Canadian software engineer in SF. Lover of good interface design, personal finance, running, making awesome jokes, donating blood & listening to synthpop.