−25%

auf Windows bei Jahreszahlung, bis 31.10. Zu den Tarifen

EQVPS
Loslegen

VPS für ein MCP-Gateway: mehrere MCP-Server hinter einer Adresse und einem Token

Ihr Team nutzt drei selbst gehostete MCP-Server, und jeder hat drei URLs und drei Geheimnisse. Legen Sie sie hinter eine HTTPS-Adresse auf einem kleinen VPS: Caddy prüft ein Bearer-Token, routet nach Pfad und hält die Server selbst vom Internet fern. Eine getestete Konfiguration.

Selbst gehostete MCP-Server vermehren sich. Erst gibt es einen für die interne Dokumentation, dann einen für die Datenbank, dann schreibt jemand einen kleinen für die Deploy-Pipeline. Jeder bekommt seinen eigenen Port, seine eigene URL und sein eigenes Geheimnis, und jede Person im Team trägt alle drei in ihre Client-Konfiguration ein. Ein Token zu rotieren heißt, allen Bescheid zu sagen.

Ein Gateway macht daraus eine Adresse und ein Token. Clients verbinden sich mit https://mcp.example.com/docs/mcp, .../db/mcp und so weiter. Das Gateway prüft das Token, schickt jeden Pfad an den richtigen Server auf localhost, und die Server selbst sind nie offen. Dafür braucht es sehr wenig: einen kleinen VPS mit eigener IPv4 und Caddy.

Das folgende Setup haben wir auf einem Testserver gebaut und mit echten Clients geprüft, bevor wir es aufgeschrieben haben.

Was Sie brauchen

  • Einen VPS mit öffentlicher IPv4, damit Clients Port 443 erreichen. Nano-IP reicht, solange Ihre MCP-Server selbst keine schwere Arbeit leisten.
  • Eine Domain mit einem A-Record auf den Server, hier mcp.example.com.
  • Ubuntu 24.04 oder Debian 12/13. Caddy ist in den Standard-Repositorys beider Systeme.

1. MCP-Server auf localhost betreiben

Jeder Server läuft als systemd-Dienst. Hier nutzen wir den Referenzserver server-everything zweimal, damit das Beispiel in sich geschlossen ist; setzen Sie Ihre eigenen ein. Er braucht Node.js 20 oder neuer:

mkdir -p /opt/mcp && cd /opt/mcp
npm install @modelcontextprotocol/server-everything
# /etc/systemd/system/mcp-everything-1.service
[Unit]
Description=MCP server everything #1
After=network.target

[Service]
WorkingDirectory=/opt/mcp
Environment=PORT=3101
ExecStart=/opt/mcp/node_modules/.bin/mcp-server-everything streamableHttp
Restart=always
User=nobody

[Install]
WantedBy=multi-user.target

Eine zweite Kopie nutzt Port 3102. Dann:

systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2

Beim Testen ist uns aufgefallen: Dieser Server lauscht auf allen Interfaces, nicht nur auf 127.0.0.1, und viele andere tun das auch. Schritt 3 kümmert sich darum.

2. Caddy: erst das Token prüfen, dann nach Pfad routen

Erzeugen Sie ein Token und geben Sie es Caddy über eine Umgebungsdatei:

apt install -y caddy
echo "MCP_GATEWAY_TOKEN=$(openssl rand -hex 32)" > /etc/caddy/gateway.env
chmod 600 /etc/caddy/gateway.env
mkdir -p /etc/systemd/system/caddy.service.d
printf '[Service]\nEnvironmentFile=/etc/caddy/gateway.env\n' > /etc/systemd/system/caddy.service.d/env.conf
systemctl daemon-reload

Die Caddyfile:

