Zelf gehoste MCP-servers vermenigvuldigen zich. Eerst is er een voor de interne documentatie, dan een voor de database, dan schrijft iemand een kleintje voor de deploy-pipeline. Elk krijgt een eigen poort, een eigen URL en een eigen geheim, en iedereen in het team plakt ze alle drie in zijn clientconfiguratie. Een token roteren betekent iedereen een bericht sturen.
Een gateway maakt daar één adres en één token van. Clients verbinden met https://mcp.example.com/docs/mcp, .../db/mcp enzovoort. De gateway controleert het token, stuurt elk pad naar de juiste server op localhost, en de servers zelf staan nooit open. Er is heel weinig voor nodig: een kleine VPS met eigen IPv4 en Caddy.
We hebben de opzet hieronder op een testserver gebouwd en met echte clients gecontroleerd voordat we dit schreven.
Wat je nodig hebt
- Een VPS met een publiek IPv4-adres, zodat clients poort 443 bereiken. Nano-IP is genoeg, tenzij je MCP-servers zelf zwaar werk doen.
- Een domein met een A-record naar de server, hier
mcp.example.com. - Ubuntu 24.04 of Debian 12/13. Caddy zit in de standaardrepository's van beide.
1. De MCP-servers op localhost draaien
Elke server draait als systemd-service. Hier gebruiken we de referentieserver server-everything twee keer, zodat het voorbeeld op zichzelf staat; vervang hem door je eigen servers. Hij heeft Node.js 20 of nieuwer nodig:
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
Een tweede kopie gebruikt poort 3102. Daarna:
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
Iets wat we tijdens het testen zagen: deze server luistert op alle interfaces, niet alleen op 127.0.0.1, en veel andere doen dat ook. Stap 3 regelt dat.
2. Caddy: eerst het token controleren, dan routeren op pad
Genereer een token en geef het aan Caddy via een omgevingsbestand:
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
De 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 haalt het certificaat voor mcp.example.com zelf op.
Het route-blok is geen versiering. Onze eerste versie had de tokencontrole en de handle_path-blokken naast elkaar op het hoogste niveau, en verzoeken zonder token gingen gewoon door met 200. Caddy sorteert directives in zijn eigen volgorde, en handle_path draait vóór respond. Binnen route draaien ze in de geschreven volgorde. Pas je deze configuratie aan, test dan eerst het geval „zonder token”.
handle_path haalt het voorvoegsel weg, dus /everything/mcp komt bij de server aan als /mcp. flush_interval -1 geeft gestreamde events direct door.
3. Alles sluiten behalve SSH en HTTPS
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
Daarna waren poort 3101 en 3102 in onze test van buiten de server onbereikbaar, terwijl 22 en 443 antwoordden. Poort 80 blijft alleen open zodat Caddy certificaten kan ophalen en vernieuwen en naar HTTPS kan doorsturen.
4. Controleren
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"
Het eerste verzoek hoort 401 te geven en het tweede 200. In onze run kregen een verkeerd en een afgekapt token allebei 401, en een onbekend pad kreeg 404, zelfs met het juiste token.
Daarna vanaf een client, bijvoorbeeld Claude Code:
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
Beide servers toonden ✔ Connected. Dezelfde client met een verkeerd token toonde ✘ Failed to connect. Meer over het instellen van clients in EQVPS MCP koppelen aan Claude Code en Codex.
De volgende server toevoegen
Start hem op een nieuwe localhost-poort, voeg één handle_path-blok toe en herlaad Caddy. Niemands configuratie verandert, behalve een nieuwe URL onder dezelfde host en met hetzelfde token.
Grenzen om te kennen
- Eén token voor iedereen is eenvoudig en roteren is één bestand, maar je kunt niet één persoon de toegang ontnemen zonder het voor iedereen te wijzigen. Voor toegang per gebruiker zet je er een auth-proxy voor of geef je iedereen een eigen route en token.
- De gateway maakt een server niet veilig. Een MCP-server die shellcommando's uitvoert, blijft een shell voor iedereen met het token. Houd tokens uit gedeelde configuraties en uit git.
- Kies de grootte op de servers, niet op de gateway. Caddy zelf gebruikt een paar tientallen megabytes. Draait een van je servers een lokaal model of indexeert hij een grote repository, dan bepaalt die het pakket, niet de proxy.
Draai je maar één MCP-server? VPS voor MCP-servers behandelt dat eenvoudigere geval. Wil je dat je agents ook de server beheren waarop ze draaien? Dat doet de EQVPS MCP-server (of de REST API als je liever script), en de pakketten met eigen IPv4 beginnen bij $8 per maand.
Reacties
Nog geen reacties. Wees de eerste.