Heads up: posts on this site are drafted by Claude and fact-checked by Codex. Both can still get things wrong — read with care and verify anything load-bearing before relying on it.
why → how

Why does MCP exist?

Every AI app was reinventing the same plumbing to talk to the same tools. MCP is the standard that turns an M×N integration mess into M+N.

AI & ML intro Apr 29, 2026 · updated Aug 25, 2026 · 12 min read

On this page

The picture version

Six pictures for a reader who has never heard of MCP. The prose below fills in the seams the pictures skip.

1 · The problem

It can describe the errand perfectly. It cannot run it.

“read pull request #482 and file a ticket about it” Sure — here’s how you can do that: 1. open the pull request… 2. copy the description… 3. open your tracker and… a flawless plan. it did none of it. no hands the code host the issue tracker wants to reach — can’t
A model is a text box: it produces words, not network requests. Everything it “does” in the world is done by the program around it — and somebody has to write that program for every tool.

2 · The old way

Every assistant, wired by hand to every tool.

assistant A assistant B assistant C your files the code host the database the tracker 3 assistants × 4 tools = 12 separate pieces of glue each one written, maintained and debugged by someone, doing nearly the same thing
Every pairing needs its own small, boring integration: credentials, an HTTP client, a function the model is allowed to call. Add one tool and you owe an integration to every assistant — and none of it is the interesting part.

3 · The move

Agree on the shape of the socket. Now it’s 3 + 4.

assistant A assistant B assistant C a files server a code-host server a database server a tracker server ONE AGREED SHAPE 3 + 4 = 7 pieces, each written once write the tracker side once and every assistant that speaks the protocol can use it — including ones that don’t exist yet
Nothing clever happens here. The saving comes entirely from everyone agreeing to speak the same boring format — the same move a shared plug shape made for appliances, or a standard protocol made for editors and language tooling.

4 · The errand, step by step

The model asks. The app does. The server answers.

THE MODEL emits text, including requests to call a tool THE APP holds the credentials, makes the real calls, pastes results back in code-host server get_pull_request tracker server create_issue “call get_pull_request” here’s what came back one shared format same format The model never speaks the protocol. It names a tool; the app does the talking. and before any of this, the app asked each server for its list of tools — getting back names, descriptions, and exactly which arguments each one takes
The tool list arrives at runtime, so the app can hand the model tools it was never built to know about. That runtime discovery is what turns a client/server split into an ecosystem — the ticket server you write today works in an assistant released next year.

5 · The seam

The protocol describes tools. It does not judge them.

a server you installed read_file list_directory delete_everything … it can advertise anything relayed faithfully the protocol carries the description, makes no judgement the app’s permission layer asks you first, logs it, refuses it — or doesn’t The only thing that can say no lives outside the protocol. worth remembering before installing a server you found online — especially one that will read text written by strangers
MCP is an interoperability standard, not a sandbox. A standard socket doesn’t make every appliance safe; whether a tool gets called, with what confirmation and what audit trail, is entirely the host application’s business.

6 · Keep this card

The whole idea on one index card.

MCP = one agreed message format + a split: the app runs it, the tool lives apart + the tool list discovered at runtime — discovery is the part that turns M×N into M+N
Picture to keep: a wall socket. Your assistant is the appliance, each tool is a device with a plug, and MCP is the agreed shape of the socket — except that this socket tells the appliance, on connection, what it can do.

Why it exists

You ask your AI assistant to “read pull request #482 and file a Jira ticket summarizing it.” It writes back a flawless plan for how you could do that. It cannot do it. The model is a text box; GitHub and Jira are somewhere else entirely. So somebody on your team spends a day writing glue — auth, an HTTP client, a function the model is allowed to call. Then the team next door picks a different assistant and writes that same Jira glue again, slightly differently. That PR-to-ticket errand is the example I’ll carry through this post.

