What Is the Model Context Protocol?

A model is only as useful as the context and tools it can reach, and MCP is the open standard that lets any AI app plug into any tool through one interface instead of a hundred bespoke integrations.

Large language models are strong reasoners trapped behind a narrow doorway: the text you hand them. To be useful on real work, a model needs to reach outside that doorway — into your files, your database, your issue tracker, your monitoring, your internal services. For the last few years, every team wired those connections up by hand, once per app and once per tool. The Model Context Protocol (MCP) exists to end that busywork by making the connection a standard.

This post is the opening of an eight-part series that builds MCP up from first principles. Here we cover what problem it solves, the pieces it defines, and how it differs from the tool-calling you may already be doing.

The M×N integration problem

Say you have M AI applications — a chat assistant, an IDE agent, a customer-support bot — and N systems you want them to use — Postgres, GitHub, Google Drive, a payments API. If each app integrates each system directly, you are on the hook for M × N integrations. Every new tool means touching every app; every new app means re-implementing every tool. The glue code is duplicated, subtly inconsistent, and never quite finished.

MCP reframes this as M + N. Each application speaks MCP as a client. Each system is wrapped once as an MCP server. Any client can now talk to any server, because they share a protocol. Write a GitHub server once and every MCP-capable app can use it; make your app MCP-capable once and it can use every server anyone has written. The integration surface collapses from a grid to two lists.

That is the whole pitch, and it is the same pitch that made the Language Server Protocol reshape editor tooling: standardize the seam, and an ecosystem grows on both sides of it.

The mental model: hosts, clients, servers

MCP has exactly three roles, and keeping them straight makes everything else easier.

The user talks to the host. The host, through its clients, talks to servers. Servers do the actual reaching-out to files, databases, and APIs. Nothing about MCP requires a particular model vendor or programming language; it is a wire contract, not a library.

What a server exposes

A server offers three kinds of capability, and the difference between them is who is in control. We spend a full post on each later, but here is the shape:

Tools are model-controlled, resources are application-controlled, prompts are user-controlled. That three-way split is the cleanest way to remember what belongs where.

MCP versus plain tool calling

If you have used an LLM’s function-calling feature, you already hand the model a list of tools and let it emit structured calls. So what does MCP add?

Function calling defines how one app describes tools to one model in one request. It says nothing about where those tools come from, how they are discovered, or how they are reused across applications. You still write the tool implementations into your app, redeploy to change them, and re-implement the same tool in the next app.

MCP standardizes the layer underneath: discovery (tools/list), invocation (tools/call), transport, and lifecycle. A server publishes its tools at runtime; the client discovers them and adapts them to whatever tool-calling format its model expects. The result is that tools become shareable, swappable dependencies rather than code baked into each app. Function calling is how the model asks to use a tool; MCP is how the tool got there and how any app can offer the same one.

Here is the flavor of a discovery exchange — a client asking a server what it can do, in the JSON-RPC style MCP uses:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "create_issue",
        "description": "Open a new issue in a repository.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "repo": { "type": "string" },
            "title": { "type": "string" },
            "body": { "type": "string" }
          },
          "required": ["repo", "title"]
        }
      }
    ]
  }
}

The client takes that schema, reshapes it into the format its model expects, and now the model can request a create_issue call — without anyone hardcoding the tool into the app.

When MCP is worth it — and when it is not

MCP shines when the same capability should be reachable from more than one application, when you want third parties to extend your app without a code change, or when you want to swap a data source without touching model code. A shared “company knowledge” server that your IDE agent, your support bot, and your internal chat all consume is exactly the case MCP was built for.

It is overkill when you have a single app with a couple of tightly-coupled, in-process functions that will never be reused elsewhere. Wrapping those in a protocol adds a process boundary and a serialization step for no gain. Reach for MCP when the reuse or the extensibility is real, not reflexively.

What the rest of the series covers

With the mental model in place, the next posts go deep: the wire protocol and connection lifecycle (JSON-RPC and the initialize handshake), the two transports (stdio and streamable HTTP), then a post each on tools, and on resources and prompts. After that we build a real server, build a client that drives it from a model, and finish with security and production concerns. By the end you will be able to write MCP servers and clients from scratch and reason about them soundly.

Key takeaways

Further reading

Sources & References

Official MCP docs