Serverele MCP găzduite de voi se înmulțesc. Mai întâi e unul pentru documentația internă, apoi unul pentru baza de date, apoi cineva scrie unul mic pentru pipeline-ul de deploy. Fiecare are portul lui, URL-ul lui și secretul lui, iar fiecare om din echipă le lipește pe toate trei în configurația clientului. Să schimbi un token înseamnă să scrii tuturor.
Un gateway transformă asta într-o adresă și un token. Clienții se conectează la https://mcp.example.com/docs/mcp, .../db/mcp și așa mai departe. Gatewayul verifică tokenul, trimite fiecare cale la serverul potrivit pe localhost, iar serverele nu sunt niciodată expuse. E nevoie de foarte puțin: un VPS mic cu IPv4 propriu și Caddy.
Am construit configurația de mai jos pe un server de test și am verificat-o cu clienți reali înainte să o scriem.
Ce îți trebuie
- Un VPS cu IPv4 public, ca clienții să ajungă la 443. Nano-IP ajunge, dacă serverele tale MCP nu fac ele însele muncă grea.
- Un domeniu cu un record A spre server, aici
mcp.example.com. - Ubuntu 24.04 sau Debian 12/13. Caddy e în depozitele standard ale ambelor.
1. Rulează serverele MCP pe localhost
Fiecare server rulează ca serviciu systemd. Aici folosim de două ori serverul de referință server-everything, ca exemplul să fie complet; pune-le pe ale tale. Are nevoie de Node.js 20 sau mai nou:
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
A doua copie folosește portul 3102. Apoi:
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
Un lucru observat la teste: acest server ascultă pe toate interfețele, nu doar pe 127.0.0.1, și multe altele fac la fel. Pasul 3 rezolvă asta.
2. Caddy: verifici tokenul, apoi rutezi după cale
Generează un token și dă-l lui Caddy printr-un fișier de mediu:
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 obține singur certificatul pentru mcp.example.com.
Blocul route nu e decor. Prima noastră versiune avea verificarea tokenului și blocurile handle_path una lângă alta la nivelul de sus, iar cererile fără token treceau direct cu 200. Caddy sortează directivele în propria ordine, iar handle_path rulează înainte de respond. În interiorul route rulează în ordinea scrisă. Dacă adaptezi configurația, testează mai întâi cazul „fără token”.
handle_path elimină prefixul, deci /everything/mcp ajunge la server ca /mcp. flush_interval -1 trimite evenimentele în flux pe măsură ce sosesc.
3. Închide tot în afară de SSH și HTTPS
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
După asta, în testul nostru porturile 3101 și 3102 erau inaccesibile din afara serverului, iar 22 și 443 răspundeau. Portul 80 rămâne deschis doar ca să poată Caddy obține și reînnoi certificate și să redirecționeze spre HTTPS.
4. Verifică
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"
Prima cerere trebuie să afișeze 401, a doua 200. La noi, un token greșit și unul trunchiat au primit amândouă 401, iar o cale necunoscută a primit 404 chiar și cu tokenul corect.
Apoi dintr-un client, de exemplu Claude Code:
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
Ambele servere au arătat ✔ Connected. Același client cu token greșit a arătat ✘ Failed to connect. Mai multe despre configurarea clienților în Conectează EQVPS MCP la Claude Code și Codex.
Adăugarea următorului server
Pornește-l pe un port localhost nou, adaugă un bloc handle_path și reîncarcă Caddy. Configurația nimănui nu se schimbă, în afară de un URL nou sub același host și cu același token.
Limite bune de știut
- Un token pentru toți e simplu și schimbarea lui e un fișier, dar nu poți revoca accesul unei singure persoane fără să-l schimbi pentru toți. Pentru acces per utilizator, pune în față un proxy de autentificare sau dă fiecăruia ruta și tokenul lui.
- Gatewayul nu face un server sigur. Un server MCP care rulează comenzi shell rămâne un shell pentru oricine are tokenul. Ține tokenurile departe de configurațiile comune și de git.
- Dimensionează după servere, nu după gateway. Caddy folosește câteva zeci de megabytes. Dacă unul dintre servere rulează un model local sau indexează un depozit mare, el decide planul, nu proxyul.
Ai un singur server MCP? VPS pentru servere MCP acoperă acel caz mai simplu. Vrei ca agenții tăi să administreze și serverul pe care trăiesc? Asta face serverul MCP EQVPS (sau API-ul REST, dacă preferi scripturi), iar planurile cu IPv4 propriu pornesc de la $8 pe lună.
Comentarii
Încă nu există comentarii. Fii primul.