Aller au contenu
Développement medium

ContextForge : la passerelle MCP pour gouverner vos serveurs

11 min de lecture

Quand vous n'avez qu'un serveur MCP, le brancher dans un client suffit. Dès que vous en avez plusieurs, chacun avec sa propre authentification, son transport et son cycle de vie, câbler chaque serveur dans chaque agent devient ingérable. C'est le problème que résout une passerelle MCP.

ContextForge (le projet mcp-context-forge d'IBM, sous licence Apache 2.0) est une passerelle et un registre MCP. Il se place devant vos serveurs MCP, vos APIs REST et vos agents A2A, et expose un endpoint unifié avec découverte centralisée, authentification, rate-limiting et observabilité. Ce n'est pas un générateur de serveur comme reShapr ; c'est la couche de gouvernance au-dessus de vos serveurs.

Ce guide déroule un lab reproductible : déployer la passerelle, fédérer un serveur MCP existant, composer un serveur virtuel unifié, puis appeler un outil au travers, le tout piloté par l'API REST.

  • Déployer ContextForge en local avec Docker.
  • Fédérer un serveur MCP existant derrière la passerelle.
  • Composer un serveur virtuel exposé en endpoint MCP unifié.
  • Appeler un outil fédéré par l'API et gérer la gouvernance (JWT, SSRF).

Trois notions structurent la passerelle. Les comprendre évite de confondre un serveur fédéré et un serveur virtuel.

  • Gateway (fédération) : un serveur MCP distant que la passerelle enregistre et introspecte. À l'enregistrement, ContextForge découvre automatiquement ses outils, prompts et ressources.
  • Tool : un outil exposé à travers la passerelle. Il vient soit d'un serveur MCP fédéré, soit d'une API REST que vous déclarez directement.
  • Virtual server : un regroupement d'outils choisis, publié comme un seul endpoint MCP que vos agents consomment. C'est la brique qui compose une vue cohérente à partir de plusieurs sources.

Autour de ça, ContextForge ajoute la gouvernance : authentification JWT et RBAC, rate-limiting, protection SSRF, registres de prompts et de ressources, et observabilité OpenTelemetry.

Le compose.yaml lance la passerelle sur le port 4444, avec une base SQLite et un compte administrateur. La clé JWT_SECRET_KEY doit faire au moins 32 octets.

services:
gateway:
image: ghcr.io/ibm/mcp-context-forge:latest
container_name: cf-gateway
ports: ["4444:4444"]
environment:
- HOST=0.0.0.0
- MCPGATEWAY_UI_ENABLED=true
- MCPGATEWAY_ADMIN_API_ENABLED=true
- AUTH_REQUIRED=true
- JWT_SECRET_KEY=my-test-key-but-now-longer-than-32-bytes
- PLATFORM_ADMIN_EMAIL=admin@example.com
- PLATFORM_ADMIN_PASSWORD=changeme
- DATABASE_URL=sqlite:////data/mcp.db
- SECURE_COOKIES=false
volumes:
- ./data:/data
networks: [cfnet]
networks:
cfnet:
Fenêtre de terminal
mkdir -p data && chmod 777 data
docker compose up -d

Toute l'API est protégée. On génère un jeton JWT avec l'utilitaire fourni, en réutilisant exactement le même secret que JWT_SECRET_KEY :

Fenêtre de terminal
TOKEN=$(docker exec cf-gateway python3 -m mcpgateway.utils.create_jwt_token \
--username admin@example.com --exp 10080 \
--secret my-test-key-but-now-longer-than-32-bytes)

Un appel de vérification confirme la version et l'état du runtime :

Fenêtre de terminal
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:4444/version | jq '.app, .platform.python'
# { "name": "ContextForge", "version": "1.0.4", "mcp_protocol_version": "2025-11-25" }
# "3.12.13"
curl -s http://localhost:4444/health | jq '.status, .mcp_runtime.effective_mode'
# "healthy"
# "off" <- le sidecar Rust est bien désactivé par défaut

Le champ effective_mode: "off" confirme le point du départ : le plan de contrôle est en Python, le sidecar Rust n'intervient pas.

Pour la démo, on fédère un petit serveur MCP d'horloge (fast-time-server) lancé en SSE sur le même réseau Docker. L'enregistrement se fait par un POST /gateways :

Fenêtre de terminal
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"fast_time","url":"http://fasttime:8080/sse","transport":"SSE"}' \
http://localhost:4444/gateways

Une fois le réglage appliqué et la passerelle redémarrée, l'enregistrement réussit et ContextForge introspecte le serveur : ses outils apparaissent automatiquement dans le registre.

Fenêtre de terminal
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:4444/tools \
| jq '.[] | {name, id}'
# { "name": "fast-time-get-system-time", "id": "addedc953912436cb67c5fac1edfd8f3" }
# { "name": "fast-time-convert-time", "id": "c713864ab5bc4ee2834a8b3c7ffc5098" }

Un POST /servers regroupe les outils choisis derrière un endpoint MCP unique. On référence les identifiants d'outils obtenus ci-dessus :

