Key takeaways
- Change one line (
mcp.run(transport="streamable-http")) to make any local MCP server remote - Users connect with a URL: no pip install or local dependencies needed
- Deploy with Docker to Railway, any cloud provider, or a VPS
What is a remote MCP server?
Most MCP servers run locally: users install a package, and their AI assistant launches it as a subprocess. A remote MCP server runs on your infrastructure instead. Users connect to a URL.
New to MCP? Read What is an MCP server? first. Before building a remote server, you may want to start with How to build an MCP server to understand the local approach.
{
"mcpServers": {
"your-server": {
"url": "https://your-server.com/mcp"
}
}
}
No pip install. No npx. No local dependencies. The user pastes a URL and they are connected.
When to use remote vs local
| Local (pip/npx) | Remote (URL) | |
|---|---|---|
| User setup | Install package + config | Paste URL |
| Code visibility | User has the code | Code stays on your server |
| Infra cost | None (runs on user's machine) | You pay for hosting |
| Best for | Open source tools, utilities | Proprietary data, AI models, premium services |
| Piracy risk | Low (license key) | None (code never distributed) |
| Offline use | Works offline | Requires internet |
Use local for open-source tools where distribution is the goal. Use remote when you need full control over the code, data, or access.
Building a remote server
The transport difference
Local MCP servers use stdio: they read from stdin and write to stdout. Your AI assistant launches them as a subprocess.
Remote MCP servers use Streamable HTTP: they listen on an HTTP port and handle requests over the network. The older SSE transport is deprecated as of the 2026-07-28 MCP specification, so new servers should not use it.
In code, the only change is the transport:
Local (default):
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("my-server")
@mcp.tool()
def my_tool(query: str) -> str:
return do_thing(query)
if __name__ == "__main__":
mcp.run() # stdio by default
Remote:
import os
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("my-server")
@mcp.tool()
def my_tool(query: str) -> str:
return do_thing(query)
if __name__ == "__main__":
port = int(os.environ.get("PORT", "8000"))
mcp.run(transport="streamable-http", host="0.0.0.0", port=port)
One line changed. Everything else: tool definitions, business logic, error handling: stays the same.
These examples use version 2 of the Python SDK (pip install "mcp>=2"), which serves both current clients and older ones that still open a session. In version 1 the class is FastMCP (from mcp.server.fastmcp import FastMCP) and the host and port go in the constructor instead of run(). Version 1 code that imports FastMCP fails at import on version 2, so pin mcp<2 in your dependencies until you migrate.
With host="0.0.0.0" the server accepts any Host header. To restrict it to your own domain, pass transport_security:
from mcp.server.transport_security import TransportSecuritySettings
security = TransportSecuritySettings(allowed_hosts=["my-server.example.com"])
mcp.run(transport="streamable-http", host="0.0.0.0", port=port, transport_security=security)
Requests with any other Host header are rejected with a 421.
Scaffold with MCP Creator
The fastest way to set up a remote server. Both mcp-creator-typescript and mcp-creator-python support hosting="remote":
scaffold_server(
package_name="my-remote-mcp",
description="A remote hosted MCP server",
tools=["my_tool"],
hosting="remote"
)
This generates:
- Server entry point with the Streamable HTTP transport
Dockerfilefor containerized deployment.env.examplewithPORT=8000README.mdwith remote config examples
Dockerfile
A minimal Dockerfile for a Python MCP server:
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -e .
EXPOSE 8000
ENV PORT=8000
CMD ["python", "-m", "my_mcp_server.server"]
Build and test locally:
docker build -t my-mcp-server .
docker run -p 8000:8000 my-mcp-server
Then point your AI assistant at http://localhost:8000/mcp to test.
Deployment options
Any platform that runs Docker containers works. Two common paths:
Railway (simplest): Push your code to GitHub, connect the repo in Railway, and it auto-detects the Dockerfile and deploys. You get a URL like https://my-server.up.railway.app. Set environment variables in their dashboard.
Any cloud or VPS: Deploy the Docker container to AWS (ECS/Lambda), GCP (Cloud Run), Fly.io, or a $5/month VPS with docker compose up -d. Use a reverse proxy (nginx, Caddy) for HTTPS.
Authentication
Remote servers need authentication. Users should not be able to connect without credentials.
API key in headers
The simplest approach: users include an API key in their MCP config:
{
"mcpServers": {
"your-server": {
"url": "https://your-server.com/mcp",
"headers": {
"Authorization": "Bearer mcp_live_abc123..."
}
}
}
}
On your server, check the header on each request. The MCP SDK does not handle auth for you: add middleware or check the header in your tool handlers.
License key verification
If you are selling through MCP Marketplace, the buyer's license key arrives in that Authorization header (Bearer mcp_live_...).
From version 1.2.0, the mcp-marketplace-license SDK verifies it. Take the key from the header with license_key_from_header and pass it to verify_license(slug=..., key=...). Always pass key on a remote server: without it, verify_license reads MCP_LICENSE_KEY from your server's own environment, not the buyer's request.
Rate-limit requests that carry no valid key on your own server. Every check you forward to the license server counts against your server's address.
See the license key guide for a complete example.
Combining remote + paid
For a premium remote server: paid access, code never distributed:
scaffold_server(
package_name="my-premium-mcp",
tools=["free_tool", "premium_tool"],
paid=true,
paid_tools=["premium_tool"],
hosting="remote"
)
This generates a server with:
- Streamable HTTP transport for remote hosting
- License verification on premium tools
- Dockerfile for deployment
- Config examples with auth headers
This is the most secure monetization model. Users never get your code, and access is controlled per-request.
Things to consider
Latency. Local servers respond in milliseconds. Remote servers add network round-trip time. For most tools this is not noticeable, but for tools that the AI calls many times in a loop, the latency adds up.
Uptime. Your server is now a service. If it goes down, your users' tools stop working. Use health checks, monitoring, and redundancy for production servers.
Costs. You are paying for hosting. Price your server accordingly: remote servers typically justify subscription pricing ($10-50+/mo) rather than one-time fees.
Rate limiting. Without rate limiting, a single user can overwhelm your server. Add per-key rate limits, especially for expensive operations.
Next steps
- How to monetize your MCP server: the full guide to both local and remote pricing models
- License keys for MCP servers: how license verification works
- Free vs Pro: the freemium strategy for MCP servers
- MCP Creator: scaffold servers with AI in TypeScript or Python
- How to install an MCP server: how users connect to your remote server from their AI client