Les serveurs MCP auto-hébergés se multiplient. D'abord un pour la documentation interne, puis un pour la base de données, puis quelqu'un en écrit un petit pour le pipeline de déploiement. Chacun a son port, son URL et son secret, et chaque membre de l'équipe colle les trois dans la configuration de son client. Faire tourner un jeton, c'est prévenir tout le monde.
Une passerelle ramène tout cela à une adresse et un jeton. Les clients se connectent à https://mcp.example.com/docs/mcp, .../db/mcp et ainsi de suite. La passerelle vérifie le jeton, envoie chaque chemin au bon serveur sur localhost, et les serveurs eux-mêmes ne sont jamais exposés. Il faut très peu de chose : un petit VPS avec IPv4 propre et Caddy.
Nous avons monté la configuration ci-dessous sur un serveur de test et l'avons vérifiée avec de vrais clients avant de l'écrire.
Ce qu'il vous faut
- Un VPS avec IPv4 publique, pour que les clients joignent le 443. Nano-IP suffit, sauf si vos serveurs MCP font eux-mêmes un travail lourd.
- Un domaine avec un enregistrement A pointant vers le serveur, ici
mcp.example.com. - Ubuntu 24.04 ou Debian 12/13. Caddy est dans les dépôts standard des deux.
1. Faire tourner les serveurs MCP sur localhost
Chaque serveur tourne comme service systemd. Ici, nous utilisons deux fois le serveur de référence server-everything pour que l'exemple soit autonome ; remplacez-le par les vôtres. Il faut Node.js 20 ou plus récent :
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
Une seconde copie utilise le port 3102. Ensuite :
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
Une chose remarquée pendant les tests : ce serveur écoute sur toutes les interfaces, pas seulement sur 127.0.0.1, et beaucoup d'autres font de même. L'étape 3 règle ce point.
2. Caddy : vérifier le jeton, puis router par chemin
Générez un jeton et transmettez-le à Caddy via un fichier d'environnement :
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
Le 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 obtient tout seul le certificat de mcp.example.com.
Le bloc route n'est pas décoratif. Notre première version plaçait la vérification du jeton et les blocs handle_path côte à côte au niveau supérieur, et les requêtes sans jeton passaient avec un 200. Caddy trie les directives dans son propre ordre, et handle_path s'exécute avant respond. Dans route, elles s'exécutent dans l'ordre écrit. Si vous adaptez cette configuration, testez d'abord le cas « sans jeton ».
handle_path retire le préfixe, donc /everything/mcp arrive au serveur sous la forme /mcp. flush_interval -1 transmet les événements en streaming dès leur arrivée.
3. Tout fermer sauf SSH et HTTPS
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
Après cela, dans notre test, les ports 3101 et 3102 étaient injoignables depuis l'extérieur du serveur, tandis que 22 et 443 répondaient. Le port 80 reste ouvert uniquement pour que Caddy obtienne et renouvelle ses certificats et redirige vers HTTPS.
4. Vérifier
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"
La première requête doit afficher 401 et la seconde 200. Lors de notre essai, un jeton erroné et un jeton tronqué ont tous deux obtenu 401, et un chemin inconnu a obtenu 404 même avec le bon jeton.
Ensuite depuis un client, par exemple Claude Code :
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
Les deux serveurs affichaient ✔ Connected. Le même client avec un mauvais jeton affichait ✘ Failed to connect. Pour la configuration des clients, voir Connecter EQVPS MCP à Claude Code et Codex.
Ajouter le serveur suivant
Lancez-le sur un nouveau port localhost, ajoutez un bloc handle_path, rechargez Caddy. La configuration de personne ne change, hormis une nouvelle URL sous le même hôte et le même jeton.
Les limites à connaître
- Un jeton pour tous, c'est simple et le faire tourner tient en un fichier, mais on ne peut pas retirer l'accès à une seule personne sans le changer pour tous. Pour un accès par utilisateur, placez un proxy d'authentification devant ou donnez à chacun sa route et son jeton.
- La passerelle ne rend pas un serveur sûr. Un serveur MCP qui exécute des commandes shell reste un shell pour quiconque détient le jeton. Gardez les jetons hors des configurations partagées et de git.
- Dimensionnez pour les serveurs, pas pour la passerelle. Caddy n'utilise que quelques dizaines de mégaoctets. Si l'un de vos serveurs fait tourner un modèle local ou indexe un gros dépôt, c'est lui qui décide de l'offre, pas le proxy.
Un seul serveur MCP ? VPS pour serveurs MCP traite ce cas plus simple. Vous voulez que vos agents gèrent aussi le serveur sur lequel ils vivent ? Le serveur MCP d'EQVPS le permet (ou l'API REST si vous préférez les scripts), et les offres avec IPv4 propre commencent à $8 par mois.
Commentaires
Pas encore de commentaires. Soyez le premier.