Skip to content

Lesson 3: What Is MCP (Model Context Protocol)?

At the end of the last lesson we had working tool calling and a scaling problem. Tools were welded into one application. Every other application that wanted the same capability had to reimplement it.

The Model Context Protocol is the answer to that problem. This lesson explains what it is, what it deliberately is not, and — the part most introductions skip — when you should not use it.

The N×M problem

Say your company has ten systems worth exposing to an LLM: GitHub, Postgres, an internal metrics API, Google Drive, Slack, Jira, a feature-flag service, the data warehouse, a deploy tool, and a customer database.

Now say four applications want access: an internal chat assistant, an AI coding tool, a customer support agent, and a data analysis notebook.

Without a standard, that is forty integrations. Each application implements each system’s tools against its own interface. When the metrics API changes, four teams update four codebases. When a fifth application arrives, it writes ten integrations from scratch. Nothing is reusable because there is nothing to reuse against.

With a standard, it is fourteen: ten servers and four clients. Write the metrics server once and every current and future MCP-compatible application can use it. When the API changes, one codebase changes.

This is not a new insight — it is the Language Server Protocol argument. Before LSP, every editor implemented Python support, Go support, Rust support separately, and the quality varied wildly. After LSP, language teams wrote one server and every editor got it. MCP makes the same bet for AI applications.

What MCP actually is

MCP is an open protocol specifying how an application supplies context and capabilities to an LLM. Concretely, it is:

A message format. JSON-RPC 2.0 — a long-established, boring, well-understood standard. Requests, responses, notifications. Nothing novel.

A set of primitives. Standard message types for the things AI applications need: listing available tools, calling one, reading a resource, fetching a prompt template. Lesson 6 covers these in detail.

A lifecycle. How a client and server introduce themselves, negotiate protocol version, and declare what they support.

Transports. How the bytes move — over stdio for local processes, over HTTP for remote ones.

That is the whole protocol. It is deliberately small. The intelligence lives in the model and in your server implementation; MCP is the pipe between them.

What MCP is not

Three misconceptions worth clearing early, because each one leads people to build the wrong thing.

MCP is not a replacement for tool calling. This is the big one. Under the hood, an MCP server’s tools still reach the model as ordinary tool definitions, and the model still responds with ordinary tool-use requests. MCP does not change the mechanism from Lesson 2 at all. It standardizes where the tool definitions come from and how the call gets routed — a distribution and discovery layer, not a new inference feature.

MCP is not an agent framework. It has no opinion about loops, planning, memory, or orchestration. It does not run your agent. You still write the loop, or use an SDK that does. MCP only answers “what capabilities exist and how do I invoke one.”

MCP is not model-specific. It is an open standard, not an Anthropic product feature, and it has been adopted well beyond its origin. A server you write works with any compatible host.

When to use it — and when not to

Here is the judgment call most tutorials skip. MCP has real costs: a separate process to run, a protocol layer to debug, packaging and distribution to think about. Those costs are worth it sometimes and pure overhead other times.

Use MCP when

Multiple applications need the same capability. This is the core case. Two or more consumers and the reuse pays for the protocol immediately.

You want your service available inside tools you do not control. If you want your product usable from Claude Desktop or an IDE assistant, an MCP server is the way in. You cannot ship code into someone else’s application, but you can ship a server they connect to.

You want capabilities to be swappable at runtime. Users can add and remove servers without your application shipping a release.

You are consuming rather than building. Even if you never write a server, there are a great many good ones already — filesystem, Git, Postgres, browser automation. Wiring an existing server into your agent is often an afternoon versus a week.

Skip MCP when

One application, tools that will never leave it. If your agent needs three functions specific to your product and nothing else will ever call them, define them as ordinary tools. Adding MCP buys you a process boundary and a serialization layer in exchange for nothing.

The tool is trivial and hot. A tool that does one arithmetic operation does not need its own server and an IPC round trip.

You need the tool to run inside your application’s process. Tools that need direct access to your in-memory application state — the current request context, an open transaction, a live session object — are awkward across a process boundary. Keep them in-process.

A reasonable rule: build tools in-process first. Extract to an MCP server when a second consumer appears. The migration is not hard, and you will not have paid for a protocol you did not need.

The pieces, briefly

Lesson 4 covers architecture properly, but here is enough to make the next paragraph make sense.

A host is the application the user interacts with — Claude Desktop, an IDE, your own app. The host embeds one or more clients, each maintaining a connection to exactly one server. A server is a program exposing capabilities: your database wrapper, a filesystem bridge, an API adapter.

When the user asks something, the host collects the tool lists from all connected servers, hands them to the model as tool definitions, and — when the model asks for one — routes the call to the right server, gets the result, and feeds it back. Exactly the loop from Lesson 2, with a routing layer in the middle.

Why servers are usually separate processes

A newcomer’s reasonable question: why a whole process rather than a library?

Language independence. Your server can be Python while the host is TypeScript. Neither cares.

Isolation. A server that crashes, hangs, or leaks memory does not take the host down with it.

Security boundary. The server runs with its own permissions and its own credentials. Your database password lives in the server’s environment, not in the host application — and, importantly, not anywhere the model can see it.

Independent lifecycle. Update the server without updating the host, and vice versa.

That last point about credentials deserves emphasis, and we return to it in Lesson 12. Secrets belong in the server’s environment. The model gets results, never keys.

What this costs you

Being straight about the trade-off: MCP adds a serialization boundary and a process boundary. Debugging spans two processes, so a failure can be in your logic, the transport, the client, or the host. Latency goes up. Errors have to be marshalled across the boundary rather than surfacing as a stack trace.

These are manageable — Lesson 9 is entirely about making them manageable — but they are real. The reuse has to be worth it. Usually it is. Sometimes it isn’t, and recognizing which is which is the actual skill.

Next

You know what MCP is for and when it is worth using. Next we go one level deeper: the three roles, how they connect, what a message looks like on the wire, and the handshake that opens a session. That mental model is what makes Lesson 9’s debugging tractable — you cannot debug a system whose shape you cannot picture.

Next: Lesson 4: MCP Architecture — Hosts, Clients, and Servers

New lessons in this track publish weekly. Subscribe to get each one when it lands.