MCP Garden

The Model Context Protocol — the N×M problem, the architecture, the primitives, and the 2026 stateless spec.

The whiteboard needs a wider screen — here are the notes in order.

note

What MCP is

MCP is a standardised way to connect a model to external data sources and tools. Open-sourced by Anthropic, and now the closest thing the ecosystem has to a common port.

Before MCPa hand-written adapter per system, inside your app
With MCPone protocol, every system behind a server
Rendering diagram…

Before, every integration was a bespoke adapter living inside the app. After, the app speaks one protocol and every integration is a server behind it — swappable, reusable, written once by whoever owns the system.

The one-liner worth remembering: MCP is tool discovery for LLMs. It is not a new way to call a function; it is a standard way to find out what functions exist and what they need.

#mcp#anthropic
schema

The N×M problem

The problem underneath is simple. A model's knowledge is a fixed store, and it is isolated from live systems — so every integration needs a bespoke bridge.

Fixed knowledgethe training set, frozen
Isolatedno view of current data or running systems
Bespoke bridgesone custom adapter per integration

N × M

N models, M tools, every pair speaking its own protocol. That is chaos with two costs: wasted build effort and, worse, wasted maintenance effort — M integrations that each break on their own schedule.

One protocol in the middle collapses it to N + M:

Rendering diagram…
#mcp#integration
schema

Host, client, server

Three roles, and they are easy to blur. The host is the application. It holds one client per connection. Each client talks to one server.

one client per server
Host · IDE, chat client, agentthe part you writestdioStreamable HTTPSQLHTTPSMCP client 1one connectionMCP client 2one connectionMCP serverfiles · localMCP serverissues · remoteDatabaselocal or remoteWeb APIsome remote serviceBackend4Database1Cloud1
RoleWho writes itWhat it owns
Hostthe app vendorthe model, the UI, the approval prompts
Clientthe SDKone connection, protocol bookkeeping
Serveryoucapabilities and the external system behind them

The server is the only piece most people write. It exposes capabilities and does the talking to whatever sits behind it — a local database, a web API, a filesystem.

#architecture#roles
note

Transports: stdio and HTTP

Two transports in practice, and the choice is really "same machine or not".

TransportUsed forShape
stdiolocal serverschild process, stdin/stdout
Streamable HTTPremote serversplain HTTP, stateless
HTTP+SSE (legacy)nothing newdeprecated, on a 12-month offramp
the same server, two ways to reach it
Host
spawn process
stdio
Server
Host
HTTPS request
Streamable HTTP
Server

Why the shift away from SSE

The original remote transport held a long-lived server-sent-events stream open so the server could push requests back. That made every connection sticky — you could not put a plain load balancer in front of it, and a dropped stream lost the session.

stdio is not a lesser transport — for anything that touches the local filesystem or local credentials it is the correct one, and the simplest.

#stdio#http#transport
schema

One request, end to end

What actually happens when a user asks something the model cannot answer alone.

Rendering diagram…

The two halves

  • Discovery. The client asks the server what it offers and gets back a described list. The model reads those descriptions like documentation.
  • Invocation. The model picks one, the client calls it, the server does the real work and returns something the model can read.
#sequence#discovery
schema

Tools, resources, prompts

A server exposes three things. The difference that matters is who decides to use them.

PrimitiveWhat it isControlled by
Toolsexecutable functions with typed inputthe model
Resourcesreadable data, static or dynamicthe application
Promptstemplates a user can invokethe user
Toolsmodel-controlled — the LLM chooses to call
Resourcesapplication-controlled — read-only, the host attaches
Promptsuser-controlled — a slash command, a template

The wire methods

Discovery and use are separate calls, and each has a list side and an act side: tools/list then tools/call, resources/list then resources/read, prompts/list then prompts/get.

If you are unsure whether something is a tool or a resource, ask: should the model be allowed to trigger it on its own? Yes → tool. No → resource.

#tools#resources#prompts
note

The client side, and MRTR

The protocol was originally two-way: a server could call back into the client to borrow the model, ask the user something, or read the workspace roots.

PrimitiveWhat it didStatus
Samplingserver borrows the host’s modeldeprecated
Rootsserver asks which directories are in scopedeprecated
Elicitationserver asks the user a question mid-callreplaced by MRTR
Loggingserver emits structured logs to the hostdeprecated

All of these needed a held-open bidirectional stream, which is exactly what the stateless core removed.

What replaced elicitation

Multi Round-Trip Requests. Instead of pushing a question down an open stream, the server returns resultType: "input_required" along with what it needs. The client collects the answers and retries the original call with them attached.

