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.
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.
2 · The old way
Every assistant, wired by hand to every tool.
3 · The move
Agree on the shape of the socket. Now it’s 3 + 4.
4 · The errand, step by step
The model asks. The app does. The server answers.
5 · The seam
The protocol describes tools. It does not judge them.
6 · Keep this card
The whole idea on one index card.
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:
- You don’t have to pick a vendor’s walled garden. If your team standardizes on MCP servers for your internal tools, any client that speaks the protocol can use them — including clients you haven’t chosen yet. The integration outlives the assistant.
- You can ship a tool once and benefit everywhere. There’s a public registry of reference and community servers — filesystem, Git, fetch, and a long tail of SaaS tools — and hosts generally expose connecting one as configuration rather than as a code change, though how much setup that really is depends on the host and the server’s auth. (Which servers exist, and how good they are, moves month to month; check the registry rather than trusting a list in a blog post.)
- The “agent” and the “tools” become separable concerns. Model research moves fast, but the integration to your bug tracker doesn’t need to move at that pace. MCP draws a clean seam between them.
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:
- Host. The AI application the user actually interacts with — Claude Desktop, Claude Code, an IDE plugin, your internal chatbot. It embeds one or more MCP clients.
- Client. A connection-handler inside the host. Each client maintains one connection to one server.
- Server. A separate process exposing one capability — a Git repo, a Postgres database, a filesystem directory, the Jira API. The protocol surface a server has to implement is small; how much work one is depends almost entirely on the auth and API you’re wrapping, not on MCP.
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.
Famous related terms
- Agent harness —
agent = model + harness. MCP is one well-defined way for a harness to acquire its tool set. - Tool use / function calling —
tool use = model emits structured calls + host executes them— the model-side half. MCP is the host-side half: where those tools come from and how the host reaches them. - LSP (Language Server Protocol) —
LSP = JSON-RPC + editor/language-server split. The clear ancestor of MCP’s design, and the same M×N → M+N move made a decade earlier for editors and language tooling — in a domain where it has already finished playing out. - JSON-RPC 2.0 —
JSON-RPC = remote procedure calls + JSON wire format— the underlying message format. Plain JSON requests and responses withmethod,params,id. Nothing exotic. - Tool registry / plugin system —
tool registry ≈ vendor-specific MCP— older, vendor-specific takes on the same problem (ChatGPT Plugins, custom function-calling registries). MCP is the open, cross-vendor version.
Going deeper
- The Model Context Protocol specification —
the primary source, and the only place to settle “what exactly does
tools/callsend” or “which remote transport is current.” - The reference MCP servers (filesystem, Git, fetch, …) — the explainer that isn’t prose: read one end-to-end to see what “a server” concretely is, and how little of it is protocol code.
- The spec release notes — the rabbit hole, and the fastest way to see which parts of the protocol are still moving; the July 2026 post is where the stateful session model was dropped.
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.