By late 2024 this was a familiar wall for teams building AI assistants. Their agent could write code, summarize text, reason about a problem — but the moment it needed to do something in the real world (read a file, query a database, search a wiki, open a Jira ticket) someone had to write glue.

This is the classic M×N integration problem. With M AI applications (Claude Desktop, Cursor, your internal copilot, the chatbot in your IDE) and N things they want to talk to (filesystem, Git, Postgres, Slack, Google Drive, your company’s wiki), the naive world needs M × N bespoke integrations. Each one is a small, boring, slightly different piece of plumbing. None of it is the interesting part of building an agent, and all of it has to exist before the agent is useful.

The Model Context Protocol (MCP), introduced and open-sourced by Anthropic in November 2024, exists to collapse that M × N into M + N. Write one MCP server for Postgres, and any MCP-aware client can speak to it without new integration code. Write one MCP-aware client and it can reach the servers other people have already shipped. Not literally for free — you still own auth, credentials, and whether a given client supports the features your server uses — but the per-pair integration work goes away, and that was the part nobody wanted to do five times.

If that pattern sounds familiar, it should: it’s the same move LSP made for editors and language tooling in 2016. Before LSP, every editor had to ship a custom integration for every language. After LSP, an editor that spoke the protocol could pick up any language server that spoke it, without a per-language build. MCP is LSP’s idea applied to AI assistants and the world of tools they want to reach — except that LSP’s core vocabulary is fixed and small (go-to-definition, hover, rename), while an MCP server can advertise any tool it likes at runtime. That open-endedness is MCP’s whole point and also where its security problems live.

Why it matters now

Agents are only as useful as the surface area they can touch. A model that can reason brilliantly about your codebase but can’t read your codebase is a parlor trick. So a large part of building one of these products isn’t “is the model smart enough?” — it’s “what can the agent actually reach, and how painful was it to wire up?”

MCP changes the answer to that second question. A few things follow:

For software engineers specifically, this means the question shifts from “how do I bolt my agent onto Jira?” to “is there an MCP server for Jira, and if not, how do I write a small one?” That’s a much smaller question.

The short answer

MCP = JSON-RPC + a small vocabulary (tools, resources, prompts) + a client/server split

Picture to keep: a wall socket. Your assistant is the appliance, each tool is a device with a plug, and MCP is the agreed shape of the socket — so nobody has to hand-wire the Jira lamp into every house. Except that a real socket only ever delivers one thing, voltage, while an MCP server tells the host at connection time what it can do. That runtime discovery is the actual power of the protocol, and it’s also why plugging in an untrusted server is nothing like plugging in a lamp.

MCP is an open protocol, built on top of JSON-RPC 2.0, that lets an AI application (the client / host) talk to external capabilities (each one exposed by an MCP server) through a tiny shared vocabulary. Servers offer three kinds of things: tools the model can call, resources it can read, and prompts the user can invoke. Any client that speaks MCP can use any server that speaks MCP. That’s the whole idea.

How it works

Try to give the model Jira access the obvious ways, and watch each one fail.

Naive attempt: let the model call the Jira API. Put the API token in the system prompt and ask it to make the request. This doesn’t fail subtly — it fails immediately. A language model emits text; it has no network stack, no sockets, no way to execute anything. And a secret pasted into the context is now sitting in text the model will happily repeat if something in the conversation talks it into doing so.

Fix: the host executes, the model only asks. The model emits a structured request (“call create_issue with these arguments”), and the application around it — the host — actually makes the HTTP call, holds the credentials, and pastes the result back into the context. That’s plain tool use, and it’s the same loop covered in What is an agent harness?. Why it breaks: every host now hardcodes every tool. Claude Desktop writes Jira glue, Cursor writes Jira glue, your internal copilot writes Jira glue — that’s the M × N mess from the top of the post, with the glue baked into the assistant instead of shared.

Fix: make the tool list something the host discovers at runtime. Move the Jira code out of the host and into a separate process — an MCP server — that the host connects to over a standard protocol. Three roles, worth keeping straight:

A client asks its server what do you offer? — tools/list for the tools, resources/list and prompts/list for the rest. (There’s also server/discover, which answers a different question: who the server is, what protocol versions it speaks, and which capabilities it supports — not the catalogue itself.)

But “here’s a list of tools” isn’t enough for the model to call one. It needs to know each tool’s name, what it does, and exactly which arguments it takes — otherwise it guesses, and guessed arguments fail at the API. So tools/list returns a JSON schema for every tool’s parameters, which the host hands to the model alongside the prompt. Servers also expose resources (readable things addressed by a URI — a file, a row, a doc) and prompts (named templates the user can trigger, like a slash-command).

Now the errand runs:

user: "summarize the latest PR description and file an issue"

host →  asks model what to do, sends along the list of available tools
model → tool_call: github.get_pull_request(repo=…, number=…)
host →  forwards to the GitHub MCP server, gets back PR data
model → tool_call: jira.create_issue(title=…, body=…)
host →  forwards to the Jira MCP server, gets back the new issue URL
model → "Filed JIRA-1421 with a summary of #482."

Note what never happens: the model never speaks MCP. It emits tool calls; the host translates them into MCP requests, routes them to the right server, and feeds results back into the context.

But a filesystem server and a hosted SaaS server don’t want the same pipe. One is best run as a local subprocess with no network exposure at all; the other lives on someone else’s machine. So the protocol is transport-agnostic. In practice the local case is stdio: the host launches the server as a subprocess and talks over its standard input and output. The remote case is an HTTP-based transport, and this is the part of the spec that has been revised most since launch — the specification is versioned by date for exactly that reason, so check it rather than trusting a blog post (including this one) for the current remote transport. The JSON-RPC payload is the same either way; only the pipe changes.

But not every server and client support the same features. Does this server push notifications when a resource changes? Does this client support the things a server might need to ask it for mid-request? Assuming yes and being wrong means a failed or hung request. The protocol’s answer to that has itself changed: early versions opened every connection with an initialize/initialized handshake, and the connection then carried that negotiated state. The 2026-07-28 revision retired that. MCP is now a stateless protocol — every request carries its own protocol version and client capabilities in a _meta field, so any request can land on any server instance without a prior session, and a server must not infer anything from earlier requests on the same connection. Asking a server who it is and what it supports became optional rather than mandatory: server/discover is there if a client wants that picture up front.

And here’s the seam: none of this makes anything safe. A server can advertise a delete_everything tool and MCP will faithfully describe it to your model. Whether the model is allowed to call it, whether the user gets a confirmation prompt, whether the call is logged — all of that lives in the host’s permission layer, not in the protocol. MCP is an interoperability standard, not a sandbox. Worth internalizing before you install a random MCP server you found online, especially one that will be reading text written by strangers (see prompt injection).

Read the spec and the protocol turns out to be a small set of JSON-RPC methods — tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get, server/discover, plus notifications. The power isn’t cleverness; it’s everyone agreeing to speak the same boring thing. (The exact roster moves between spec revisions: initialize was central until the 2026-07-28 release removed it, and roots, sampling and logging are deprecated there, still working but on the way out. Read the dated spec, not a summary.)

You started with MCP = JSON-RPC + a small vocabulary + a client/server split. What did the walk-through add? — + schemas, so the model can call a tool it has never seen, and + discovery at runtime, which is the actual M×N → M+N move. The split alone would just be a library; discovery is what makes the Jira server you wrote work in a client that didn’t exist yet.

Going deeper

What’s stable and what isn’t: MCP was introduced and open-sourced by Anthropic in November 2024 and the protocol is built on JSON-RPC 2.0 — both documented in the specification. The wire details are a moving target: the spec is versioned by date and has already changed its session model, its remote transport and parts of its feature set. And the set of clients and servers shipping in production changes month to month, so treat any specific roster (“X supports MCP”) as a thing to check against current docs rather than a snapshot to memorize.