−25%

på Windows ved årlig betaling, til 31/10. Se pakkerne

EQVPS
Kom i gang

VPS til en MCP-gateway: flere MCP-servere bag én adresse og én token

Dit team bruger tre selvhostede MCP-servere, og alle har tre URL'er og tre hemmeligheder. Sæt dem bag én HTTPS-adresse på en lille VPS: Caddy tjekker en bearer-token, router efter sti og holder selve serverne væk fra internettet. En testet konfiguration.

Selvhostede MCP-servere formerer sig. Først er der én til den interne dokumentation, så én til databasen, så skriver nogen en lille til udrulningspipelinen. Hver får sin egen port, sin egen URL og sin egen hemmelighed, og alle i teamet indsætter alle tre i deres klientkonfiguration. At skifte en token betyder at skrive til alle.

En gateway gør det til én adresse og én token. Klienterne forbinder til https://mcp.example.com/docs/mcp, .../db/mcp og så videre. Gatewayen tjekker token, sender hver sti til den rigtige server på localhost, og selve serverne er aldrig eksponeret. Der skal meget lidt til: en lille VPS med egen IPv4 og Caddy.

Vi byggede opsætningen nedenfor på en testserver og tjekkede den med rigtige klienter, før vi skrev det her.

Det skal du bruge

  • En VPS med offentlig IPv4, så klienterne kan nå 443. Nano-IP er nok, medmindre dine MCP-servere selv laver tungt arbejde.
  • Et domæne med en A-record, der peger på serveren, her mcp.example.com.
  • Ubuntu 24.04 eller Debian 12/13. Caddy findes i standardarkiverne for begge.

1. Kør MCP-serverne på localhost

Hver server kører som en systemd-tjeneste. Her bruger vi referenceserveren server-everything to gange, så eksemplet er selvstændigt; brug dine egne. Den kræver Node.js 20 eller nyere:

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 anden kopi bruger port 3102. Derefter:

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

Noget vi bemærkede under testen: denne server lytter på alle interfaces, ikke kun 127.0.0.1, og mange andre gør det samme. Trin 3 tager sig af det.

2. Caddy: tjek token, og route derefter efter sti

Generér en token og giv den til 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 henter selv certifikatet til mcp.example.com.

route-blokken er ikke pynt. Vores første version havde tokentjekket og handle_path-blokkene side om side på øverste niveau, og forespørgsler uden token gik lige igennem med 200. Caddy sorterer direktiver i sin egen rækkefølge, og handle_path kører før respond. Inde i route kører de i den skrevne rækkefølge. Hvis du tilpasser konfigurationen, så test tilfældet „uden token“ først.

handle_path fjerner præfikset, så /everything/mcp når serveren som /mcp. flush_interval -1 sender streamede hændelser videre, efterhånden som de kommer.

3. Luk alt undtagen SSH og HTTPS

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

Derefter kunne port 3101 og 3102 i vores test ikke nås uden for serveren, mens 22 og 443 svarede. Port 80 er kun åben, så Caddy kan hente og forny certifikater og omdirigere til HTTPS.

4. Tjek det

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"

Den første forespørgsel skal skrive 401, og den anden 200. I vores kørsel fik en forkert og en afkortet token begge 401, og en ukendt sti fik 404 selv med den rigtige token.

Derefter fra en klient, for eksempel Claude Code:

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

Begge servere viste ✔ Connected. Samme klient med forkert token viste ✘ Failed to connect. Mere om klientopsætning i Forbind EQVPS MCP til Claude Code og Codex.

Tilføj den næste server

Start den på en ny localhost-port, tilføj én handle_path-blok og genindlæs Caddy. Ingens konfiguration ændrer sig, ud over en ny URL under samme vært og med samme token.

Begrænsninger værd at kende

  • Én token til alle er enkelt, og at skifte den er én fil, men du kan ikke fjerne én persons adgang uden at ændre den for alle. Til adgang pr. bruger, sæt en godkendelsesproxy foran eller giv hver person sin egen route og token.
  • Gatewayen gør ikke en server sikker. En MCP-server, der kører shell-kommandoer, er stadig en shell for den, der har token. Hold tokens ude af delte konfigurationer og git.
  • Vælg størrelse efter serverne, ikke gatewayen. Caddy selv bruger nogle få titals megabyte. Kører en af dine servere en lokal model eller indekserer et stort repo, er det den, der bestemmer planen, ikke proxyen.

Kun én MCP-server? VPS til MCP-servere dækker det enklere tilfælde. Vil du have, at dine agenter også passer den server, de bor på? Det klarer EQVPS MCP-serveren (eller REST API'et, hvis du hellere vil scripte), og planer med egen IPv4 starter ved $8 om måneden.

FAQ

Hvorfor overhovedet sætte MCP-servere bag en gateway?

Én adresse og én token pr. team i stedet for én pr. server, TLS ét sted, og selve serverne lytter kun på localhost. At tilføje en fjerde server bliver en ændring på tre linjer i gatewayen, ikke en ny hemmelighed til alle.

Hvilken plan har jeg brug for?

En plan med egen IPv4, fordi klienterne skal kunne nå port 80 og 443. Nano-IP ($8/md., 2 vCPU, 1 GB) er nok til en gateway og nogle lette MCP-servere. Laver en server selv tungt arbejde, så vælg størrelse efter den.

Virker streaming gennem Caddy?

Ja. Streamable HTTP bruger almindelige POST-kald plus server-sent events. Sæt flush_interval -1 på reverse proxyen, så hændelser sendes videre med det samme i stedet for at blive bufferet.

Er én delt token sikker nok?

For et lille team er det en fornuftig start: token forlader aldrig HTTPS, og du skifter den ved at ændre én fil. Når du har brug for adgang pr. person eller tilbagekaldelse, så sæt et rigtigt godkendelseslag foran. Den delte token er gulvet, ikke loftet.

Kan det køre på en NAT-plan?

Ikke som offentlig gateway. En NAT-plan har én videresendt port til SSH og intet andet indgående, så klienterne kan ikke nå 443. Det virker kun, hvis alle klienter er på samme server.

Kommentarer

Ingen kommentarer endnu. Vær den første.

Skriv en kommentar

Kommentarer modereres, før de vises.