Aller au contenu principal

ADR-0165 : Hébergement des serveurs MCP dans le Navin Manager et isolation par projet de la passerelle MCP

Contexte​

La plupart des serveurs MCP de la bibliothèque Navin sont des commandes locales (stdio : npx, uvx). La passerelle MCP du Control Plane ne parle que le transport streamable HTTP : il faut une infrastructure qui accueille ces commandes, les expose en HTTP et les isole par tenant et par projet. Dans la plateforme, un serveur MCP rattaché à un projet devait aussi n'être utilisable que dans ce projet.

Décision​

  1. Runtime navin-mcp-host (navin-manager/mcp-host, bibliothèque standard Python uniquement) : un processus enfant par serveur, lancé sans shell, environnement propre (rien n'est hérité de l'hôte), dossier de travail <state_dir>/<tenant>/<projet>/<nom> en 0700, limites mémoire, fichiers et cœur, arrêt après inactivité, redémarrage après plantage avec mise en échec au-delà de 5 plantages par minute. Chaque serveur est exposé en streamable HTTP sur /t/{tenant}/p/{projet}/s/{nom}/mcp avec son propre jeton (seule l'empreinte sha256 est sur l'hôte) ; un jeton d'un autre serveur ou d'un autre projet reçoit 401 sans indice sur l'existence du serveur. La liste des outils autorisés est aussi appliquée sur l'hôte. /healthz et /metrics (Prometheus) servent la supervision.
  2. Service catalogue mcp-host du Manager : installation, modes simple et répliqué (un serveur est placé sur une instance par son identifiant), contrôles préalables (npx, uvx). La page MCP du Manager héberge, retire et renouvelle le jeton d'un serveur stdio d'un projet (menu « ... ») ; le Manager écrit servers.json sur l'instance, relu à chaud par le runtime.
  3. Publication : le Manager liste les serveurs hébergés d'un tenant (permission mcp:publish, rôle admin seulement). POST /v1/mcp/hosted/sync du Control Plane les publie dans leur projet : jeton rangé dans le Credential Broker (openbao://mcp/hosted/<id>), serveur du projet créé ou mis à jour (hosted_ref, V198), serveurs qui ne sont plus hébergés désactivés. Aucune réponse ni aucun audit ne contient un jeton. L'adresse des instances mcp-host doit figurer dans EAI_EGRESS_PRIVATE_ALLOWLIST.
  4. Isolation par projet dans la passerelle MCP : un serveur rattaché à un projet n'est listé et appelé que depuis ce projet (en-tête X-Project ou clé d'API liée au projet). Depuis un autre projet, la passerelle répond une erreur JSON-RPC mcp_server_project_mismatch sans aucun appel amont, et les routes outils, découverte, statut, catalogue et suppression répondent 403 ; chaque refus est audité. Un serveur sans projet reste partagé par le tenant ; tenant_admin et security_officer sans contexte de projet gardent la vue de tous les projets.

Alternatives écartées​

  • Proxys tiers (mcp-proxy, supergateway) : un seul processus partagé sans environnement propre ni jeton par serveur, et une dépendance de plus à maintenir sur l'hôte.
  • Lancer les commandes stdio dans le Control Plane : mélangerait du code de tiers au processus qui détient les autorisations de la plateforme.

Conséquences​

Un serveur stdio d'un projet devient utilisable par les agents de ce projet seulement, sans qu'un secret quitte le Manager ou le Credential Broker. Le Manager doit être relié à la plateforme (EAI_PLATFORM_MANAGER_URL, EAI_PLATFORM_MANAGER_TOKEN_REF).

Preuves​

navin-manager/mcp-host/tests, navin-manager/server/tests/test_mcp_hosting.py, services/control-plane/tests/test_mcp_project_isolation.py, services/control-plane/tests/test_mcp_hosted_sync.py, portal/tests/navin-library.test.ts.