Fenêtre de terminal
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"server":{"name":"horloge","associated_tools":["addedc953912436cb67c5fac1edfd8f3","c713864ab5bc4ee2834a8b3c7ffc5098"]}}' \
http://localhost:4444/servers | jq '{id, name, associatedTools}'
# { "id": "058e85525e3844e39f02cd78f37b5b83", "name": "horloge",
# "associatedTools": ["fast-time-get-system-time","fast-time-convert-time"] }

Le serveur virtuel expose alors un endpoint MCP que n'importe quel client (Claude Desktop, un agent LangGraph, l'inspecteur MCP) peut consommer avec le même jeton :

http://localhost:4444/servers/058e85525e3844e39f02cd78f37b5b83/mcp

C'est tout l'intérêt : vos agents ne connaissent qu'une URL et un jeton, la passerelle s'occupe du reste.

L'appel se fait en JSON-RPC 2.0 sur /rpc, méthode tools/call :

Fenêtre de terminal
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"fast-time-get-system-time","arguments":{}}}' \
http://localhost:4444/rpc | jq '.result.content[0].text'
# "2026-07-03T12:23:13Z"

La réponse vient bien du serveur fast-time fédéré, mais elle a transité par la passerelle : authentifiée, journalisée, et soumise aux politiques. Le client, lui, n'a parlé qu'à ContextForge.

ContextForge ne fédère pas que des serveurs MCP : il transforme aussi une API REST en outil MCP, via un POST /tools. Vous décrivez l'endpoint et son schéma d'entrée, la passerelle l'expose comme n'importe quel autre outil :

Fenêtre de terminal
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"tool":{"name":"weather","url":"https://api.exemple.com/weather","integration_type":"REST","request_type":"GET","input_schema":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}' \
http://localhost:4444/tools

C'est le recouvrement avec reShapr : pour envelopper une API isolée, les deux savent le faire. La différence est que ContextForge le fait au sein d'une plateforme qui fédère et gouverne l'ensemble.

Le projet migre progressivement le chemin de données vers un runtime Rust (sessions, event-store, exécution directe des outils), pour la performance. Deux points à retenir :

  • Le plan de contrôle reste en Python : validation JWT, RBAC, hooks de plugins et l'API d'administration utilisée ici.
  • Le sidecar s'active par paliers via RUST_MCP_MODE (off, shadow, edge, full), désactivé par défaut. Vous l'avez vu dans /health avec effective_mode: "off".

Autrement dit, l'API et les concepts de ce guide ne bougent pas avec cette évolution : le Rust accélère le transport, il ne remplace pas la surface que vous pilotez.

  • Authentification : JWT par défaut sur l'API (l'auth Basic API est désactivée), plus RBAC. Protégez JWT_SECRET_KEY comme un secret de production.
  • Protection SSRF : activée par défaut, elle empêche la passerelle d'atteindre des adresses internes non autorisées. Ouvrez uniquement ce dont vous avez besoin via SSRF_ALLOWED_NETWORKS.
  • Rate-limiting et retries au niveau des adaptateurs, pour ne pas laisser un agent emballé saturer un serveur en aval.
  • Observabilité : traces OpenTelemetry exportables vers Jaeger, Zipkin ou Phoenix, indispensables quand plusieurs agents appellent plusieurs serveurs.

Ils ne jouent pas au même étage, et se combinent bien.

  • reShapr répond à « j'ai une API REST/GraphQL/gRPC à exposer en MCP, vite et sans coder ». Outil léger, mono-source.
  • ContextForge répond à « j'ai plusieurs serveurs MCP à unifier, sécuriser et observer derrière un point d'entrée ». Plateforme de gouvernance.

Vous pouvez générer des serveurs avec reShapr, puis les fédérer dans ContextForge.

  • blocked by SSRF protection : la cible est une adresse privée. Autorisez la plage via SSRF_ALLOWED_NETWORKS (recommandé) ou SSRF_ALLOW_PRIVATE_NETWORKS=true en lab.
  • 401/403 sur l'API : le jeton a été généré avec un secret différent de JWT_SECRET_KEY, ou il a expiré (--exp en minutes). Régénérez-le avec le bon secret.
  • Image introuvable sur un tag précis : tous les tags semver ne sont pas publiés sur ghcr.io. Le latest correspondait ici à la 1.0.4. Épinglez ensuite un tag reproductible plutôt que latest.
  • La passerelle en conteneur ne joint pas un serveur sur l'hôte : mettez-les sur le même réseau Docker (nom de service) ou utilisez host.docker.internal.
  • ContextForge est une passerelle et un registre MCP : un endpoint unifié devant vos serveurs MCP, APIs REST et agents.
  • On fédère un serveur (/gateways), la passerelle découvre ses outils, on les regroupe en serveur virtuel (/servers), et on appelle via /rpc.
  • La gouvernance est native : JWT/RBAC, protection SSRF par défaut, rate-limiting, observabilité OpenTelemetry.
  • Le plan de contrôle reste Python ; le sidecar Rust est un accélérateur optionnel (RUST_MCP_MODE=off par défaut).
  • Face à reShapr, ContextForge est la couche plateforme, pas le générateur mono-API.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn