Skip to main content
RapidDev - Software Development Agency
mcp-tutorial

How to deploy a remote MCP server

Deploy an MCP server remotely by switching from stdio to Streamable HTTP transport and hosting on platforms like Vercel, Railway, or Render. You need to wrap your server with an HTTP framework like Express, configure environment variables on your hosting platform, and update your MCP host config to point to the deployed URL instead of a local command.

What you'll learn

  • How to convert a stdio MCP server to Streamable HTTP for remote deployment
  • Step-by-step deployment to Railway, Render, and Vercel
  • How to configure environment variables and secrets on each platform
  • How to connect Claude Desktop and Cursor to your remote server
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read30-45 minMCP SDK 1.0+, Node.js 18+, Vercel/Railway/RenderMarch 2026RapidDev Engineering Team
TL;DR

Deploy an MCP server remotely by switching from stdio to Streamable HTTP transport and hosting on platforms like Vercel, Railway, or Render. You need to wrap your server with an HTTP framework like Express, configure environment variables on your hosting platform, and update your MCP host config to point to the deployed URL instead of a local command.

Deploying MCP Servers to the Cloud

Most MCP servers start as local stdio processes, but sharing them with a team or accessing them from multiple machines requires remote deployment. This tutorial walks you through converting your server to use Streamable HTTP transport and deploying it to popular cloud platforms. You will learn how to handle environment variables, set up health checks, and configure your MCP hosts to connect to the remote endpoint.

Prerequisites

  • A working MCP server built with @modelcontextprotocol/sdk
  • Node.js 18+ installed locally
  • A GitHub account for deployment integration
  • An account on Railway, Render, or Vercel
  • Basic familiarity with Git and deploying web applications

Step-by-step guide

1

Add Streamable HTTP transport to your server

Your stdio server communicates via stdin/stdout, which does not work over the network. Install Express and update your server to use the Streamable HTTP transport provided by the MCP SDK. The createMcpExpressApp helper wraps your McpServer instance in an Express application that exposes a /mcp endpoint. This is the only code change needed — all your existing tools and resources remain the same.

typescript
1npm install express
2npm install -D @types/express

Expected result: Your project has Express installed and you are ready to create the HTTP entry point.

2

Create the HTTP server entry point

Create a new file that imports your MCP server and wraps it with the Express Streamable HTTP transport. This file will be your deployment entry point. The server listens on the PORT environment variable (which cloud platforms set automatically) and logs to stderr. Add a health check endpoint at the root so load balancers and uptime monitors can verify your server is running.

typescript
1// src/http.ts
2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4import { registerTools } from "./tools.js";
5
6const server = new McpServer({
7 name: "my-remote-server",
8 version: "1.0.0",
9});
10
11registerTools(server);
12
13const app = createMcpExpressApp(server);
14
15// Health check endpoint
16app.get("/", (_req, res) => {
17 res.json({ status: "ok", name: "my-remote-server" });
18});
19
20const PORT = parseInt(process.env.PORT || "3000", 10);
21app.listen(PORT, () => {
22 console.error(`MCP server running on port ${PORT}`);
23});

Expected result: Running node dist/http.js starts an HTTP server that responds on /mcp for MCP traffic and / for health checks.

3

Deploy to Railway

Railway is the easiest platform for deploying MCP servers because it supports long-running processes and WebSocket upgrades natively. Push your repository to GitHub, then create a new Railway project and connect it to your repo. Railway auto-detects Node.js and runs npm start. Set your start command to node dist/http.js and add any required environment variables (like API keys) in the Railway dashboard under Variables. Railway assigns a public URL automatically.

typescript
1// package.json — add start script
2{
3 "scripts": {
4 "build": "tsc",
5 "start": "node dist/http.js"
6 }
7}

Expected result: Your MCP server is live at https://your-project.up.railway.app with the /mcp endpoint accessible.

4

Deploy to Render

Render is another excellent option with a generous free tier for web services. Create a new Web Service on Render, connect your GitHub repo, set the build command to npm install && npm run build, and the start command to node dist/http.js. Add environment variables in the Environment section. Render provides a .onrender.com subdomain automatically. Note that Render free tier services spin down after 15 minutes of inactivity, which will cause the first request to take 30-60 seconds.

typescript
1// render.yaml (optional — Infrastructure as Code)
2services:
3 - type: web
4 name: my-mcp-server
5 runtime: node
6 buildCommand: npm install && npm run build
7 startCommand: node dist/http.js
8 envVars:
9 - key: API_KEY
10 sync: false

Expected result: Your MCP server is live at https://your-project.onrender.com with the /mcp endpoint accessible.

5

Deploy to Vercel as a serverless function

Vercel works differently — it runs serverless functions, not long-running processes. This means each request is handled by a new function invocation. Streamable HTTP's stateless design supports this pattern. Create an api/mcp.ts file that exports the Express app as a Vercel function. Note that Vercel has a 10-second timeout on the free tier (60s on Pro), so tools that take longer will need a different approach.

typescript
1// api/mcp.ts — Vercel serverless function
2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4import { registerTools } from "../src/tools.js";
5
6const server = new McpServer({
7 name: "vercel-mcp-server",
8 version: "1.0.0",
9});
10
11registerTools(server);
12
13const app = createMcpExpressApp(server);
14
15export default app;

Expected result: Your MCP server is deployed as a Vercel serverless function at https://your-project.vercel.app/api/mcp.

6

Connect your MCP host to the remote server

