Model Context Protocol is an open standard that lets an AI model call external tools and read external data through one consistent interface instead of a custom integration per model per service. It was created by Anthropic and has since been adopted by OpenAI, Google DeepMind, and Microsoft. According to Stacklok's 2026 software report, 41% of surveyed software organizations now run MCP servers in limited or broad production.

That adoption number is what made me actually sit down and understand the protocol rather than treating it as another AI acronym. This is how it works underneath.

The problem it solves

Before MCP, connecting a language model to your database meant writing an integration specific to that model's function-calling format. Connecting the same database to a second model meant writing it again. Ten tools and three models is thirty integrations, each with its own auth handling, schema format, and error semantics.

MCP inverts this. You write one server that exposes your database, and any MCP-capable client can talk to it. The client is whatever the user is running: Claude Code, Cursor, Windsurf, an IDE extension, a custom agent. The server is your thing. Neither side needs to know anything about the other beyond the protocol.

The ecosystem numbers reflect how quickly that caught on. As of December 2025 there were more than 10,000 active public MCP servers, with over 97 million monthly SDK downloads across the Python and TypeScript SDKs alone. Official SDKs now exist for TypeScript, Python, C#, Java, and Swift.

The three primitives

Almost everything in MCP reduces to three concepts, and understanding the difference between them is most of understanding the protocol.

Tools are functions the model can call. They have a name, a description, and a JSON Schema for their inputs. The model decides when to call them. A tool that queries your issue tracker, sends an email, or runs a database migration falls here. Tools are model-controlled, which means the model chooses to invoke them based on the conversation.

Resources are data the model can read. A file, a database record, a log stream. Resources are application-controlled: the client application decides what to expose and when, rather than the model reaching for them on its own. The distinction matters for security. A resource cannot have side effects, a tool can.

Prompts are reusable templates the user can invoke deliberately, usually surfaced as slash commands in whatever client they are using. These are user-controlled. Nothing happens until a person picks one.

Three different control models in one protocol is a deliberate design choice, not an accident. It means a server author can expose read-only data without granting the model permission to act, and expose actions without those actions firing automatically.

How a request actually flows

The wire format is JSON-RPC 2.0. A client connects to a server, the two exchange capability information during initialization, and from there the client can list and call whatever the server offers.

The description field is doing more work than it looks like. It is the only thing the model reads when deciding whether a tool is relevant. I have watched a perfectly functional MCP server go unused for an entire session because its tool description was vague enough that the model never connected it to what the user was asking for.

Transports, and why the 2026 spec update mattered

MCP originally leaned on stdio for local servers and Server-Sent Events for remote ones. SSE required the server to hold a long-lived connection per client, which works fine for a developer running one server on a laptop and badly for anything trying to serve many users behind a load balancer.

The 2026-07-28 specification revision addressed this. The protocol moved toward stateless, cacheable, routable request handling, which is the difference between something you can deploy on ordinary web infrastructure and something that needs sticky sessions and special handling. If you evaluated MCP for remote deployment before that revision and gave up on the operational complexity, that is the thing that changed.

A minimal server

The TypeScript SDK hides most of the JSON-RPC plumbing. A working server is genuinely short:

Notice the description is a full sentence explaining both what it does and what it returns. That is not decoration. Write these the way you would write a docstring for a colleague who has never seen your codebase.

Where it gets uncomfortable

Giving a model the ability to call arbitrary tools against your infrastructure is a real security surface, and the ecosystem is younger than the enthusiasm around it. A malicious or compromised MCP server sits in a position to read whatever the client sends it and return whatever it wants back into the model's context. Prompt injection through tool results is a live concern, not a theoretical one.

My working rule is to treat an MCP server the way I would treat any dependency with network access and credentials, which after the recent supply chain compromises in the npm ecosystem feels like a low bar rather than paranoia. Read the source of any server you did not write, scope its credentials to the minimum it needs, and do not connect a production database to something you found in a directory listing.

Is it worth learning?

If you are building anything that connects a model to your own systems, yes, and the reason is boring rather than exciting: it is the thing everyone else standardized on. The one-click setup in Cursor and Windsurf means users expect it. Writing a custom integration in 2026 mostly means doing extra work to be incompatible with the tools your users already have.

What I would not do is add an MCP server to a project because MCP is interesting. The protocol is a distribution mechanism. If you do not have something a model should be able to reach, it solves a problem you do not have.

Reference: the official MCP specification and docs.

Previous Post Next Post