laranevans.com
Topics / AI / Context Engineering / Model Context Protocol

The Model Context Protocol (MCP) is an open standard that lets language-model applications connect to external data, tools, and workflows through a uniform interface. Anthropic introduced it in November 2024. Implementations now exist across multiple model providers, IDEs, and command-line agents.

The M×N generator: why a shared interface exists

One force generates the whole protocol. Without a shared interface, every host application (a chat client, an IDE plugin, a coding agent) needs custom integrations for every data source it wants to read from (Postgres, Google Drive, GitHub, Linear, a private file system). The number of integrations grows as the product of hosts and sources. With M hosts and N sources, you build and maintain M×N connectors. MCP collapses that product to a sum. Each host speaks the protocol once, each source ships one compliant server, and any host reaches any server: M+N.

flowchart LR
    subgraph product["Without MCP: M×N connectors"]
        H1[Host A] --- S1[Source 1]
        H1 --- S2[Source 2]
        H2[Host B] --- S1
        H2 --- S2
    end
    subgraph sum["With MCP: M+N"]
        HA[Host A] --- P((MCP))
        HB[Host B] --- P
        P --- SA[Server 1]
        P --- SB[Server 2]
    end

Everything else on this page follows from that move. Once a host and a server share one wire format, the design questions become: what does the wire carry (the four primitives), and how is a server shaped behind it (the server-pattern axes).

JSON-RPC over a decoupled transport

MCP runs over JSON-RPC 2.0. A client inside the host application opens a session with a server, the server advertises its capabilities during an initial handshake, and the two exchange requests and responses through that session for the rest of the conversation. The server declares which primitives it implements during the handshake, so the host knows what it has to work with for the session.

Transport is decoupled from message format. The reference implementations support two transports out of the box.

Transport How it runs Used for
stdio The host spawns the server as a subprocess and reads and writes JSON-RPC over stdin and stdout Local servers: a filesystem server reading the user's home directory, a SQLite server reading a local database
HTTP with Server-Sent Events The host opens an HTTP connection to a remote server, posts requests, and reads responses on a long-lived SSE stream Hosted servers, including official servers run by SaaS vendors

The four primitives

A server exposes capability through four named primitives. Each has a different purpose and a different direction of control, and a server typically implements a subset rather than all four. The table is the reference spine. The prose below it gives the reasoning each one carries.

Primitive Direction What it carries Defining trait
Resources Host pulls Read-only context the model reasons over Addressed by URI, no side effects
Tools Model invokes Functions the model calls through structured tool calls Side effects, no rollback semantics
Prompts User invokes Pre-built prompt templates the user triggers explicitly User-initiated, not model-initiated
Sampling Server pulls The server requests an LLM completion from the host Reverse channel, least implemented

Resources are read-only context the model treats as material to reason over. A file on disk, a row in a database, a snapshot of a Notion page. The server returns the resource's contents (text, JSON, or binary with a MIME type) and the host adds it to the model's context window. Resources have URIs. The host uses the URI as a stable handle, so the user or the model refers back to the same resource across turns without re-fetching every time.

Tools are functions the model invokes through structured tool calls. Each tool definition includes a name, a description, and a JSON Schema for its parameters. When the model decides to use a tool, the host receives the call, the server executes it, and the result flows back through the host into the model's next turn. See Tool Calling for the underlying mechanic. Tools have side effects. A "send email" tool sends email. A "create issue" tool creates an issue. The protocol carries no rollback semantics, so tool design has to account for accidental or adversarial invocation.

Prompts are pre-built prompt templates the server exposes for the user or the host application to invoke explicitly. A code-review server might expose a /review-diff prompt that loads the current diff, applies a review template, and submits it. Unlike resources, prompts are user-initiated rather than model-initiated.

Sampling is a reverse channel through which the server requests LLM completions from the host. It is useful when a server needs the model's help to produce a result, such as summarizing a long document before returning it as a resource. Sampling lets servers stay model-agnostic: the host owns the model relationship, the server borrows the inference. Sampling is the least-implemented primitive across the ecosystem. Many servers never need it, and hosts vary in whether they expose it at all.

Server-pattern axes

A server is more than the primitives it exposes. Servers cluster along three structural axes. The transport question above is already settled, so these three are what remain to decide. The choices ripple into security, observability, and what the agent experiences when using the server.

Axis The question it answers The choices
Responsibility What is the server built for Filesystem gateway, database adapter, API facade, retrieval connector, code-execution environment, orchestrator
Statefulness Where does state live Stateless tool wrapper, session-scoped state, persistent state
Deployment Where does the server run Local subprocess (stdio), hosted remote (HTTP with SSE)

Responsibility: what the server is built for

A loose taxonomy of what servers are built for. Reference and community servers across the ecosystem (modelcontextprotocol/servers) cover all of these shapes.

  • Filesystem gateways expose a bounded directory tree through tools like read_file, write_file, list_directory. Almost always scoped to one or a few root paths. The official filesystem server is the canonical example.
  • Database adapters are a thin query proxy that isolates the agent from raw credentials. Tools like list_tables, describe_table, execute_query. The official PostgreSQL server fits this pattern.
  • API facades wrap a SaaS API (GitHub, Slack, Linear, Sentry, Jira) behind a small set of intent-shaped tools. The server hides keys, pagination, rate limits, and OAuth from the model.
  • Retrieval connectors expose a search index, vector store, or document corpus with tools that encapsulate query and fetch. Cloud drives, knowledge bases, and code-search systems all land here. See RAG for the broader pattern this fits inside.
  • Code-execution environments expose a sandboxed runtime (Python, JavaScript, notebooks) with tools to run code, manipulate a virtual filesystem, and return results. Anthropic's Code execution with MCP post by Jones and Kelly (November 2025) argues this shape lets the agent "read tool definitions on-demand, rather than reading them all up-front," which keeps the model's context budget bounded.
  • Orchestrators are a meta-server that fans out to other MCP servers or external systems, exposing higher-level workflow tools ("file a bug and post to the channel"). Often built on frameworks like mcp-agent, which combines MCP with durable workflow execution.