Update your Claude Desktop or Cursor configuration to point to the remote URL instead of a local command. For Claude Desktop, edit ~/Library/Application Support/Claude/claude_desktop_config.json. For Cursor, edit .cursor/mcp.json in your project root. Replace the command/args configuration with a url field pointing to your deployed endpoint. If your deployment requires complex infrastructure decisions or you want help choosing the right platform, the RapidDev engineering team can assist with architecture planning.

typescript
1// Claude Desktop — remote server config
2{
3 "mcpServers": {
4 "my-remote-server": {
5 "url": "https://your-project.up.railway.app/mcp"
6 }
7 }
8}
9
10// Cursor — remote server config (.cursor/mcp.json)
11{
12 "mcpServers": {
13 "my-remote-server": {
14 "url": "https://your-project.up.railway.app/mcp"
15 }
16 }
17}

Expected result: Your MCP host connects to the remote server and you can use all tools and resources as if they were running locally.

Complete working example

src/http.ts
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
3import { z } from "zod";
4
5const server = new McpServer({
6 name: "my-remote-mcp-server",
7 version: "1.0.0",
8});
9
10// Example tool — replace with your own
11server.tool(
12 "lookup",
13 "Look up information by ID",
14 { id: z.string().describe("The ID to look up") },
15 async ({ id }) => {
16 // Replace with your actual logic
17 const data = { id, name: "Example", status: "active" };
18 return {
19 content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
20 };
21 }
22);
23
24// Create Express app with Streamable HTTP transport
25const app = createMcpExpressApp(server);
26
27// Health check endpoint
28app.get("/", (_req, res) => {
29 res.json({
30 status: "ok",
31 server: "my-remote-mcp-server",
32 version: "1.0.0",
33 transport: "Streamable HTTP",
34 });
35});
36
37const PORT = parseInt(process.env.PORT || "3000", 10);
38app.listen(PORT, () => {
39 console.error(`MCP server listening on port ${PORT}`);
40 console.error(`MCP endpoint: http://localhost:${PORT}/mcp`);
41 console.error(`Health check: http://localhost:${PORT}/`);
42});

Common mistakes when deploying a remote MCP server

Why it's a problem: Deploying a stdio server directly to the cloud without switching transports

How to avoid: Stdio uses subprocess pipes, which require the server to run on the same machine as the host. You must switch to Streamable HTTP transport for remote deployment.

Why it's a problem: Hardcoding API keys in server code instead of using environment variables

How to avoid: Use process.env.API_KEY and set the value in your hosting platform's environment variable configuration. Never commit secrets to your repository.

Why it's a problem: Using Vercel free tier for long-running MCP tools

How to avoid: Vercel free tier has a 10-second function timeout. If your tools call slow APIs, use Railway or Render which support long-running processes.

Why it's a problem: Forgetting to add a health check endpoint

How to avoid: Cloud platforms need a health check endpoint to verify your server is running. Add a simple GET / route that returns a 200 status.

Why it's a problem: Not setting the PORT environment variable

How to avoid: Cloud platforms inject the PORT environment variable. Always use process.env.PORT instead of a hardcoded port number.

Best practices

  • Keep tool definitions in a shared module that both stdio and HTTP entry points can import
  • Always use process.env.PORT for the listening port — cloud platforms set this automatically
  • Add a health check endpoint at / or /health for load balancer and uptime monitoring
  • Store all secrets as environment variables on your hosting platform, never in code
  • Use console.error() for all logging — never console.log() which can interfere with protocols
  • Set up a staging environment to test changes before deploying to production
  • Monitor your server logs through your hosting platform's dashboard
  • Consider adding rate limiting middleware for public-facing MCP servers

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have an MCP server that uses stdio transport. Help me convert it to Streamable HTTP transport using Express and deploy it to [Railway/Render/Vercel]. Show me the complete code changes needed and the deployment configuration.

MCP Prompt

Convert my existing MCP server to use Streamable HTTP transport. Create an HTTP entry point using createMcpExpressApp that includes a health check endpoint, reads PORT from environment variables, and logs to stderr. Keep the existing tool definitions unchanged.

Frequently asked questions

Can I deploy an MCP server to AWS Lambda?

Yes, since Streamable HTTP supports stateless request-response patterns, it works with serverless platforms including AWS Lambda. Wrap your Express app with a Lambda adapter like @vendia/serverless-express. Be aware of Lambda's timeout limits (15 minutes max, but API Gateway has a 30-second limit).

Do I need a custom domain for my remote MCP server?

No, you can use the default URL provided by your hosting platform (e.g., your-project.up.railway.app). Custom domains are optional and mainly useful for branding or organizational requirements.

How do I handle authentication for a remote MCP server?

Streamable HTTP supports OAuth 2.1 authentication as defined in the MCP specification. For simpler setups, you can validate a shared secret passed as a header. Avoid exposing MCP servers without any authentication on the public internet.

What happens if my Render free tier service spins down?

The first request after a spin-down will take 30-60 seconds while Render cold-starts your service. This may cause MCP client timeouts. Upgrade to a paid plan or use Railway to keep the server running continuously.

Can multiple users share the same remote MCP server?

Yes, Streamable HTTP supports multiple concurrent sessions via the Mcp-Session-Id header. Each connected client gets its own session, and the server handles them independently.

How much does it cost to run an MCP server in the cloud?

Railway Hobby is $5/month, Render free tier is $0 (with spin-down), and Vercel free tier handles low-traffic servers at no cost. For most personal and small team use cases, costs are under $10/month.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.