How to Deploy a Remote MCP Server on Vercel: Complete Step-by-Step Guide 2025
The Model Context Protocol (MCP) is revolutionizing how AI applications interact with external tools and data sources. In this comprehensive guide, we'll walk through deploying your own remote MCP server on Vercel's serverless platform, using our production my-mcp-server server as a real-world example.
What is the Model Context Protocol (MCP)?
MCP is an open standard developed by Anthropic that enables large language models (LLMs) to communicate seamlessly with external systems. Think of it as a universal adapter that allows AI assistants like Claude, ChatGPT, and others to access your custom tools, databases, and APIs in a standardized way.
Key Benefits of MCP
- Standardized Communication: One protocol to rule them all - no more custom integrations for each AI platform
- Tool Discovery: AI clients can automatically discover what tools your server provides
- Type Safety: Built-in schemas ensure data validation and proper error handling
- Extensibility: Easy to add new tools without changing the core architecture
Why Deploy MCP on Vercel?
Vercel officially announced MCP server support in May 2025, making it the premier platform for hosting remote MCP servers. Here's why:
- Serverless Architecture: Pay only for what you use, with automatic scaling
- Global Edge Network: Low latency access from anywhere in the world
- Built-in Support: Native Streamable HTTP transport support
- Easy Deployment: Push to Git and deploy automatically
- Fluid Compute: Efficient resource utilization for AI workloads
Architecture Overview
Before diving into the code, let's understand the architecture:
┌──────────────────┐ HTTPS ┌──────────────────────┐
│ │ ◄────────────► │ │
│ MCP Client │ Streamable │ Vercel Function │
│ (Claude Code, │ HTTP │ (MCP Server) │
│ Cursor, etc.) │ │ │
│ │ │ ┌────────────────┐ │
└──────────────────┘ │ │ Tool Handler │ │
│ ├────────────────┤ │
│ │ Tool Handler │ │
│ ├────────────────┤ │
│ │ Tool Handler │ │
│ └────────────────┘ │
│ │
└──────────────────────┘
Why Streamable HTTP?
The Streamable HTTP transport (introduced in the MCP specification 2025-03-26) is the recommended protocol for remote MCP servers. It offers:
- Efficient connection reuse
- Better concurrency handling than SSE
- Stateless or stateful operation modes
- Lower TCP connection overhead
Step 1: Project Setup
First, create a new project or use an existing Next.js/Node.js project:
# Create new project
mkdir my-mcp-server
cd my-mcp-server
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk zod
npm install -D @vercel/node typescript @types/node
Create your tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"outDir": "dist",
"declaration": true
},
"include": ["src/**/*", "api/**/*"]
}
Step 2: Create the MCP Server Handler
Create the main API handler at api/index.ts. This is where the magic happens:
Setting Up the Server Factory
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
// Create MCP Server Factory
function createMcpServer(): McpServer {
const server = new McpServer({
name: "my-mcp-server",
version: "1.0.0",
});
// Register your first tool
server.registerTool("hello_world", {
title: "Hello World",
description: "A simple greeting tool",
inputSchema: {
name: z.string().describe("Name to greet"),
},
}, async ({ name }) => {
return {
content: [{
type: "text",
text: JSON.stringify({
message: `Hello, ${name}! Welcome to MCP.`,
timestamp: new Date().toISOString(),
}, null, 2),
}],
};
});
return server;
}
Implementing the Request Handler
// Main API Handler
export default async function handler(
req: VercelRequest,
res: VercelResponse
) {
// CORS headers for cross-origin requests
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers",
"Content-Type, Authorization, mcp-session-id, mcp-protocol-version");
res.setHeader("Access-Control-Expose-Headers",
"mcp-session-id, mcp-protocol-version");
// Handle preflight requests
if (req.method === "OPTIONS") {
return res.status(200).end();
}
// Health check endpoint
if (req.method === "GET" && req.query.action === "health") {
return res.status(200).json({
status: "ok",
server: "my-mcp-server",
version: "1.0.0",
});
}
// Handle MCP protocol requests
try {
const server = createMcpServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // Stateless mode for serverless
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
// Cleanup after response
res.on("finish", async () => {
try {
await transport.close();
await server.close();
} catch {
// Ignore cleanup errors
}
});
} catch (error) {
if (!res.headersSent) {
return res.status(500).json({
jsonrpc: "2.0",
error: {
code: -32603,
message: `Internal server error: ${error}`,
},
id: req.body?.id || null,
});
}
}
}
Step 3: Configure Vercel Deployment
Create vercel.json for proper configuration:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"version": 2,
"buildCommand": "npm run build",
"functions": {
"api/index.ts": {
"maxDuration": 60
}
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET, POST, OPTIONS" },
{ "key": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }
]
}
]
}
Step 4: Adding Real Tools - The my-mcp-server Example
Let's look at how our production my-mcp-server server implements real tools. This server provides web search, social media publishing, image generation, and more.
Web Search Tool
// Web Search Tool
server.registerTool("search_web", {
title: "Search Web",
description: "Search the web for current information using Tavily AI",
inputSchema: {
query: z.string().describe("Search query"),
maxResults: z.number().optional().describe("Maximum results (default: 5)"),
topic: z.enum(["general", "news"]).optional().describe("Search type"),
},
}, async ({ query, maxResults, topic }) => {
const result = await tavilySearch(query, { maxResults, topic });
return {
content: [{
type: "text",
text: JSON.stringify({
query: result.query,
answer: result.answer,
results: result.results,
}, null, 2),
}],
};
});
Image Generation Tool
// Image Generation Tool
server.registerTool("generate_hero_image", {
title: "Generate Hero Image",
description: "Generate a hero image using Gemini AI",
inputSchema: {
topic: z.string().describe("Image topic/description"),
style: z.enum([
"modern-professional",
"tech-gradient",
"minimal-abstract"
]).describe("Visual style"),
},
}, async ({ topic, style }) => {
const image = await generateImageWithGemini(topic, style);
return {
content: [{
type: "text",
text: JSON.stringify({
status: "success",
topic,
style,
mimeType: image.mimeType,
base64Data: image.base64Data,
}, null, 2),
}],
};
});
Step 5: Environment Variables
Set up your environment variables in Vercel:
# Required for various integrations
TAVILY_API_KEY=your_tavily_key
GOOGLE_API_KEY=your_google_key
TYPEFULLY_API_KEY=your_typefully_key
Step 6: Deploy to Vercel
Deploy your MCP server:
# Install Vercel CLI
npm install -g vercel
# Deploy
vercel
# For production
vercel --prod
After deployment, your server will be available at:
https://your-project.vercel.app/api
Step 7: Connect Clients to Your MCP Server
Connecting Claude Code / Claude Desktop
Add to your Claude configuration (typically at ~/.claude.json or settings.json):
{
"mcpServers": {
"my-mcp-server": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://your-project.vercel.app/api"
]
}
}
}
Connecting with the AI SDK (Vercel)
import { experimental_createMCPClient } from "ai";
const mcpClient = await experimental_createMCPClient({
transport: {
type: "sse",
url: "https://your-project.vercel.app/api",
},
});
// Get available tools
const tools = await mcpClient.tools();
console.log("Available tools:", tools);
Step 8: Testing Your Server
Test your server is working:
# Health check
curl https://your-project.vercel.app/api?action=health
# List tools (via mcp-remote)
npx mcp-remote https://your-project.vercel.app/api --list-tools
Best Practices
1. Stateless Design
Vercel Functions are stateless. Design your tools accordingly:
// Good: Stateless operation
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // Stateless
});
2. Error Handling
Always return proper JSON-RPC error responses:
} catch (error) {
return res.status(500).json({
jsonrpc: "2.0",
error: {
code: -32603,
message: `Internal error: ${error.message}`,
},
id: req.body?.id || null,
});
}
3. Timeout Management
Set appropriate timeouts in vercel.json:
{
"functions": {
"api/index.ts": {
"maxDuration": 60
}
}
}
4. Input Validation with Zod
Always validate inputs with Zod schemas:
inputSchema: {
query: z.string().min(1).max(1000),
limit: z.number().min(1).max(100).optional(),
},
Next Steps
Now that you understand how to deploy an MCP server on Vercel, here are some ideas for tools you could build:
- Research Tools: Web search, news aggregation, topic research
- Content Tools: Blog outline generation, SEO optimization
- Image Tools: AI image generation with various styles
- Database Tools: Query and manage your databases
- API Integrations: Connect to third-party services
Conclusion
Deploying an MCP server on Vercel combines the power of serverless computing with the flexibility of the Model Context Protocol. By following this guide, you can create production-ready AI tool integrations that work across Claude, ChatGPT, and other MCP-compatible clients.
The MCP ecosystem is growing rapidly, with major companies like Booking.com, Expedia, Morningstar, and Microsoft adopting the protocol. Now is the perfect time to build your own MCP integrations and join this expanding ecosystem.
Resources
Ready to deploy your own MCP server? Start with our template and have your AI-powered tools live in minutes!