−25%

la planurile Windows anuale, până pe 31 oct. Vezi planurile

EQVPS
Începe

VPS pentru un gateway MCP: mai multe servere MCP în spatele unei adrese și al unui token

Echipa ta folosește trei servere MCP găzduite de voi și fiecare om are trei URL-uri și trei secrete. Pune-le în spatele unei singure adrese HTTPS pe un VPS mic: Caddy verifică un bearer token, rutează după cale și ține serverele departe de internet. O configurație testată.

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ă.

Întrebări frecvente

De ce să pui serverele MCP în spatele unui gateway?

O adresă și un token pe echipă în loc de câte unul pe server, TLS într-un singur loc, iar serverele ascultă doar pe localhost. Adăugarea unui al patrulea server devine o modificare de trei rânduri în gateway, nu un secret nou pentru toți.

Ce plan îmi trebuie?

Un plan cu IPv4 propriu, pentru că clienții trebuie să ajungă la porturile 80 și 443. Nano-IP ($8/lună, 2 vCPU, 1 GB) ajunge pentru un gateway și câteva servere MCP ușoare. Dacă un server face el însuși muncă grea, alege mărimea după el.

Merge streamingul prin Caddy?

Da. Streamable HTTP folosește cereri POST obișnuite plus server-sent events. Setează flush_interval -1 la reverse proxy, ca evenimentele să treacă imediat în loc să stea în buffer.

Un token comun e suficient de sigur?

Pentru o echipă mică e un început rezonabil: tokenul nu iese niciodată din HTTPS și îl schimbi modificând un fișier. Când ai nevoie de acces per persoană sau de revocare, pune în față un strat de autentificare adevărat. Tokenul comun e podeaua, nu tavanul.

Pot face asta pe un plan NAT?

Nu ca gateway public. Un plan NAT are un port redirecționat pentru SSH și nimic altceva la intrare, deci clienții nu ajung la 443. Funcționează doar dacă toți clienții sunt pe același server.

Comentarii

Încă nu există comentarii. Fii primul.

Lasă un comentariu

Comentariile sunt moderate înainte să apară.