mcp.example.com {
	route {
		@unauthorized not header Authorization "Bearer {$MCP_GATEWAY_TOKEN}"
		respond @unauthorized 401

		handle_path /everything/* {
			reverse_proxy 127.0.0.1:3101 {
				flush_interval -1
			}
		}
		handle_path /tools2/* {
			reverse_proxy 127.0.0.1:3102 {
				flush_interval -1
			}
		}
		respond 404
	}
}
caddy validate --config /etc/caddy/Caddyfile && systemctl restart caddy

Das Zertifikat für mcp.example.com holt sich Caddy selbst.

Der route-Block ist keine Dekoration. Unsere erste Version hatte die Token-Prüfung und die handle_path-Blöcke nebeneinander auf oberster Ebene, und Anfragen ohne Token gingen mit 200 durch. Caddy sortiert Direktiven in seiner eigenen Reihenfolge, und handle_path läuft vor respond. Innerhalb von route laufen sie in der geschriebenen Reihenfolge. Wenn Sie diese Konfiguration anpassen, testen Sie zuerst den Fall „ohne Token“.

handle_path entfernt das Präfix, /everything/mcp kommt beim Server also als /mcp an. flush_interval -1 reicht gestreamte Events sofort durch.

3. Alles schließen außer SSH und HTTPS

apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable

Danach waren die Ports 3101 und 3102 in unserem Test von außerhalb des Servers nicht erreichbar, während 22 und 443 antworteten. Port 80 bleibt nur offen, damit Caddy Zertifikate holen und erneuern und auf HTTPS umleiten kann.

4. Prüfen

TOKEN=$(cut -d= -f2 /etc/caddy/gateway.env)
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"1"}}}'
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"

Die erste Anfrage sollte 401 ausgeben, die zweite 200. In unserem Durchlauf bekamen ein falsches und ein abgeschnittenes Token beide 401, und ein unbekannter Pfad bekam selbst mit richtigem Token 404.

Dann von einem Client aus, zum Beispiel Claude Code:

claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
  --header "Authorization: Bearer $TOKEN"
claude mcp list

Beide Server zeigten ✔ Connected. Derselbe Client mit falschem Token zeigte ✘ Failed to connect. Mehr zur Client-Einrichtung in EQVPS MCP mit Claude Code und Codex verbinden.

Den nächsten Server hinzufügen

Starten Sie ihn auf einem neuen localhost-Port, fügen Sie einen handle_path-Block hinzu, laden Sie Caddy neu. Bei niemandem ändert sich die Konfiguration, außer um eine neue URL unter demselben Host mit demselben Token.

Grenzen, die man kennen sollte

  • Ein Token für alle ist einfach, und es zu rotieren ist eine Datei. Sie können aber niemandem einzeln den Zugang entziehen, ohne ihn für alle zu ändern. Für Zugriff pro Nutzer setzen Sie einen Auth-Proxy davor oder geben jeder Person eine eigene Route und ein eigenes Token.
  • Das Gateway macht einen Server nicht sicher. Ein MCP-Server, der Shell-Befehle ausführt, ist für jeden mit dem Token weiterhin eine Shell. Halten Sie Tokens aus geteilten Konfigurationen und aus git heraus.
  • Dimensionieren Sie nach den Servern, nicht nach dem Gateway. Caddy selbst braucht ein paar Dutzend Megabyte. Wenn einer Ihrer Server ein lokales Modell betreibt oder ein großes Repository indiziert, bestimmt er den Tarif, nicht der Proxy.

Nur ein MCP-Server? VPS für MCP-Server behandelt diesen einfacheren Fall. Sollen Ihre Agenten auch den Server verwalten, auf dem sie leben? Das kann der EQVPS MCP-Server (oder die REST API, wenn Sie lieber skripten), und die Tarife mit eigener IPv4 beginnen bei $8 im Monat.

FAQ

Warum MCP-Server überhaupt hinter ein Gateway legen?

Eine Adresse und ein Token pro Team statt je eines pro Server, TLS an einer Stelle, und die Server selbst lauschen nur auf localhost. Ein vierter Server ist dann eine Drei-Zeilen-Änderung am Gateway, kein neues Geheimnis für alle.

Welchen Tarif brauche ich?

Einen Tarif mit eigener IPv4, weil Clients die Ports 80 und 443 erreichen müssen. Nano-IP ($8/Monat, 2 vCPU, 1 GB) reicht für ein Gateway und ein paar leichte MCP-Server. Erledigt ein Server selbst schwere Arbeit, richten Sie die Größe danach.

Funktioniert Streaming durch Caddy?

Ja. Streamable HTTP nutzt normale POST-Anfragen plus Server-Sent Events. Setzen Sie flush_interval -1 am Reverse Proxy, damit Events sofort weitergereicht statt gepuffert werden.

Ist ein gemeinsames Token sicher genug?

Für ein kleines Team ist es ein vernünftiger Anfang: Das Token verlässt HTTPS nie, und Sie rotieren es, indem Sie eine Datei ändern. Wenn Sie Zugriff pro Person oder Widerruf brauchen, setzen Sie eine echte Auth-Schicht davor. Das gemeinsame Token ist die Untergrenze, nicht die Obergrenze.

Läuft das auf einem NAT-Tarif?

Nicht als öffentliches Gateway. Ein NAT-Tarif hat einen weitergeleiteten Port für SSH und sonst nichts eingehend, Clients erreichen 443 also nicht. Es funktioniert nur, wenn alle Clients auf demselben Server laufen.

Kommentare

Noch keine Kommentare. Sei der Erste.

Kommentar hinterlassen

Kommentare werden vor der Anzeige moderiert.