The Model Context Protocol (MCP) is an open standard that connects language models to tools and data sources in a uniform way. Announced by Anthropic on November 25, 2024 and often described as "a USB-C port for AI," it turns a passive model — one that can only produce text — into an agent that can act: read a file, query a database, call an API. Here is how it works, and how you build or consume an MCP server.

The problem: the M×N explosion

Before MCP, every LLM ⇄ tool integration was recoded per application: combinatorial and brittle. Connecting M AI apps to N data sources required up to M × N bespoke connectors.

MCP flips this: each host speaks MCP once, each service exposes an MCP server once. You go from M × N to M + N, and integrations become reusable and interoperable across hosts (Claude, IDEs, third-party agents) — exactly what LSP did for code editors.

Architecture: host ↔ client ↔ server

MCP follows a client-host-server architecture, built on JSON-RPC 2.0 with a stateful session.

MCP architecture: a host drives clients that talk 1 with servers exposing tools, resources, and prompts. Diagram: MCP architecture (host ⇄ client ⇄ server).

  • Host: the AI application (Claude Desktop, IDE extension, agent runtime). It manages the LLM context, creates clients, enforces security and consent, and coordinates sampling.
  • Client: created by the host, in a 1
    relationship with a server
    ; negotiates capabilities and routes messages.
  • Server: exposes focused capabilities via three primitives. It can be local (subprocess) or remote (HTTP service).

A core design principle: a server never sees the whole conversation, nor other servers — isolation is enforced by the host. At initialize, client and server declare what they support (capability negotiation).

The three server primitives

Each primitive has a different "control axis":

Primitive Controlled by Examples
Tools the model (it decides when to call) read a file, run a SQL query, create a ticket
Resources the application (host/user picks the context) documents, schemas, configuration
Prompts the user (often via slash commands) reusable templates

Tools are discovered via tools/list and invoked via tools/call; their input is described in JSON Schema. Resources are identified by URI (file://, https://, git://…), read via resources/read, with optional subscription. Prompts are fetched via prompts/get with arguments.

On the client side, complementary primitives make servers "well-behaved": sampling (a server can request an LLM completion via the client, staying model-independent), roots (the client declares access boundaries), and elicitation (ask the user for information mid-run).

A typical exchange

The client lists tools, the model picks one, the client calls it:

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }

The server returns each tool's schema; the client exposes them to the LLM, which produces a structured call the server executes, and the result is fed back into context. Good implementations keep a human in the loop: show exposed tools and confirm before sensitive actions.

Transports: stdio (local) and Streamable HTTP (remote)

MCP defines two standard transports (custom ones are allowed):

  • stdio: the client launches the server as a subprocess and exchanges newline-delimited JSON-RPC over stdin/stdout (stderr for logs). Ideal for local tools.
  • Streamable HTTP (2025-03-26 revision, replacing the older HTTP+SSE): a single endpoint accepting POST and GET; the server replies with plain JSON or opens an SSE stream. Headers Mcp-Session-Id and MCP-Protocol-Version. Suited to remote use (multi-client, identity, audit).

Transport security: validate the Origin header (anti DNS-rebinding), bind to 127.0.0.1 locally, and authenticate every connection.

Building a server (conceptual)

Official SDKs exist for Python, TypeScript, C#, Java, Go, Rust, and more. In Python, with FastMCP, exposing a tool takes a few lines — the docstring becomes the description, type hints generate the input schema:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_forecast(city: str) -> str:
    """Return today's weather for a city."""
    return f"Sunny in {city}, 24°C."

if __name__ == "__main__":
    mcp.run(transport="stdio")

You then declare the launch command in the host's configuration; the host starts the subprocess and discovers capabilities at initialize.

Security: a new threat model

Giving a model tools opens a novel attack surface. Key risks:

  • Tool poisoning / prompt injection: a malicious server can hide instructions in a tool's description to hijack the model. Clients must treat tool annotations as untrusted outside trusted servers (an Invariant Labs demo exfiltrated WhatsApp history this way).
  • Token passthrough: a server must not accept tokens not explicitly issued for it (it bypasses controls and breaks audit).
  • Confused deputy and session hijacking: require per-client consent before any third-party OAuth flow, use non-guessable session IDs, and don't use the session to authenticate.
  • Local server compromise: a stdio server runs on your machine — hence explicit consent before launch and sandboxing. CVE-2025-6514 (RCE in mcp-remote) is a reminder of supply-chain risk.

The throughline: user consent and control at every step.

Ecosystem and adoption in 2026

MCP has become a de facto standard. OpenAI adopted it (March 2025, Agents SDK and ChatGPT desktop), followed by Google DeepMind (Gemini) and Microsoft (Copilot, VS Code). Client support is first-class in Claude, ChatGPT, Cursor, VS Code, and JetBrains. The spec evolved from 2024-11-05 to 2025-11-25 (async operations, server identity, an official registry). In December 2025, Anthropic donated MCP to the Agentic AI Foundation under the Linux Foundation — a mark of open governance.

MCP does for AI tools what HTTP did for the web: one protocol, many implementations. For a developer, exposing a capability means writing a small server; consuming one means pointing a compatible host at it — with security now a first-order responsibility.