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.
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 (
stderrfor 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-IdandMCP-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 inmcp-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.