Tutorial, MCP spec 2026-07-28
Host an MCP server on a VPS. Your tools, reachable by Claude from anywhere.
Most MCP hosting guides deploy to a serverless platform, or stop before HTTPS and auth. Here’s the whole path on a server you control, tested against the official Python SDK.
What a remote MCP server needs
- TransportStreamable HTTP/mcp
- Claude on the webConnects from the cloudPublic HTTPS
- AuthOptional in the specAdd a token
A server on your laptop can't be reached by claude.ai.
Host it on a Lite 2
$4
- Always on
- Fixed IP
- HTTPS
- Claude
- Cursor
- Python
2 vCPU, 2 GB, 40 GB NVMe. Billed by the hour, paid in crypto.
The short answer
A remote MCP server is a normal HTTPS service with one endpoint, /mcp, speaking Streamable HTTP. Run it on 127.0.0.1, put Caddy in front for HTTPS and a token check, and keep it up with systemd.
Claude on the web connects from Anthropic’s cloud, so the server needs a public address: a laptop or a home network won’t do. The builder below writes every file.
MCP, the Model Context Protocol, is how Claude, Cursor and other AI apps call your tools: a database query, an internal API, a search over your files. Locally, a tool runs as a subprocess. Remotely, it’s a web service.
A VPS gives that service a fixed address, no cold starts, and your data on a machine you control.
In this guide you’ll learn
- Local or remote: which you need
- A builder for server, service, proxy and client
- Deploy step by step on Ubuntu
- Auth: a token, or OAuth
- Fixes for 421, 401 and old SSE guides
Chapter 1
Local or remote MCP server?
The MCP spec defines two transports. Which one you need depends on who connects.
| stdio (local) | Streamable HTTP (remote) | |
|---|---|---|
| How it runs | The client starts it as a subprocess | A web service on one endpoint, /mcp |
| Who can use it | Apps on the same computer | Any client that can reach the URL |
| Claude on the web | No | Yes, with a public HTTPS address |
| Auth | Credentials from the environment | Optional in the spec; add a token or OAuth |
The older HTTP+SSE transport has been deprecated since 2025; new servers should use Streamable HTTP.
Chapter 2
Build your MCP server files
Enter the domain you’ll point at the server and the client you’ll use. The token is generated in your browser and never sent anywhere.
1. ~/server/server.py
from mcp.server import MCPServer
from mcp.server.transport_security import TransportSecuritySettings
mcp = MCPServer("My tools")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
security = TransportSecuritySettings(allowed_hosts=["mcp.example.com", "mcp.example.com:*"])
app = mcp.streamable_http_app(transport_security=security)2. /etc/systemd/system/mcp.service
[Unit] Description=MCP server After=network.target [Service] User=mcp WorkingDirectory=/home/mcp/server ExecStart=/home/mcp/server/.venv/bin/uvicorn server:app --host 127.0.0.1 --port 8000 --proxy-headers --forwarded-allow-ips=127.0.0.1 Restart=always [Install] WantedBy=multi-user.target
3. /etc/caddy/Caddyfile
mcp.example.com {
@nokey not header Authorization "Bearer YOUR_TOKEN"
respond @nokey 401
reverse_proxy 127.0.0.1:8000
}4. Connect
claude mcp add --transport http my-tools https://mcp.example.com/mcp --header "Authorization: Bearer YOUR_TOKEN"
Chapter 3
Deploy it on Ubuntu, step by step
On a fresh Ubuntu 24.04 server, as root or with sudo.
Point your domain at the server
Add an A record for
mcp.example.comwith the server’s IP address.Install Python and Caddy
apt update && apt install -y python3-venv caddy
A user and an environment for the server
adduser --disabled-password --gecos "" mcp sudo -u mcp mkdir -p /home/mcp/server sudo -u mcp python3 -m venv /home/mcp/server/.venv sudo -u mcp /home/mcp/server/.venv/bin/pip install "mcp[cli]" uvicorn
Add the files from the builder
Save
server.pyin/home/mcp/server/, the unit as/etc/systemd/system/mcp.service, and the Caddyfile as/etc/caddy/Caddyfile.Start everything
systemctl daemon-reload systemctl enable --now mcp systemctl reload caddy
Test from your laptop
npx @modelcontextprotocol/inspector --server-url https://mcp.example.com/mcp --transport http
The Inspector needs Node 22.19 or newer. Run it on your laptop, not the server.
Then close everything except 22, 80 and 443 in your firewall. Caddy gets the HTTPS certificate on its own.
Chapter 4
Auth: a token, or OAuth?
The spec makes authorization optional. When a server uses it over HTTP, the spec’s method is OAuth 2.1, with the server publishing protected resource metadata.
| A fixed token | OAuth 2.1 | |
|---|---|---|
| What it is | A secret sent as Authorization: Bearer | Per-user sign-in through an identity provider |
| Clients that accept it | Claude Code, Claude (request headers), Cursor | Clients that support MCP OAuth |
| Good for | Your own tools, a small team | Many users, revocable per person |
| Effort | One line in the Caddyfile | An identity provider and token checks in the server |
Chapter 5
Fixing common MCP deploy errors
What goes wrong after the server works on localhost.
- 421 Misdirected Request, "Invalid Host header"How to check: The Python SDK only accepts localhost until you allowlist your domain. The builder’s server.py does that; we checked it against the official SDK.
- 401 from every requestHow to check: Caddy rejected the token. The header must be exactly Authorization: Bearer and your token, with no extra spaces.
- An old guide uses /sseHow to check: That’s the deprecated transport. Use /mcp and Streamable HTTP; Claude Code’s docs say the same.
- FastMCP or MCPServer?How to check: Version 2 of the Python SDK renamed
FastMCPtoMCPServer. Older code importsfrom mcp.server.fastmcp import FastMCP. - Tools that run commands or read filesHow to check: Anyone with the token can call them. Expose only what you’d hand that person directly.
Now it’s your move
One endpoint, HTTPS in front, a token at the door, systemd behind it. That’s a remote MCP server Claude can reach from anywhere.
Deploy a Lite 2 and paste the files above. Running agents too? See self-hosted AI agents and Claude Code on a VPS.