input_required, then retry
tools/call
input_required
client asks user
retry with answers
#elicitation#mrtr#deprecated
note

Designing a tool

The model never sees your code. It sees a name, a description and a schema — so that is where quality lives.

Few and widefive good tools beat forty narrow ones
Honest descriptionssay when NOT to use it, not just when to
Typed inputa strict schema is cheaper than a retry
Readable errorsreturn text the model can act on
Paginatenever hand back an unbounded blob

The failure modes

  • Tool soup. Forty tools with terse names. The model picks wrong, and no amount of prompt engineering above it fixes a bad menu.
  • Swallowed errors. A tool that returns an empty result on failure teaches the model that the answer is "nothing".
  • Unpaginated returns. One tool dumping a full JSON document is the most common cause of a blown context window.

The loop consuming these tools is its own subject — see an agent is a loop in the AI garden.

#tools#design
schema

Anatomy of a server

A server is smaller than it sounds. It declares what it can do, then answers two kinds of call for each primitive: list them and do one.

what you actually implement
Declare capabilities
Answer tools/list
Handle tools/call
Return content
Validate input
Talk to the real system
Shape the result
Report errors as text

The shape of one tool

FieldWhy it matters
namestable identifier — renaming it breaks callers
descriptionthe only documentation the model reads
inputSchemaJSON Schema; the client can validate before calling
handleryour code — and the only place privileges are used

The hardest part of writing a server is not the protocol — the SDKs handle that. It is deciding what to expose and what to keep out of reach.

#server#sdk
note

MCP vs function calling

A common confusion: MCP does not replace function calling. It sits above it.

Function callingMCP
What it isa model capabilitya wire protocol
Who defines the toolsyour app, in your codea server, at runtime
Discoverynone — you hardcode the listtools/list
Reuse across appsno, copy the codeyes, point at the server
Transportnone, it is in-processstdio or HTTP
Rendering diagram…

The model still emits a tool call the same way it always did. MCP answers the question one level up: where did that list of tools come from, and who else can use it?

#function-calling#comparison
note

The attack surface

Every MCP server is a new piece of untrusted text entering the model's context and a new set of privileges the model can reach. Both are attack surface.

Tool poisoninga malicious description steers the model
Indirect prompt injectioninstructions hidden in returned data
Confused deputyserver acts with its own broad credential
Supply chainyou installed a server you did not read

The one that surprises people

Tool descriptions are context. A server can put instructions in a description, and the model reads them the same way it reads yours. Invariant Labs demonstrated this concretely: a poisoned description on a second server was enough to get a model to exfiltrate a user's entire WhatsApp history through an innocuous-looking call.

Confused deputy, concretely

An agent legitimately holds a broad database credential. A prompt injection persuades it to use that credential for something the deployer never intended. The server was not hacked — it did exactly what it was asked.

Treat everything a server returns as data, never as instructions. That includes tool descriptions, which is the part people forget.

#security#prompt-injection
note

Authorization

For remote servers, authorization is the whole game. The spec settles on OAuth 2.1, and the details it tightens are the ones that get exploited.

PKCE, alwaysmandatory for every client, not just public ones
No implicit grantremoved from the spec entirely
Validate audienceonly accept tokens minted for you
No passthroughnever forward a client token to an upstream API

Two hardening changes worth knowing

  • Issuer validation (RFC 9207). Credentials are bound to the issuer that minted them, which closes a class of mix-up attacks between authorization servers.
  • CIMD replaces Dynamic Client Registration. Client ID Metadata Documents are now the supported path; DCR is formally deprecated.

Scope tokens to the minimum, and scope them per user — a server-wide service account is the confused deputy problem waiting to happen.

#oauth#pkce#security
note

The 2026-07-28 stateless spec

The 2026-07-28 spec is the largest change since MCP shipped, and it is mostly one idea: make the protocol stateless so it can be operated like a normal HTTP service.

ChangeWhat it means
Stateless coreno initialize handshake, no session id
Self-describing requestseach carries version, identity, capabilities
MRTRinput_required + retry replaces server-initiated calls
Header routingMcp-Method and Mcp-Name on every request
Cacheable liststtlMs and cacheScope on list results
Extensions frameworkTasks, MCP Apps and EMA move out of the core

Why statelessness was the blocker

With a handshake and a session id, every request had to land on the instance that started the conversation. That means sticky sessions, no plain round-robin, and a lost stream losing the session.

what it unlocks
Any request
Any instance
Plain load balancer
Horizontal scale

Deprecated on a twelve-month clock: HTTP+SSE transport, sampling, roots and logging. Build new things without them.