Community-maintained FDE reference. Not an official Vercel or Anthropic project. About this project
Getting started with MCP on Vercel
TL;DR: The Model Context Protocol (MCP) lets an AI application talk to outside systems through a small, standardized contract. One host (the app the user sees) runs one client per connected server, and each server exposes a fixed menu of tools, resources, and prompts. On Vercel, a server is a Vercel Function answering over Streamable HTTP, not a long-lived process, and that one fact shapes everything else in these docs. Read this page first, then follow the path for your role at the bottom.
This is the first page to read in vercel-mcp-reference. It builds the mental model the rest of the docs assume, in plain language, then points you at the right next page. You do not need to read it linearly; the reading paths below route each audience to what matters for them.
What MCP is, in one paragraph
MCP is an open standard for connecting AI applications to external systems. It plays the role for AI assistants that the browser-to-web-server contract plays for the web: a single, predictable way for a program the user trusts to reach out to many independent backends. Before MCP, every assistant integrated each tool its own way; MCP replaces those one-off integrations with one protocol, so any compliant client can talk to any compliant server. It is built on JSON-RPC, runs over a choice of transports, and, as of the 2026-07-28 revision, is stateless by design: instead of a long-lived session negotiated up front, every request itself carries the protocol version and the client’s capabilities, so both sides always know which features the other supports.
The mental model: host, client, server
Three roles do all the work. Getting them straight is most of understanding MCP.
- Host: the AI application the user actually uses (a chat app, an IDE, a desktop assistant). It owns the screen, the user’s trust, and the language model. The host decides what to connect to and gates anything sensitive behind user consent.
- Client: a connector that lives inside the host. There is exactly one client per server. A host connected to five servers runs five clients, each an isolated connection with its own state. The client speaks the protocol; it has no opinions about the user.
- Server: a small backend program that knows how to do one job: read a calendar, query a database, run a build. A server exposes its abilities through the protocol and should be treated as untrusted external code. In this repo, servers are Vercel Functions behind an
/api/mcproute.
Diagram source (Mermaid)
flowchart LR
User((User)) --- Host
subgraph Host["Host (chat app, IDE, assistant)"]
Model[Language model]
C1[Client 1]
C2[Client 2]
end
C1 -- "Streamable HTTP" --> S1["Server on Vercel (/api/mcp)"]
C2 -- "stdio" --> S2["Server as local process"]The single most common confusion is thinking one client talks to many servers. It does not. The host runs many clients in parallel, one per server, and keeps them isolated from each other: a server cannot see the conversation, the model’s full context, or any other server’s state. That isolation is a trust boundary, and it is deliberate. When a workflow needs several servers, the host composes them; see the orchestrator pattern.
What a server exposes: the three primitives
Everything a server offers falls into three primitives. The difference that matters is who is in control:
| Primitive | What it is | Who controls invocation |
|---|---|---|
| Tools | Actions the model can take: query, send, create, run | Model-controlled (with host/user approval for sensitive actions) |
| Resources | Context the server can supply: files, records, documents | Application-controlled (the host decides what to attach) |
| Prompts | Reusable templates a user can invoke | User-controlled (the user picks them, e.g. a slash command) |
A second set of features flows the other way and is easy to miss: sampling (a server asking the host’s model to generate text, deprecated in 2026-07-28 per SEP-2577 in favor of direct provider APIs) and elicitation (a server asking the user a question mid-call), plus utilities like progress and cancellation. Since 2026-07-28 these server-initiated exchanges run as multi round-trip requests (MRTR): the server returns an input_required result naming what it needs, and the client retries the original request with the answers. The primitives page covers all of them, with the method names and who controls each.
How a connection actually runs
Under the 2026-07-28 revision there is no opening handshake. Every request is self-contained: the client puts its protocol version and capabilities in the request’s _meta field (io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities), and the server answers with its own identity in the result (io.modelcontextprotocol/serverInfo). If the server does not support the requested version, it returns an UnsupportedProtocolVersionError instead of a result. A client that wants to know what a server offers before committing MAY call server/discover, a mandatory server method that advertises supported protocol versions, capabilities, and identity; or it can go straight to discovery (tools/list, resources/list, prompts/list) and start invoking what it finds. Both sides always know what the other can do, because the information travels with every message rather than living in connection state.
Diagram source (Mermaid)
sequenceDiagram
participant Host
participant Client
participant Server
Client->>Server: server/discover (optional probe)
Server-->>Client: versions, capabilities, identity
Client->>Server: tools/list (_meta carries version + capabilities)
Server-->>Client: available tools (result carries serverInfo)
Host->>Client: model selects a tool
Client->>Server: tools/call (name, args, _meta as always)
Server-->>Client: result (complete, or isError true)
Client-->>Host: surface result to userWire status: the pinned stack (mcp-handler 2.1.1 on @modelcontextprotocol/server 2.0.0) serves the 2026-07-28 contract natively over Streamable HTTP and falls back to stateless 2025-11-25 Streamable HTTP for legacy clients; the SDK Client defaults to that legacy handshake unless you opt in to modern version negotiation, so the examples’ in-memory test suites exercise only the legacy path. In practice: a curl with the 2026-07-28 headers gets the sessionless exchange described above, while the Inspector or an SDK Client left at its default opens with the legacy initialize and notifications/initialized exchange that revision 2025-11-25 required; the message trace shows both.
The internals overview walks through this lifecycle message by message, and the message trace shows the exact JSON-RPC bodies and HTTP headers as captured against a deployed example.
The serverless twist
Everything above is standard MCP. Here is what running it on Vercel changes, and why this repo exists:
- A Vercel Function is not a resident process. Each request may land on a fresh invocation. Fluid compute reuses warm instances when it can, but that reuse is best-effort, never a correctness guarantee. Anything your server must remember between calls has to live outside the function and travel as explicit handles in tool arguments, which is exactly the model the 2026-07-28 revision adopted when it removed protocol sessions. Serverless sessions is the deep dive on how the protocol met the platform.
- Streamable HTTP is the primary transport. Stdio assumes the client spawned your server as a child process, which cannot happen on a serverless platform. This repo’s servers speak Streamable HTTP through
mcp-handler, Vercel’s documented hosting layer, from a Next.js route handler. See transports. - Auth is mandatory in practice. Every deployed server is a remote server on a public URL. The security section covers OAuth 2.1 for MCP and the Vercel wiring, and the deployment section covers keeping preview deployments from becoming accidental public endpoints.
The 10-minute path
The fastest way to make all of this concrete is examples/minimal-server (in the repository): the smallest end-to-end server this repo can deploy, one echo tool over Streamable HTTP. You will run it locally, watch the handshake, and deploy it. You need Node 22 or newer; the deploy step also needs the Vercel CLI and a free account.
-
Clone and run. From the repo root:
Terminal window cd examples/minimal-servernpm installnpm run devThis starts the Next.js dev server with the MCP endpoint at
http://localhost:3000/api/mcp. -
Connect an inspector. In a second terminal:
Terminal window npx @modelcontextprotocol/inspectorIn the Inspector UI, choose the Streamable HTTP transport, enter
http://localhost:3000/api/mcp, connect, and callecho. Watch the message order in the Inspector’s history pane: if your Inspector build still speaks the 2025-11-25 wire protocol, the server answers it on its stateless legacy fallback (see the connection section above) and you will see the legacyinitialize, the capabilities exchange,notifications/initialized, thentools/listandtools/call; a 2026-07-28 client skips straight toserver/discoveror its first real request. Try the two failure classes, too: a tool name that does not exist fails with a JSON-RPC protocol error (tool not found), while schema-invalid arguments to the realechotool come back as anisError: truetool result. That split is exactly how hosts are meant to distinguish “you called something that is not there” from “the tool ran and failed”. -
Deploy it. From the same directory:
Terminal window vercel deployNo environment variables are required for the Inspector or any other non-browser client. If a browser-based client will call the endpoint, set
MCP_ALLOWED_ORIGINSon the project (a comma separated list of origins): the route answers any other browserOriginwith 403, and requests without anOriginheader pass through unaffected. Your MCP endpoint ishttps://<deployment>/api/mcp; point the Inspector at it and callechoagain, this time against a real Vercel Function.
The example’s README (examples/minimal-server/README.md, in the repository) explains its structure: the protocol logic lives in src/server.ts as an exported configureServer(server), and the route handler is a thin shell that wraps createMcpHandler in the Origin allowlist from src/origin.ts. Every other example in the repo copies that split.
Reading paths by role
Pick the row that fits you. Each path is ordered.
| If you are a… | Read in this order |
|---|---|
| Engineer building a server | this page → internals overview → primitives → transports → serverless sessions → patterns → deployment |
| Architect evaluating MCP plus serverless | this page → internals overview → serverless sessions → patterns: adapter, sidecar, facade, orchestrator |
| Security / governance reviewer | this page → security checklist → authorization → patterns: least privilege, trust boundaries → client-side consent |
| Non-technical stakeholder | this page → glossary → the plain-language openings of the internals pages |
For the full directory map and conventions, see the docs index.
Common first-time confusions
- “MCP is an HTTP API.” No. MCP is a JSON-RPC application protocol that runs over a transport you choose (Streamable HTTP or stdio). The semantics are identical on each; only the framing differs.
- “A Vercel Function is a resident process.” No. It is an invocation that may be created, reused, or discarded per request. The 2026-07-28 revision stopped pretending otherwise: requests are self-contained, and anything a server must remember between calls travels as explicit handles or lives in external state. Serverless sessions explains the model, and what instance reuse does and does not promise.
- “One client connects to several servers.” No. One client, one server. Many servers means many clients, composed by the host.
- “Tools, resources, and prompts are basically the same.” No. They differ by who controls them: the model, the application, and the user respectively. Choosing the wrong primitive for a capability is a frequent early design mistake.
- “A server sees the whole conversation.” No. Each client-server connection is isolated. A server receives only what it is given, by design, and you should build servers assuming the same courtesy is not extended back: treat every server as untrusted.
- “My deployment is private until I share the URL.” No. Preview deployments are public URLs unless Deployment Protection is on. The security checklist makes this a pre-deploy gate, not an afterthought.
Where to look now
- Internals overview - the lifecycle above, message by message, with debugging notes.
- Serverless sessions - the flagship page on MCP state atop stateless invocations.
examples/minimal-server(in the repository) - the 10-minute path’s target, and the structural template for every other example.- Security checklist - read it before your first real deploy, not after.
Bibliography
- Model Context Protocol, official site - https://modelcontextprotocol.io
- Model Context Protocol Specification, Architecture, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/architecture
- Model Context Protocol Specification, Versioning, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning
- Model Context Protocol Specification, Transports, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
- Model Context Protocol Specification, Server Features, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/server
- Model Context Protocol Specification, Changelog, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/changelog
- Model Context Protocol, MCP Inspector - https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector
- JSON-RPC 2.0 Specification - https://www.jsonrpc.org/specification
- Vercel Documentation, Deploy MCP servers to Vercel - https://vercel.com/docs/mcp/deploy-mcp-servers-to-vercel
- Vercel Documentation, Fluid compute - https://vercel.com/docs/fluid-compute