
This tutorial is part of Build an MCP Server, within The MCP Guide. Want it built for you? See MCP development.
To deploy an MCP server to Vercel, add one Next.js route that calls createMcpHandler from the mcp-handler package, test it locally with the MCP Inspector, then deploy with vercel --prod or a Git push. Clients connect to https://<your-app>.vercel.app/api/mcp over Streamable HTTP.
The important change in 2026 is the package version. mcp-handler 2.x is built on the official MCP TypeScript SDK v2 (@modelcontextprotocol/server), not the v1 package @modelcontextprotocol/sdk. It drops the old SSE transport and the Redis requirement. Tutorials from 2025 that use server.tool(...), redisUrl or /sse routes describe version 1.
Checked against: Vercel's Deploy MCP servers to Vercel guide (last updated 18 September 2026), the mcp-handler README and npm, on 2 November 2026. The route below ran locally with next dev and built with next build on 2 November 2026 (Node.js 24, Next.js 16.4.4, mcp-handler 2.3.0, @modelcontextprotocol/server 2.4.0, zod 4.6.7). Nothing was deployed to a Vercel account.
Key Takeaways
- Use
mcp-handler2.x with@modelcontextprotocol/server2.x and Zod 4. The README says SDK v1 users should stay onmcp-handler1.x. - On 2 November 2026 npm's latest
mcp-handlerwas 2.3.0, while Vercel's docs pinned 2.1.1 in the install command. - The handler is a Web-standard
(Request) => Responsefunction, so it also works in Nuxt, SvelteKit and Hono, not only Next.js. - One handler serves the 2026-07-28 protocol and falls back to stateless Streamable HTTP for 2025-era clients.
withMcpAuthchecks bearer tokens andprotectedResourceHandlerpublishes OAuth metadata, but you still need your own authorization server.
Prerequisites
- Node.js 20 or later. Both the Vercel guide and the README set this floor.
- A Next.js App Router project. The README lists Next.js 13 or later as the requirement for Next.js users.
- A Vercel account and the Vercel CLI for the deploy step. Everything before that runs locally.
If you are new to remote MCP servers, Lesson 21 of our MCP course explains the moving parts, and the full course covers hosts, clients and tools from the start.
Step 1: Install the packages
In your Next.js project:
npm i mcp-handler@2 @modelcontextprotocol/server@2 zod@4
Vercel's guide uses mcp-handler@2.1.1, and the README uses mcp-handler@^2. On 2 November 2026 npm showed 2.3.0 as the latest release, with @modelcontextprotocol/server ^2.0.0 and next >=13.0.0 as peer dependencies. The README adds that 2.x needs Zod 4.2.0 or later.
One more thing worth knowing: npm lists the repository as vercel-labs/mcp-handler, and the README carries a "Vercel Labs Product" badge. Treat it as a supported open-source package from Vercel's labs team, and pin the version you test.
Step 2: Create the MCP route
Create app/api/mcp/route.ts. This version adapts the dice example from Vercel's guide, adds a title, an argument description and a read-only hint, and sets the server name:
import { createMcpHandler } from 'mcp-handler';
import { z } from 'zod';
const handler = createMcpHandler(
(server) => {
server.registerTool(
'roll_dice',
{
title: 'Roll Dice',
description: 'Roll an N-sided die and return the result.',
inputSchema: z.object({
sides: z.number().int().min(2).max(100).describe('Number of sides'),
}),
annotations: { readOnlyHint: true },
},
async ({ sides }) => {
const value = 1 + Math.floor(Math.random() * sides);
return { content: [{ type: 'text', text: `You rolled a ${value}.` }] };
},
);
},
{ serverInfo: { name: 'dice-server', version: '1.0.0' } },
);
export { handler as GET, handler as POST };
How it works:
createMcpHandler(initialize, options)receives a function that registers your tools. The README's 2.x migration notes say the options object combines the SDK's server options withserverInfo,verboseLogsandonEvent.serverInfois the name clients see. Without it, the local run reported a default name of "mcp-typescript server on vercel".registerToolreplaces the variadicserver.tool(...)from 1.x. TheinputSchemais a fullz.object(...), not a raw shape. Our registerTool guide goes deeper on the config fields./api/mcpis only a convention. The README saysmcp-handlerdoes not inspect the path, so any route works as long as clients get the full URL.
The route showed up in next build output as a dynamic (ƒ) route, which is what you want for an API endpoint.
Step 3: Test it locally
Start Next.js with npm run dev, then open the Inspector:
npx @modelcontextprotocol/inspector@latest
Vercel's guide walks through the browser UI. Open http://127.0.0.1:6274, select Streamable HTTP, enter http://localhost:3000/api/mcp, paste the Proxy Session Token printed in your terminal under Configuration, and select Connect. Then list tools and call roll_dice. Those steps describe the older Inspector proxy. Inspector v2 has no separate proxy, so open the exact URL the launcher prints, which carries the session token.
For a scripted check, the Inspector CLI works without the browser:
npx @modelcontextprotocol/inspector --cli http://localhost:3000/api/mcp \
--protocol-era modern --method initialize --format json
In the local run, this reported "protocolVersion":"2026-07-28" and the dice-server name. A tools/list call with --protocol-era legacy returned the same tool, which matches the README's dual-era claim. Calling the tool with {"sides":1} returned an isError result with "Too small: expected number to be >=2" before the handler ran, so Zod validation is doing its job.
Our MCP Inspector lesson explains how to read these results.
Step 4: Deploy to Vercel
You have two options:
- Git. Import the project's Git repository into Vercel, and pushes build new deployments.
- CLI. From the project root, run
vercelfor a deployment andvercel --prodfor production. The vercel deploy reference notes that a new project's first deployment is always a production deployment, even without--prod.
The guide says you will get a URL such as https://my-mcp-server.vercel.app, so the endpoint is https://my-mcp-server.vercel.app/api/mcp. Run the Inspector against that URL before you connect a real client.
Vercel's guide lists the platform features it sees as useful for MCP: Vercel Functions with Fluid compute for bursty traffic, Instant Rollback, preview deployments with Deployment Protection, Vercel Firewall and Rolling Releases. Those are Vercel's descriptions; check your plan's limits before relying on them.
Step 5: Connect a client
Clients that support Streamable HTTP take the URL directly. Vercel's Cursor example goes in .cursor/mcp.json:
{
"mcpServers": {
"dice": {
"url": "https://my-mcp-server.vercel.app/api/mcp"
}
}
}
For stdio-only clients, the README suggests bridging with npx -y mcp-remote <url> as the command. Our MCP config generator builds the block for Claude, VS Code and other clients.
The guide also covers agents you build yourself: the AI SDK (createMCPClient from @ai-sdk/mcp), eve and TanStack AI can all connect to the same endpoint.
Step 6: Add authorization
The dice server is public. mcp-handler includes two helpers for protecting it, and the guide is clear that the package "does not issue tokens or run an authorization server." You bring that part, from an identity provider or your own service.
First, wrap the handler:
import type { AuthInfo } from '@modelcontextprotocol/server';
import { withMcpAuth } from 'mcp-handler';
const verifyToken = async (
_req: Request,
bearerToken?: string,
): Promise<AuthInfo | undefined> => {
// Demo only: replace with real checks of issuer, audience, expiry and scopes.
if (!bearerToken || bearerToken !== process.env.MCP_DEMO_TOKEN) return undefined;
return { token: bearerToken, scopes: ['read:dice'], clientId: 'demo-client' };
};
const authHandler = withMcpAuth(handler, verifyToken, {
required: true,
requiredScopes: ['read:dice'],
resourceMetadataPath: '/.well-known/oauth-protected-resource',
});
export { authHandler as GET, authHandler as POST };
The guide's version compares the token with an MCP_DEMO_TOKEN environment variable for local testing only. Per the guide, requests without a valid token get 401, and a valid token missing a required scope gets 403.
Second, publish the OAuth protected resource metadata at app/.well-known/oauth-protected-resource/route.ts using protectedResourceHandler, with authServerUrls set to your authorization server's issuer and resourceUrl set to the full /api/mcp URL. Export the handler as GET and metadataCorsOptionsRequestHandler() as OPTIONS, as the guide shows. That document is how clients discover where to sign in.
The README notes that the 2026-07-28 spec deprecates Dynamic Client Registration in favor of Client ID Metadata Documents, and that CIMD support is the authorization server's job.
Common pitfalls
- Mixing v1 and v2.
mcp-handler2.x with@modelcontextprotocol/sdk1.x will not work. Match the major versions. - Old 1.x options. The README says
basePath,sseEndpoint,redisUrl,maxDurationandsessionIdGeneratorwere removed. Mount the handler at the route you want instead. - Expecting SSE. HTTP+SSE (2024-11-05) support is removed in 2.x. Session GET and DELETE requests answer
405, because serving is stateless. - Protected previews. Deployment Protection controls who can open preview URLs, so a remote MCP client may be blocked from a protected preview. Test previews with your own session or use production for client tests.
- Reading
extra.authInfo. In 2.x, auth info moved toctx.http?.authInfoinside handler callbacks.
For a comparison of hosting platforms, read Cloudflare Workers vs Vercel for MCP Hosting.
Next step
Once the dice server works end to end, swap in a tool that reads real data and add authorization before you share the URL. If you want a team to design the tools, auth and rollout for a production MCP server, see our MCP development service.
More in the MCP Guide
Free tools: server.json Validator · MCP Error Code Lookup
Latest research: State of MCP: October 2026
Frequently asked questions
Does mcp-handler support MCP SDK v2?
Yes. The README says mcp-handler 2.x is built on MCP SDK v2 and requires @modelcontextprotocol/server ^2.0.0. If you are still on @modelcontextprotocol/sdk 1.x, the README says to use mcp-handler 1.x.
Do I need Redis to run an MCP server on Vercel?
Not with mcp-handler 2.x. The README says Redis "is no longer needed or used," because the SSE transport it supported was removed and the handler is stateless.
Can I use mcp-handler without Next.js?
Yes. It returns a Web-standard request handler, and the README lists Nuxt/Nitro, SvelteKit, Hono and other Fetch-compatible frameworks. Express needs a Web Request adapter or the official SDK middleware.
Is this the same as the Vercel MCP server?
No. This guide hosts your own MCP server on Vercel. The Vercel MCP server is Vercel's own hosted server for managing projects and deployments from an AI client; see our Vercel MCP server profile.
How do older MCP clients connect?
The README says 2025-era Streamable HTTP clients are served through the SDK's stateless legacy fallback from the same handler. Clients that only speak HTTP+SSE need to upgrade or use a bridge.











