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
- 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}/mcpavec 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./healthzet/metrics(Prometheus) servent la supervision. - Service catalogue
mcp-hostdu 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 écritservers.jsonsur l'instance, relu à chaud par le runtime. - Publication : le Manager liste les serveurs hébergés d'un tenant (permission
mcp:publish, rôle admin seulement). POST/v1/mcp/hosted/syncdu 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 dansEAI_EGRESS_PRIVATE_ALLOWLIST. - 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-Projectou clé d'API liée au projet). Depuis un autre projet, la passerelle répond une erreur JSON-RPCmcp_server_project_mismatchsans 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.