−25%

på Windows vid årsbetalning, till 31/10. Till paketen

EQVPS
Kom igång

VPS för en MCP-gateway: flera MCP-servrar bakom en adress och en token

Ditt team använder tre egenhostade MCP-servrar och alla har tre URL:er och tre hemligheter. Lägg dem bakom en HTTPS-adress på en liten VPS: Caddy kontrollerar en bearer-token, routar efter sökväg och håller själva servrarna borta från internet. En testad konfiguration.

Egenhostade MCP-servrar förökar sig. Först finns en för den interna dokumentationen, sedan en för databasen, sedan skriver någon en liten för driftsättningspipelinen. Varje server får egen port, egen URL och egen hemlighet, och alla i teamet klistrar in alla tre i sin klientkonfiguration. Att byta en token betyder att meddela alla.

En gateway gör det till en adress och en token. Klienterna ansluter till https://mcp.example.com/docs/mcp, .../db/mcp och så vidare. Gatewayen kontrollerar token, skickar varje sökväg till rätt server på localhost, och själva servrarna exponeras aldrig. Det behövs väldigt lite: en liten VPS med egen IPv4 och Caddy.

Vi byggde upplägget nedan på en testserver och kontrollerade det med riktiga klienter innan vi skrev det här.

Det här behöver du

  • En VPS med publik IPv4 så att klienterna når 443. Nano-IP räcker, om inte dina MCP-servrar själva gör tungt arbete.
  • En domän med en A-post som pekar på servern, här mcp.example.com.
  • Ubuntu 24.04 eller Debian 12/13. Caddy finns i standardförråden för båda.

1. Kör MCP-servrarna på localhost

Varje server körs som en systemd-tjänst. Här använder vi referensservern server-everything två gånger så att exemplet är fristående; byt ut mot dina egna. Den kräver Node.js 20 eller nyare:

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

En andra kopia använder port 3102. Sedan:

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

En sak vi märkte under testerna: den här servern lyssnar på alla gränssnitt, inte bara på 127.0.0.1, och många andra gör likadant. Steg 3 tar hand om det.

2. Caddy: kontrollera token, routa sedan efter sökväg

Generera en token och ge den till Caddy via en miljöfil:

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

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

Caddy hämtar certifikatet för mcp.example.com själv.

route-blocket är ingen dekoration. Vår första version hade tokenkontrollen och handle_path-blocken bredvid varandra på toppnivå, och anrop utan token gick rakt igenom med 200. Caddy sorterar direktiv i sin egen ordning, och handle_path körs före respond. Inuti route körs de i den ordning de står. Anpassar du konfigurationen, testa fallet ”utan token” först.

handle_path tar bort prefixet, så /everything/mcp når servern som /mcp. flush_interval -1 skickar vidare strömmade händelser när de kommer.

3. Stäng allt utom SSH och HTTPS

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

Därefter gick port 3101 och 3102 inte att nå utifrån i vårt test, medan 22 och 443 svarade. Port 80 är bara öppen så att Caddy kan hämta och förnya certifikat och omdirigera till HTTPS.

4. Kontrollera

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"

Det första anropet ska skriva ut 401 och det andra 200. I vår körning fick en felaktig och en avkortad token båda 401, och en okänd sökväg fick 404 även med rätt token.

Sedan från en klient, till exempel Claude Code:

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

Båda servrarna visade ✔ Connected. Samma klient med fel token visade ✘ Failed to connect. Mer om klientinställningar i Anslut EQVPS MCP till Claude Code och Codex.

Lägga till nästa server

Starta den på en ny localhost-port, lägg till ett handle_path-block och ladda om Caddy. Ingens konfiguration ändras, förutom en ny URL under samma värd och med samma token.

Begränsningar värda att känna till

  • En token för alla är enkelt och att byta den är en fil, men du kan inte återkalla en person utan att byta för alla. För åtkomst per användare, lägg en autentiseringsproxy framför eller ge varje person en egen route och token.
  • Gatewayen gör inte en server säker. En MCP-server som kör skalkommandon är fortfarande ett skal för den som har token. Håll tokens borta från delade konfigurationer och git.
  • Dimensionera efter servrarna, inte gatewayen. Caddy själv använder några tiotal megabyte. Kör en av dina servrar en lokal modell eller indexerar ett stort repo, är det den som avgör planen, inte proxyn.

Bara en MCP-server? VPS för MCP-servrar tar upp det enklare fallet. Vill du att dina agenter också sköter servern de bor på? Det gör EQVPS MCP-server (eller REST API om du hellre skriptar), och planer med egen IPv4 börjar på $8 i månaden.

FAQ

Varför lägga MCP-servrar bakom en gateway alls?

En adress och en token per team i stället för en per server, TLS på ett ställe, och själva servrarna lyssnar bara på localhost. Att lägga till en fjärde server blir en ändring på tre rader i gatewayen, inte en ny hemlighet för alla.

Vilken plan behöver jag?

En plan med egen IPv4, eftersom klienterna måste nå port 80 och 443. Nano-IP ($8/mån, 2 vCPU, 1 GB) räcker för en gateway och några lätta MCP-servrar. Gör en server tungt arbete själv, välj storlek efter den.

Fungerar streaming genom Caddy?

Ja. Streamable HTTP använder vanliga POST-anrop plus server-sent events. Sätt flush_interval -1 på reverse proxyn så att händelser skickas vidare direkt i stället för att buffras.

Är en delad token säker nog?

För ett litet team är det en rimlig start: token lämnar aldrig HTTPS och byts genom att ändra en fil. När du behöver åtkomst per person eller återkallelse, lägg ett riktigt autentiseringslager framför. Den delade token är golvet, inte taket.

Går det på en NAT-plan?

Inte som publik gateway. En NAT-plan har en vidarebefordrad port för SSH och inget annat inkommande, så klienterna når inte 443. Det fungerar bara om alla klienter finns på samma server.

Kommentarer

Inga kommentarer än. Bli först.

Lämna en kommentar

Kommentarer modereras innan de visas.