MCP Minus the Hype: What Wiring Models to Tools Taught Me

I have written the same code more times than I care to admit. A model needs to read a calendar, so I wrap the calendar API. It needs to touch a database, so I wrap that too.

Each wrapper has its own auth, its own error shapes, its own quirks. Then the model changes or an API version bumps, and half of it stops working. It is the least interesting code in any AI project, and for a while it was most of the code.

That is the problem the Model Context Protocol was built to kill. It has been out for a bit over a year, and it has gone from an Anthropic side project to something OpenAI, Google, and Microsoft all support. I have shipped enough of it to have opinions.

So here is the honest version: what MCP actually is, how it works under the hood, the security reality nobody puts in the quickstart, and the times I still walk right past it.

The glue code I stopped writing

Strip away the branding and MCP is an answer to a counting problem. If you have a handful of models and a handful of tools you want them to use, the naive approach is one custom connector per pairing.

Five models and eight tools is forty little integrations, each one hand-built and each one yours to maintain forever. That is the M times N trap, and it is exactly the pile of wrappers I kept rewriting.

MCP turns that multiplication into addition. Every tool ships one server that describes itself in a standard way, and every AI application ships one client that speaks the same protocol. Now any client can talk to any server, and forty integrations become thirteen.

The model discovers what a tool can do at runtime instead of being pre-programmed with every endpoint. You stop writing glue and start plugging things in. That is the whole pitch, and it is a good one. The trick is not letting the pitch do your thinking for you.

A client, a server, and the handshake between them

Here is the mechanism, because it is simpler than the acronym makes it sound. There are two roles. The client is your AI application, something like Claude Desktop or an agent you built. The server is whatever sits in front of your tools and data.

When the client connects, it does not just fire off a request and hope. It asks the server what it can do, and the server answers with a structured list: here are my tools, here is what each one expects, here is what it returns. Discovery first, then execution. The messages are plain JSON-RPC, so there is nothing exotic to learn on the wire.

A server exposes three kinds of thing. Tools are actions the model can invoke, like sending an email or querying a table. Resources are read-only data the model can pull in, addressed by URI, like a file or an API response. Prompts are reusable templates the server offers up for common tasks.

Tools are the ones people mean when they say MCP, but the other two matter more than the tutorials suggest. The code is genuinely small. Here is a working server in Python using the official SDK:

Python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

That is it. The SDK reads your type hints and docstrings and generates the schema the model sees, so you never hand-write JSON Schema or parse a request yourself. A real server that calls an actual API with error handling lands closer to a hundred lines, but the shape stays this friendly.

One more piece worth knowing: how the client and server actually connect. Locally, it is stdio, where the client launches the server as a subprocess and they talk over standard in and out. Remotely, it is Streamable HTTP, which replaced the older server-sent-events transport and carries OAuth tokens so one server can serve many clients at once.

Same JSON-RPC either way. If you have wired up a CLI tool or a web endpoint before, none of this is new to you.

But we already have APIs

The obvious objection, the one I had myself, is that we solved remote calls decades ago. Why does a language model need its own protocol on top of the REST endpoint I already have?

The answer is about who the caller is. A traditional API assumes a program that already knows exactly what it wants: precise inputs, deterministic requests, no surprises. A language model is the opposite kind of caller. It reasons probabilistically, it works from fuzzy inputs, and it often has to look around and decide what to do next before it commits.

An API hands you a locked door and expects the right key on the first try. A model wants to read the sign on the door first.

So MCP does not replace your API. It sits above it. Under the hood your server can still call the same REST or GraphQL you already run. The model never sees that layer, it only sees the standard MCP description, and the discovery-then-act flow gives it room to reason.

The real payoff is not magic, it is portability. Write the server once and it works with Claude, with ChatGPT, with Cursor, with whatever ships next, because none of them needed you to write their glue. That is the part worth caring about.

The part the quickstarts skip

Now the section the cheerful explainer videos leave out entirely. The moment you let a model discover and call tools on its own, you have built a machine that reads instructions from places you do not fully control. That is not a bug you can patch. It is the shape of the thing.

