The Stronger the Harness, the Thinner MCP Gets
Is MCP dead? Not quite, but it is moving down the stack. As agent harnesses learn to write and keep their own code, MCP stops being the way agents discover and call tools, and becomes the auth and policy boundary around them. A look at harnesses, OpenAPI, code mode, and where MCP is actually going.
@KinfishT
The MCP vs API debate keeps coming back, and its loudest version goes like this: "MCP is a dead end for serious agent systems."
Every time it comes up, people split into two armies, and both are convinced the other has never shipped anything.
One side: "My agent can write code. It can call the API directly. Why would I wrap it in another protocol?"
The other side: "So every agent rewrites the Stripe client from scratch, every time, with your API key sitting in its context? Enjoy your token bill."
My honest conclusion is that both sides are right, about different agents. The thing nobody said out loud is the variable that decides who wins:
The stronger the harness, the less MCP is needed as a way to call tools, and the more it matters as a way to control them.
To be clear up front: this isn't an "MCP is useless" post. MCP still solves real problems, and I'll list them. I'm pointing at a few drawbacks, and at a line that keeps getting thinner: as harnesses grow, more of what MCP does for the model, the harness can do on its own. What's left on MCP's side is the part that matters most.
MCP isn't dying. It's moving down the stack. Let's talk about why, and where it lands.
First, What Do We Mean by "Harness"?
A model on its own is a text predictor. A harness is everything around it that turns it into an agent: the loop, the tools, the file system, the shell, memory, permissions, sub-agents.
Not all agents have the same harness, and that's the whole argument:
| Agent | What it can do | What it needs from the outside world |
|---|---|---|
| Chat assistant (Claude.ai, ChatGPT) | Talk, call predefined tools | Someone else must hand it ready-made tools |
| Tool-calling agent (an app with a fixed tool list) | Pick from a menu | A menu: schemas, descriptions, a way to call them |
| Coding harness (Claude Code, Codex, OpenCode, your own) | Read files, write code, run it, keep it, reuse it | Documentation and credentials. It can build the rest |
A chat assistant is a person who can only order from the menu. A coding harness is a person who can walk into the kitchen.
MCP was designed, very sensibly, for the first two rows. The interesting question is what happens to it as more and more agents move into the third row.
MCP Is Four Jobs Wearing One Protocol
People argue about "MCP" as if it were one thing. It's actually bundling four separate jobs:
- Describe: "here are my tools, their names, inputs and what they do" (
tools/list) - Invoke: "call this tool with these arguments" (
tools/call) - Transport: JSON-RPC over stdio or Streamable HTTP
- Authorize: OAuth 2.1, audience-bound tokens, and the server as the place where policy lives
The trick is that a strong harness absorbs the first three and badly needs the fourth.
Let's go through them one by one.
Job 1: Describe. The Harness Already Has Better Sources
To call an API, an agent needs to know what exists. MCP's answer is: list every tool, with its JSON schema, and put the whole list in the model's context.
That's great for a chat app. For a coding harness, it's expensive in a very specific way: the description is resent with every request, whether the agent needs it or not. Connect ten servers with thirty tools each, and you've spent thousands of tokens before the user has typed anything.
Meanwhile, most serious APIs already describe themselves:
- an OpenAPI spec
- a typed SDK (TypeScript types are documentation that compiles)
- a CLI with
--help - actual docs
A harness doesn't need any of that pasted into its context. It can read it from disk when it's relevant, which is exactly how you use documentation. You don't memorize the Stripe reference before writing a line of code. You open it when you need it.
This idea has a name now: progressive disclosure. Load what's needed, when it's needed.
Job 2: Invoke. Code Beats Tool Calls
This is the part the "MCP is dead" take gets right, even if the headline overshoots.
With classic tool calling, every step goes through the model:
One tool call per round trip. Every tool schema and every result passes through the model’s context.
Every intermediate result flows back through the context window. If tool A returns a 50-page document and tool B needs it, the model reads the whole thing just to pass it along, like a manager who insists on photocopying every memo before forwarding it.
With code, the agent writes the plan once and runs it:
// Written once, run in a sandbox. Only the end result comes back.
const doc = await drive.getDocument("Q3 plan")
const summary = extractActionItems(doc) // in code, not in context
await salesforce.updateOpportunity("acme-q3", { notes: summary })
console.log("updated", summary.length, "items")
The 50 pages never touch the context window. Loops, retries, filtering, joins: all in code, where they're cheap and deterministic.
And this isn't a hot take anymore. It's where the people who build MCP are going:
- Anthropic's engineering team published code execution with MCP: tools exposed as a code API that the model calls from a sandbox. In their example (Google Drive to Salesforce), token use dropped from about 150,000 to about 2,000, a reduction of roughly 98.7% for that workflow.
- Cloudflare's Code Mode converts MCP tools into a TypeScript API and asks the model to write code against it. Their follow-up put the entire Cloudflare API behind two tools,
search()andexecute(), at around 1,000 tokens. The same API as classic MCP tools would have been about 1.17 million.
Their explanation of why it works is my favorite line in this whole debate, paraphrased: models have seen an enormous amount of real TypeScript, and only a small set of contrived tool-calling examples. Code is the native language of the model. Tool-call JSON is a second language it learned in a weekend course.
Notice the irony, though. Both of those still use MCP underneath. MCP stopped being the interface the model sees and became the backend that the code calls. Keep that in mind; it's the whole thesis.
The Questions Everyone Asks About Code Mode
Whenever code mode comes up, the same handful of questions follow. They're good questions, so here are short answers (the longer ones are further down):
- "Isn't this just programmatic tool calling? This isn't new." Mostly, yes. Anthropic ships it under exactly that name, and people have been giving agents "a sandbox and two tools" for a while. What's new is the scale of adoption, and that the companies behind MCP now recommend it.
- "Any semblance of security just left the chat." / "Doesn't this defeat MCP as a safe tool layer?" Only if the generated code holds the keys. Done right, it never does. The keys stay outside the sandbox, and MCP becomes the safe layer behind the code instead of in front of the model. That's a whole section below.
- "What about hallucinated tool calls?" Code fails louder than JSON tool calls. If the model invents a method that doesn't exist or passes the wrong arguments, a typed API catches it before anything runs, and the error message tells the model exactly what to fix. A made-up JSON tool call, by comparison, often just fails quietly at runtime. You can push this further by checking the whole script against your tool schemas before the first call fires, so a bad plan never touches a real system.
- "At this point, why not just give it the database? So… an API?" Because "can reach it" isn't the same as "should reach all of it." The more direct the access, the more the boundary matters: scoped credentials, read-only by default, approvals for anything destructive. And yes, "so… an API?" is closer to right than it sounds. More on that in a moment.
The Catch: Every Server Shouldn't Bring Its Own Sandbox
Here's the drawback of code mode that I think gets missed, and it's about where it lives.
Right now, code mode mostly ships inside each MCP server. Cloudflare's server has its own search and its own execute, running in its own sandbox. Imagine every other server follows the same pattern:
Each MCP server ships its own search, execute and sandbox. Code in one sandbox can’t reach another server.
With four servers, that's four search tools, four execute tools and four sandboxes, all doing the same job. Worse, they can't talk to each other. Code running in Cloudflare's sandbox can only call Cloudflare. So "find the failing deploy on Cloudflare, open a GitHub issue, and post it to Linear" can't be one program anymore. The model has to hop between sandboxes and carry the results itself, which is the exact round-tripping code mode was supposed to remove. We'd have rebuilt the tool-sprawl problem one level up.
The fix is to move that layer into the harness, or into something that sits next to it:
One search, one sandbox, one script across every server. The servers keep their tools and credentials.
One search over every connected tool. One sandbox. One program that can call Cloudflare, GitHub and Linear in the same script, with the data flowing between them in code. The MCP servers go back to doing what they're good at: describing their tools and guarding the credentials. The harness does the composing.
I think this is simply the right abstraction. The harness owns composition: search, sandbox, the script. The servers own capabilities and trust: tool definitions, credentials, policy. Each side does the thing it's actually good at, and you get one sandbox to secure instead of one per server.
Server-side code mode still has a place. For a gigantic API like Cloudflare's, or for a chat client with no sandbox of its own, the server doing it is the only option. But as a default, I'm convinced search and execute belong to the harness, not to every server. It's the same pattern as the rest of this post: as the harness grows, it absorbs the interface work, and the servers keep the trust work.
Getting Harness-Level Code Mode Today
You don't have to wait for every harness to ship this. Here's what exists right now, from "built in" to "bolt on":
- Built into the harness. Goose, Block's open-source agent, shipped Code Mode as a platform extension in v1.17. It wraps all your other extensions as JavaScript modules and runs the model's code in one Deno-based runtime, so the model sees a handful of tools instead of dozens. This is the pattern done properly: one layer, inside the harness, across every server.
- Bolted onto Claude Code or Codex. cmcp is a small open-source proxy: you register it as your only MCP server, add your real servers behind it, and the agent gets just
search()andexecute(). It generates TypeScript types from each tool's schema and runs the code in a QuickJS sandbox. It's a tiny project, but it shows how little is needed to retrofit this onto a harness that doesn't have it yet. - A gateway in the middle. Executor (more on it below) does the same aggregation, one catalog with search, describe and call, and adds the parts a quick proxy skips: stored credentials, per-tool policies and approvals.
- Experiments. Armin Ronacher's pi-codemode-mcp explores connecting MCP servers to the Pi harness through code instead of tool calls.
- Building your own harness. Anthropic's code execution with MCP describes the pattern on the client side: turn every connected MCP server into a code API inside one execution environment. Cloudflare's Agents SDK can wrap any MCP server in code mode from the agent's side, not just Cloudflare's own.
One honest caveat for the bolt-on options: a proxy or gateway that sits in front of all your servers sees all of your traffic, and often holds all of your tokens. That's fine, and often better than scattering them, but pick one you trust, or run it yourself. I see proxies as the stopgap. The end state is harnesses shipping this natively, the way Goose already does.
There's one thing this move doesn't solve by itself: if the harness's sandbox now calls Cloudflare, GitHub and Stripe, it needs to do it without holding all three sets of keys. That's the next problem, and the one MCP is best placed to solve. More on that below.
Job 3: Transport. HTTP Was Fine All Along
For a coding harness, transport is a non-problem. It has fetch. It has curl. It has a shell. JSON-RPC over Streamable HTTP is a perfectly good transport, but it isn't something a harness needs. An SDK over plain HTTPS does the job.
(Honestly, transport was never the exciting part of MCP. Nobody adopted MCP because they were thrilled about JSON-RPC 2.0.)
Everything Above the API Is a Wrapper
Here's a framing that cleared up a lot of this debate for me. Stripe, GitHub, Cloudflare: the real thing is the HTTP API. Everything else is a client sitting on top of it:
┌── SDK (for programs)
├── CLI (for terminals)
HTTP API ──┼── Web UI (for people)
├── MCP (for agents)
└── Skill (for harnesses)
An SDK is the API wrapped in your language's functions. A CLI is the API wrapped in commands and flags. And an MCP server, for most services, is the API wrapped in a protocol that agents speak. Same endpoints, different envelope.
So "MCP vs API" is a slightly broken question. It's really "which wrapper fits this caller?" A chat assistant can only open the MCP envelope. A coding harness can open any of them, and the SDK or CLI is often the cheaper one to open.
Two honest caveats, so this doesn't become a slogan:
- Some MCP servers wrap no API at all. Blender, your local files, a browser session. There, MCP is the interface.
- MCP adds things the other wrappers don't: a login flow designed for agents, risk annotations, the ability to ask the user a question mid-task. That's not "just a different protocol." That's agent-specific plumbing, and it's the part worth keeping.
But as a default mental model? The API is the source of truth. Everything above it is packaging. Pick the packaging your caller can open most cheaply.
So Is "Write It Once and Save It" Just MCP With Extra Steps?
I've noticed this with my own setup. When a harness works with an API for a while, it doesn't rewrite the integration every time. It writes it once, saves it to the file system or to git, and reuses it on every run after that. And when I look at what it ends up with, it's a saved, reusable, documented integration that the model calls. Which is, more or less, an MCP server.
Here's the honest version: it's MCP's first three jobs, done in code instead of protocol. In a harness it looks like this:
.agents/skills/stripe/
SKILL.md ← when to use it, auth, gotchas (read when relevant)
client.ts ← generated once from the OpenAPI spec, reviewed
refunds.ts ← small, tested helpers the agent wrote and kept
<!-- SKILL.md -->
# Stripe
Use for refunds, invoices and customer lookups.
- Import from ./client.ts. Never construct raw HTTP calls.
- Auth: STRIPE_KEY is injected by the runtime.
Never print it or pass it to tools.
- Refunds over $500 need a human: call `requestApproval()` first.
Compared to a stack of MCP tool definitions:
- Discovery: the agent reads one short file, and only when Stripe is relevant. Nothing sits in context the rest of the time.
- Invocation: typed code, composed freely, intermediate results stay out of context.
- Reuse: it's in git. It gets reviewed like any other code. It gets better over time.
So yes: it's "MCP with extra steps." But the steps are steps the harness is already good at (reading files, writing code, running it), and the result is cheaper and more composable for that harness.
What it is not is a replacement for job number four.
Job 4: Authorize. This Is Where MCP Earns Its Keep
Look at that SKILL.md again: "STRIPE_KEY is injected by the runtime."
That one line is doing a lot of unearned work. In the harness-only world, the agent's process holds the real key. Every script it writes can call any endpoint that key allows. A prompt injection, a malicious README, a poisoned web page: suddenly that key is one creative tool call away from doing something you didn't intend.
This is the part of MCP that a stronger harness does not absorb, because it's not a capability problem. It's a trust problem. Making the agent smarter doesn't make it more trustworthy; arguably it makes the blast radius bigger.
What MCP gives you here:
- The model never holds the credential. The client attaches an OAuth token; the server holds the upstream API key. The model sees
refund_order({ id })and a result, nothing else. - Least privilege by construction. If the server doesn't expose
delete_customer, no amount of prompting produces it. - Audience-bound tokens. OAuth 2.1 with PKCE, resource indicators and no token passthrough. A token for one server is useless at another.
- One choke point for policy and audit. Rate limits, approvals, logs, "refunds over $500 need a human": in one deterministic place, not scattered across scripts an agent wrote at 3 AM.
- Enterprise control. With enterprise-managed authorization (ID-JAG, which Okta calls Cross App Access), a company's IdP decides which agent may reach which app for which employee, and disabling the employee cuts off their agents. Atlassian has already shipped this for its Rovo MCP server.
None of that is about how the model discovers tools. All of it is about how a company can let a model touch real systems and still sleep at night.
Code Mode's Dirty Secret: Where Do the Keys Live?
Code mode has a question nobody likes to answer in the demo: the code the model writes needs to call Stripe. Stripe needs a secret key. So where is the key?
The naive answer is "in the sandbox's environment variables." I believe that's the wrong answer. A sandbox isolates the process. It doesn't isolate the secret. If STRIPE_KEY is readable inside the box, then a prompt-injected agent doesn't need to escape anything. It just reads the variable, or prints it to a log that flows straight back into the model's context.
From what I've seen, the serious platforms have converged on one idea: the key never enters the box. It gets added on the way out. I see three shapes of it:
1. Placeholder swap at the egress proxy. Inside the sandbox, the "key" is a fake string. All network traffic is forced through a proxy outside the sandbox, which swaps the placeholder for the real secret, but only for the approved host:
inside the sandbox:
Authorization: Bearer __STRIPE_KEY__ ← placeholder, useless
the proxy, outside the sandbox:
to api.stripe.com? → swap in the real key → forward
to evil.com? → no swap, or blocked
Deno Sandbox works this way: code only sees a placeholder, and the real value appears only on requests to the host that secret is tied to. Vercel Sandbox injects headers at the network layer outside the VM, overwriting whatever the code tried to set. Infisical's open-source Agent Vault does the same as a proxy you run yourself.
2. Capabilities instead of keys. Cloudflare's Dynamic Workers can start the sandbox with no network access at all, then hand it an object, not a credential. The code calls cf.dns.create(...), and the parent Worker adds the credentials on the outbound request. The agent can't leak a secret it never held. It holds a door, not a key.
3. A gateway that holds everything. The agent never talks to Stripe at all. It calls a tool on a gateway, an MCP server or something like Executor (more on that below), and the gateway makes the real call with the credential it stores.
What I find telling is what all three have in common: a trusted boundary outside the agent's code that owns the credential. I believe that boundary is exactly the job MCP is growing into. In my view, code mode doesn't remove the need for it. It makes the need louder.
My strong opinion on this:
- I'd go as far as calling a raw production secret in a sandbox's environment a bug. Not a trade-off, a bug. If your code-mode setup works that way, I'd fix that before shipping anything.
- But I believe injection only stops theft, not misuse. As one write-up put it well: the sandbox isolates the agent, it doesn't isolate the secret. And even with a proxy, a tricked agent can still use the key through the proxy: refund the wrong order, delete the wrong branch. So you still need narrow scopes, short-lived tokens, host allow-lists, per-tool policies and approvals for anything destructive.
- Watch the logs. From what I've seen, a lot of real credential leaks aren't clever attacks. They're a
console.logwhose output gets piped back into the model's context, and from there into a transcript.
I think the industry is clearly feeling this. Whenever I look at what people are saying about agent credentials, I see the same complaint over and over: static API keys in config files, agents with far more access than they need. I also see vendors racing to ship credential vaults, proxies and managed credential APIs for agents. I don't think that's a niche concern anymore. I believe it's the next layer of the stack being built.
This is also what we're building toward with Farm.js. Today, Farm.js can already expose selected API routes as MCP tools, keeping each route's own validation and putting authorization in one place on the server. The direction I believe in, and the one we're working on, goes a step further: the MCP tools that connect to your services, and the code mode that composes them, should live directly in the harness. The harness gets one search and one sandbox. The connections to GitHub, Stripe or your own APIs stay behind a boundary that owns the credentials. Getting there is one of the goals of farmjs.dev/agents, and honestly, this post is partly me explaining why.
One Gateway Instead of Twenty Connections
There's a second, more mundane auth problem: sprawl.
Today, a typical setup looks like this. Two tools for GitHub, a couple for Jira, a couple for Slack, a Linear server, a Postgres server. Each one is configured separately, in each agent: once in Claude Code, again in Cursor, again in Codex. Each one has its own login or pasted API key. Each one dumps its tool list into the context.
Executor (open source, MIT, on GitHub) is a good example of the fix:
Before (per agent, repeated for each):
Claude Code ──┬── GitHub MCP (own login)
├── Jira MCP (own login)
├── Slack MCP (own login)
└── Stripe key (pasted in config)
After:
Claude Code ─┐
Cursor ──────┼── Executor ──┬── GitHub (OAuth, once)
Codex ───────┘ (one MCP) ├── Jira
├── Slack
└── Stripe (key in vault)
How it works:
- You connect each service once, to Executor: MCP servers, OpenAPI specs, GraphQL APIs, even several accounts of the same service (work GitHub and personal GitHub).
- Every agent connects to just Executor, and authenticates to it once.
- The agent doesn't get every tool in its context. It searches the catalog, looks up the one tool it needs, then calls it. Classic progressive disclosure, code-mode style, with the code running in an isolated runtime.
- Executor injects the credentials at call time, so the agent's code never sees the raw token. Secrets can live in an encrypted file, your system keychain or 1Password.
- Each tool gets a policy: always allowed, needs approval, or blocked.
So yes: you authenticate to Executor, Executor stores your tokens, and it makes the calls to your services on your behalf. That's an important detail to sit with: the gateway now holds the keys to everything. That's the right design. One well-guarded place is better than tokens scattered across five agent configs. But it means the gateway's own security, and where it runs, matter a lot. (Executor can run locally or self-hosted, so the keys don't have to leave your machine, or as a hosted service if you'd rather not run it.)
The Defense: Where MCP Is Simply the Right Answer
Before I go full "harness maximalist," let me steelman the other side properly, because it has real points:
1. Most agents aren't coding harnesses. The biggest AI surfaces in the world are chat apps, used by people who will never open a terminal. For them, MCP isn't a choice; it's the only way their assistant reaches their tools. "MCP only helps chat apps" sounds like a dismissal until you remember how big the chat apps are.
2. Things with no API. Blender, Unity, Figma's canvas, a browser session, a local database, a Kubernetes cluster. Plenty of what agents should reach doesn't have a clean OpenAPI spec to generate a client from. An MCP server is the adapter.
3. The M×N problem is real. If every harness writes its own Stripe integration, we're back to every-app-integrates-with-every-service. MCP moves that cost to the service owner, once. Skills and generated clients help inside one harness. They don't help the ecosystem.
4. Messy systems need a human-designed interface. Picture an ERP with 5,500 tables. "Point the agent at the database" is not a plan there. A curated tool surface (find_open_orders, not SELECT *) is domain knowledge, encoded once.
5. And OpenAPI isn't a silver bullet either. Let's be fair to MCP:
- many specs are stale, incomplete or auto-generated from code that lies about its own types
- OpenAPI describes endpoints, not intent. It tells you
POST /v1/refundsexists, not when an agent should use it or what it must check first - OpenAPI has security schemes, but no story for agent authorization: no consent, no audience binding, no enterprise policy
- 400 endpoints is not a tool surface; it's a phone book
So the honest picture is: OpenAPI is a great source of truth for describing an API. MCP is a better envelope for letting an agent use one safely. They're not rivals; they're layers.
To Be Clear: What MCP Is Still Solving Today
I don't want this post to read as "MCP doesn't do anything." It does a lot, and some of it is easy to forget precisely because it already works. Here's what MCP is solving right now, that a plain API or a harness-written client doesn't give you for free:
- One integration, every client. Write an MCP server once and it works in Claude, ChatGPT, Cursor, VS Code and every other host that speaks MCP. Before MCP, every assistant had its own plugin format. That alone was a huge unlock.
- A standard way for agents to log in. OAuth 2.1 with PKCE, discovery through well-known metadata, clients that can identify themselves without manual setup, tokens bound to one server. Before MCP, every agent integration invented its own auth story, usually "paste your API key here."
- Credentials that never reach the model. The client holds the token, the server holds the upstream key, the model only sees tool names and results. A harness with a key in its environment can't say the same.
- Intent, not just endpoints. A good MCP tool says when to use it and how risky it is. Tool annotations like
readOnlyHintanddestructiveHintlet a host decide what needs a human's approval. OpenAPI has no field for "this one deletes things, ask first." - More than tools. MCP also has resources (data the app can attach), prompts (reusable workflows a user can trigger), elicitation (the server can ask the user a question mid-task) and sampling (the server can ask the client's model for help). An OpenAPI spec has no concept of any of these.
- Local things, safely. A stdio MCP server can expose your local database, your editor or Blender to an agent without opening a network port or writing a custom bridge.
- An ecosystem that already exists. Thousands of servers are already written. "Just generate a client from the spec" assumes a good spec exists. Often the MCP server is the best-maintained interface a product has for agents.
- A place for the company to say no. Scopes, approvals, audit logs, enterprise-managed authorization. The governance story for agents is being built on MCP, not around it.
So when I say "the harness eats the interface," I mean one of MCP's jobs gets cheaper to do another way, for one kind of agent. Everything on this list still stands.
The Line Gets Thinner as the Harness Grows
So this isn't "MCP is useless." It's two simpler claims: MCP has some real drawbacks for capable agents, and the gap between what MCP gives you and what a good harness can do on its own keeps shrinking.
The drawbacks, plainly:
- Context cost. Every connected server puts its tool list in front of the model on every request, used or not.
- Indirection. Every intermediate result travels back through the model, even when it's only being passed to the next call.
- A second interface to maintain. Many MCP servers are thin wrappers over APIs that already have a spec, an SDK and a CLI.
- JSON tool calls aren't the model's strongest language. Code is.
And here's how the line moves as the harness gets stronger:
| Harness strength | What MCP is doing for you |
|---|---|
| Chat assistant, no code | Everything: discovery, invocation, auth, safety |
| Tool-calling agent | Discovery, invocation, auth, safety |
| Coding harness with a file system | Mostly auth and safety; discovery and invocation can move into code |
| Coding harness with skills, code mode and a sandbox | Auth, policy, audit: the boundary, not the menu |
Each step down the table, MCP does less of the interface work and more of the trust work. The line gets thinner and thinner. It doesn't disappear, because the bottom row still needs someone to hold the keys, enforce scopes and log what happened, and that's exactly what MCP is good at.
Where This Is Going
Here's my bet, with my confidence attached so you can hold me to it:
1. Progressive disclosure becomes the default way agents consume tools. (High confidence.)
Stuffing every tool schema into the prompt will look as dated as loading every JavaScript file in the <head>. Search, load on demand, code against typed APIs. This is already happening inside MCP itself (code mode, tool search) and outside it (Skills). And it settles in the harness: one search and one sandbox across every server, not a separate search and execute bolted onto each one.
2. MCP's center of gravity moves from "tool calling" to "trust boundary". (High confidence.) The interesting MCP work will be auth, identity, policy, audit, enterprise management, sandboxed execution. The model-facing part gets thinner; the server-facing part gets thicker. MCP becomes less like a menu and more like a firewall with documentation.
3. OpenAPI and MCP converge into one contract. (Medium-high confidence.) Today people hand-write MCP servers that wrap APIs that already have specs. That's busywork. The endpoint definition should be the single source of truth, generating the OpenAPI spec, the MCP tools, the typed SDK and the agent skill from one place. Which is why I think this belongs in frameworks, not in hand-written wrappers.
4. Raw JSON tool calling declines for coding agents. (Medium confidence.) For agents that can run code, "the model emits a JSON blob and waits" becomes the fallback, not the main path. Chat agents will keep classic tool calls for a long time.
5. MCP does not die. (High confidence.) Chat surfaces, enterprises, no-API tools and the M×N problem keep it essential. Some people predict nobody will use it in ten years. I'd take the other side of that bet. Protocols that solve trust don't disappear; they become boring infrastructure. (Ask OAuth.)
The one-line version:
Harnesses eat the interface. MCP keeps the boundary.
What This Looks Like in a Framework
This is the part I care about as someone building one.
If the endpoint is the source of truth, the framework should be able to project it into every surface an agent (or a human) might need. In Farm.js, an API route is defined once with createEndpoint, and the same definition already feeds the generated OpenAPI reference and, when you opt in, an MCP tool:
// src/app/api/projects/route.ts
import { createEndpoint } from "@farm.js/core/api";
import { z } from "zod";
export const GET = createEndpoint(
{
method: "GET",
query: z.object({
limit: z.coerce.number().int().positive().default(20),
}),
mcp: {
name: "list_projects",
description: "List visible projects.",
},
},
listProjects,
);
One definition, and:
- the OpenAPI spec documents it, with a typed
limitquery param - the MCP endpoint exposes it as
list_projects, keeping the route's own validation, middleware and error handling - authorization is decided at the server, per request, not by whatever code an agent wrote
The next step, and the one I think every framework will take, is to project that same contract into harness-native form too: a typed client and a skill file the agent can load on demand. Same validation, same auth boundary, three doors in: humans read OpenAPI, chat agents use MCP, coding harnesses use code.
Write the contract once. Let the framework worry about which door the caller came in through.
Things I'd Build From Here
If you're looking for gaps (and I always am), this shift opens a few:
- OpenAPI → harness skill generator. Take a spec, emit a typed client plus a short
SKILL.mdwith intent, auth notes and gotchas. The boring 80% of "write it once and save it," automated. - An agent-grade spec linter. Score an OpenAPI spec on whether an agent can actually use it: missing descriptions, vague errors, undocumented pagination, side effects that aren't marked. Most specs would fail, which is the point.
An MCP auth gateway for small teams. I had this on my list, then found it already exists: Executor is pretty much that, open source. The gap that's still open is one level up: gateways like it plugging into a company's IdP, so IT decides which agent gets which tool for which employee (the ID-JAG flow), instead of each person connecting accounts by hand.- Code-mode by default. An MCP client that never puts tool schemas in context, only a
searchand anexecute, with a sandbox.
What the Wider Debate Says
Before wrapping up, I went through what others have written and measured on both sides. Here's my short version of it.
The critics are right about cost.
- In Scalekit's benchmark of GitHub's MCP server against the
ghCLI, MCP used 4 to 32 times more tokens, and 28% of its runs failed on timeouts. - Quandri found 77 tools across four servers eating about 10% of a 200k context window before any work started.
- Vercel replaced about 80% of an agent's tools with a single bash tool in a sandbox, and success went from 80% to 100% while it got more than three times faster.
- Mario Zechner and Armin Ronacher showed small scripts and generated code beating big tool lists. (To be fair, Mario's own benchmark found MCP and a CLI equally reliable once the tools were designed well. Tool design mattered more than the protocol.)
But the fix uses MCP; it doesn't replace it.
Anthropic, Cloudflare and Goose all landed on the same answer: let the model write code, and call MCP underneath it. Some of the loudest early critics moved the same way. Armin Ronacher now argues that MCP benefits greatly from code mode, and Simon Willison came back to MCP after the stateless spec, because a fixed set of tools is much easier to audit than a shell with internet access.
The defenders are right about trust.
- A malicious public GitHub issue got an agent to leak private repositories through the GitHub MCP server.
- A poisoned support ticket got an agent holding an over-privileged key to leak a Supabase table.
- Instructions hidden in tool descriptions worked about a third of the time across 45 real servers.
- Researchers from OpenAI, Anthropic and Google DeepMind beat 12 out of 12 published prompt-injection defenses.
I believe no filter will save us here. What helps is someone outside the agent's code holding the keys, limiting what each tool can do and keeping a record. That's exactly where MCP has been investing: OAuth 2.1, enterprise-managed authorization already supported by Okta, Atlassian, Linear and others, and enterprise allow-lists for which servers are allowed at all.
Even MCP's own maintainers agree on where the interface work belongs. At the MCP Dev Summit, lead maintainer David Soria Parra called context bloat a client problem rather than a protocol problem, fixed by clients loading tools on demand. In other words: the harness should handle it.
The line from all of this that stuck with me most came from a comment Simon Willison quoted: "Maybe the idealized form of MCP is just an auth gateway for the API." I'd say it slightly differently, a trust boundary rather than just an auth gateway, but I think that's where this is all heading.
Conclusion
The "MCP is dead" take is half right. For a strong harness, MCP as a tool menu is often the expensive way to do something the harness could do better in code. Anthropic and Cloudflare have, more or less, said the same thing with numbers attached.
But "dead" confuses the interface with the boundary. As agents get more capable, the question shifts from "can the agent call this API?" (yes, obviously, it can write the client in a minute) to "should it, as whom, with which permissions, and who can see what it did?"
That second question doesn't get easier as models get smarter. It gets harder.
That's the job MCP is growing into. The menu gets thinner. The boundary gets thicker.
And honestly? That's a much better job.
To be clear one last time: MCP is still solving real problems today, and plenty of agents need all of it. My point is narrower. For capable harnesses, the line between "use MCP" and "just write the code" keeps getting thinner, and what's left on MCP's side of that line is the trust boundary. That's not a protocol dying. That's a protocol finding its job.