The reference catalog skews toward single-responsibility servers. Multi-responsibility servers exist but trade clarity for surface area.

Statefulness: where state lives

  • Stateless tool wrappers treat each tool call as an independent RPC. State lives in the underlying system, not in the server. This is the default for pure API facades, query proxies, and similar adapters. Horizontal scaling is straightforward.
  • Session-scoped state holds transient state per MCP session (caches, handles, workflow progress). State clears on disconnect or restart. The pattern fits multi-step operations and long-running tasks. The MCP roadmap (as of mid-2026) names async-operations work that appears to assume this shape.
  • Persistent state maintains state across sessions and restarts, usually backed by a database. Agent memory, project context, and durable workflow state all land here. Persistent-state servers are the closest MCP analog to a traditional backend service.

Tool-design patterns cut across every axis

Tool design is where the most concrete guidance lives, and it applies regardless of which responsibility shape or statefulness the server lives in. Anthropic's Writing tools for AI agents post and the awslabs/mcp design guidelines both name these patterns.

  • Intent-centric tools group functionality by what the user is trying to accomplish, not by underlying API endpoint. A create_issue tool that accepts title, body, and labels in one call beats three separate tools for the same workflow. This avoids the "god tool" anti-pattern and the tool-proliferation anti-pattern at the same time.
  • Handle-passing for large results means tools that produce large payloads return identifiers (a document ID, a result handle) rather than the full payload. A separate tool dereferences the handle when the model needs the contents. The Anthropic code-execution post calls this out explicitly: keeping intermediate results in the execution environment, surfacing summaries and handles rather than full data, keeps context budget bounded.
  • Two-step retrieval pairs a search tool with a get tool. The model discovers candidates first, then fetches detail on a chosen subset. This reduces tokens compared to a single tool that returns everything matching a query.
  • Progressive disclosure has tools accept a detail_level parameter (summary, full, raw) so the agent trades verbosity against context cost based on need. Anthropic's code-execution post recommends this directly for a search_tools tool: "a detail level parameter... that allows the agent to select the level of detail required (such as name only, name and description, or the full definition with schemas)."
  • Schema-first design gives tool input and output schemas rich descriptions, tight type constraints, enums, and defaults. Tool definitions deserve as much attention as prompts. The model reads them on every turn the tool is available.
  • Safety-constrained tools keep destructive or irreversible operations absent, narrowly constrained, or gated through confirmation. A tool that takes raw shell commands without constraints is a vulnerability waiting to land. The awslabs design guidelines reinforce this: tool naming, validation layers, and security scanning belong in the build pipeline.

The patterns compose. A production retrieval connector usually combines intent-centric tools, two-step retrieval, handle-passing, and progressive disclosure. The combination is often what separates a usable MCP server from a noisy one.

Security considerations

MCP servers run code on behalf of the user, and tool definitions land in the model's context window. Both surfaces matter. A few patterns are worth carrying.

  • Consent boundaries belong at the host. The host application controls which servers connect, which tools the model invokes without confirmation, and which require an explicit prompt before execution. The protocol itself does not encode policy, so the host is the enforcement point.
  • Tool descriptions are model input. A tool whose description contradicts its behavior misleads the model. A tool whose description contains hidden instructions becomes a prompt-injection vector. See Prompt Injection for the broader pattern.
  • Server identity matters. A malicious server returns plausible-looking resource content that contains adversarial instructions. The host has no built-in way to distinguish a trustworthy server from a hostile one beyond what the user configured. Treat third-party servers the way you treat third-party browser extensions.
  • Tool arguments are attacker-controllable through the model. If the model accepts user input and passes it to a tool, prompt injection or accidental confusion turns those arguments into a security vector. Validate at the server, not only at the host.

The ecosystem

A reference catalog of MCP servers lives at modelcontextprotocol.io/servers and at github.com/modelcontextprotocol/servers. The early catalog covers local filesystems, SQLite, Postgres, Google Drive, Slack, GitHub, Puppeteer, and similar building blocks. SaaS vendors increasingly publish first-party servers for their own products.

On the host side, Claude Desktop, Cursor, the Claude Code CLI, and several other agent harnesses ship MCP client support. The list grows. The protocol is open, and any host able to speak JSON-RPC implements it.

When MCP is the right answer

MCP returns the most value when the M×N pressure is real. It fits well when:

  • A host needs access to many data sources or tools, and per-source custom code would multiply.
  • The data source or tool is shared across multiple hosts (an internal documentation server reachable from both an IDE plugin and a chat application).
  • Long-running connections matter (a database server that holds connection state across many requests).

MCP fits less well when the product collapses back toward one host and one source:

  • A single host needs a single integration and a hard-coded client call costs less than running a separate server process.
  • The interaction is purely one-shot retrieval that a direct HTTP call from inside the host handles with no per-request session overhead.
  • The model never needs to invoke the capability directly, in which case a plain library call inside the host code path stays simpler.

The protocol is one option in the design space, not a default. Hosts that already have first-class integration with a data source rarely benefit from re-routing through MCP.