Simon Willison has the cleanest framing for the core danger, which he calls the lethal trifecta. Give an agent access to private data, expose it to untrusted content, and give it a way to send data out, and you have handed an attacker everything they need.

Any one of those alone is fine. All three together is an exfiltration waiting to happen, and MCP makes it easy to wire all three together without noticing. This is not theoretical. A few of the incidents from the last year, all documented:

  • Tool poisoning. Researchers at Invariant Labs showed that a server can hide instructions inside a tool's description, invisible in the UI but read by the model as gospel. Their proof of concept got an editor to cough up a user's SSH key through a tool that looked like a calculator.

  • The GitHub server exploit. A malicious instruction planted in a public GitHub issue could hijack an agent using the official GitHub MCP server and drag private repository data into a public pull request. The root cause was architectural, one credential spanning public and private, which the researchers noted has no obvious fix.

  • The first malicious server in the wild. A package posing as a Postmark email integration behaved perfectly for fifteen releases, then quietly added a blind copy to an attacker's address, skimming every email that passed through it.

  • Anthropic's own reference server. Even the filesystem server from the people who invented MCP shipped a sandbox escape (CVE-2025-53109) that could reach arbitrary code execution. If the authors can leave a hole in the flagship example, your hand-rolled server deserves a hard look.

None of this means MCP is unsafe to use. It means the trust boundary moved, and you have to treat every server you did not write yourself the way you would treat any other untrusted dependency: scoped credentials, no blind token passthrough, and a very short list of servers you actually trust with real data.

The convenience is real. So is the attack surface.

When I don't reach for it

Even setting security aside, MCP is not free, and it is not always the right tool. There is a cost you pay before the model does a single useful thing: every tool definition eats context.

One connected GitHub server can spend around 17,600 tokens just describing itself. Wire up seven servers and you can burn a third of a 200,000-token window on schemas before the user has typed a word. That is context you are not getting back, and it makes the model slower and dumber for everything else.

Then there is the architecture. Remote MCP adds a network hop the model has to wait on, where a plain in-process function call would just return. For a single application with three tools that only it will ever use, standing up a server and a client and a transport is machinery you do not need. Native function calling is faster to build and has nothing extra to run.

My rule of thumb is simple. If a tool needs to be reachable from several different AI surfaces, an IDE, a desktop app, a bot, then MCP earns its keep by saving you from writing that integration three times. If it is one tool for one app, I skip the protocol and call the function directly.

MCP is a sharing standard. When there is nothing to share, it is just overhead wearing a nice acronym.

Where this is actually heading

For all my caveats, I am not betting against it. The trajectory is hard to argue with. Anthropic open-sourced MCP in November 2024. Within five months OpenAI adopted it, Google followed, and by mid-2025 Microsoft and GitHub had joined the steering committee.

In December 2025 Anthropic did the thing that actually signals a standard has won: it gave MCP away, handing governance to a new foundation under the Linux Foundation umbrella, co-founded with Block and OpenAI and backed by Google, Microsoft, AWS, and others. A protocol one company controls is a product. A protocol everyone shares is infrastructure.

The comparison people reach for is HTTP, and it mostly holds. Before HTTP, connecting systems meant a pile of incompatible schemes, and once the web agreed on one, the arguing stopped and the building started. MCP is doing the same thing for the layer between models and tools.

But standards win slowly and unevenly, the spec is still moving under everyone's feet, and "the industry agreed on it" has never once meant "it is finished." Ten thousand public servers is a real number and also a very young one.

So learn it, because the direction is clear and the glue code it kills is code you genuinely should not be writing by hand anymore. Just go in the way you would with any powerful tool you did not build yourself. Understand what it is actually doing, know where the sharp edges are, and keep your hand on the parts that touch your real data.

The whole appeal of owning what you build is that you know how it works all the way down. MCP does not change that. It just gives you one less pile of wrappers standing between you and the thing you actually meant to make.