Connecteur d'assistant IA (MCP) Module Inclus

Vaks PM · Guide d'intégration · MCP · Juillet 2026

Ce que vous obtiendrez. Des utilisateurs connectant un assistant IA — Claude, ChatGPT, Copilot Studio — à Vaks PM, pour qu'il puisse lire leurs projets et, si vous l'autorisez, agir en leur nom. L'assistant se connecte en tant que l'utilisateur et ne peut jamais faire plus que ce que l'utilisateur pourrait faire. Il est opt-in par organisation et désactivé tant que vous ne l'activez pas.

Ce que ça fait — et ce que ça ne fait pas

MCP (Model Context Protocol) est une façon standard pour un assistant IA d'appeler un outil externe. Vaks PM expose un serveur MCP pour qu'un assistant puisse interroger et mettre à jour l'espace de travail au nom d'un utilisateur connecté.

Le modèle de permissions

C'est la partie rassurante, et il vaut la peine de l'énoncer clairement à quiconque est inquiet à l'idée de connecter une IA à ses données de projet :

Les droits d'un assistant sont vos droits, intersectés avec ce que l'admin a autorisé — jamais plus. L'ensemble des permissions effectives est le rôle propre de l'utilisateur, restreint par les scopes du connecteur, restreint encore par les bascules lecture/écriture et finance. Un membre de base connectant un assistant ne peut atteindre rien qu'un membre de base ne puisse déjà atteindre. Il n'existe aucun moyen pour la connexion d'élever ses privilèges.

Concrètement, trois interrupteurs contrôlés par un administrateur décident du plafond :

BasculeDésactivée (défaut)Activée
Activer le connecteur IAPersonne ne peut se connecter ; le consentement est refusé.Les utilisateurs peuvent se connecter et s'authentifier.
Autoriser les actions d'écriture de l'IALecture seule : l'assistant observe et suggère.Les outils d'écriture apparaissent, bornés par les droits de chaque utilisateur.
Exposer la finance à l'IABudgets, marges et taux sont invisibles aux assistants.La finance est lisible, toujours uniquement pour les utilisateurs qui ont déjà l'accès finance.

Prérequis

CôtéCe qu'il vous faut
LicenceAucune. Le connecteur est inclus dans le cœur gratuit ; il suffit de l'activer (opt-in, mcp.enabled).
Vaks PMorg:manage pour l'activer et le configurer. Les utilisateurs individuels n'ont besoin que de leur compte habituel pour se connecter.
L'assistantUn client compatible MCP. Claude et ChatGPT s'enregistrent seuls ; Copilot Studio et Power Platform nécessitent un client pré-enregistré — voir le guide Copilot Studio. Les clients desktop et CLI qui redirigent vers un port local (ex. une extension VS Code) s'enregistrent aussi seuls et sont reconnus comme tels, donc le port variable choisi à chaque lancement est accepté automatiquement.
JoignabilitéL'assistant est un service cloud : il doit donc pouvoir joindre votre point de terminaison MCP. Pour une instance privée, cela demande une exposition délibérée — voir y accéder depuis l'extérieur.

Étape 1 — Activer et cadrer

Ouvrez Admin → Intégrations → Connecteurs IA (MCP).

  1. Activez Activer le connecteur IA (MCP).
  2. Décidez de Autoriser les actions d'écriture de l'IA. Commencez en lecture seule. Vous pourrez activer les écritures plus tard, une fois que vous aurez vu comment les assistants sont utilisés.
  3. Décidez de Exposer la finance à l'IA. Laissez désactivé, sauf si vous voulez spécifiquement que les assistants lisent les chiffres financiers.
  4. Copiez l'URL du serveur MCP affichée dans la première carte — c'est ce que les utilisateurs collent dans leur assistant.
Il n'y a rien de plus à configurer pour la connexion. MCP réutilise votre authentification existante — comptes locaux et tout SSO que vous avez mis en place. Les utilisateurs se connectent exactement comme pour l'application web. Si vous utilisez le SSO, la connexion MCP passe aussi par lui.
La temporisation des bascules a une subtilité. Les accès en écriture et à la finance sont décidés au moment où un utilisateur se connecte. Activer les écritures ne les accorde pas rétroactivement aux assistants déjà connectés tant qu'ils ne se rafraîchissent pas ; désactiver les écritures met jusqu'à une heure à disparaître d'une connexion existante. La finance, en revanche, est vérifiée en direct à chaque requête. Si vous devez couper les écritures immédiatement, déconnectez les connexions (voir gérer les connexions) plutôt que de seulement basculer l'interrupteur.

Étape 2 — Connecter un assistant

Un utilisateur ajoute Vaks PM comme connecteur personnalisé dans son assistant et colle l'URL du serveur MCP. L'assistant découvre seul comment s'authentifier et ouvre un flux de connexion :

  1. L'assistant envoie l'utilisateur vers Vaks PM pour s'authentifier (local ou SSO).
  2. L'utilisateur voit un écran de consentement indiquant exactement ce que l'assistant pourra faire.
  3. À l'approbation, l'assistant reçoit un jeton d'accès à courte durée de vie et peut commencer à travailler.

La connexion est un OAuth standard avec PKCE ; les jetons d'accès durent une heure et se rafraîchissent automatiquement. La quantité de configuration dépend de l'assistant — chacun a son guide pas à pas :

AssistantCe que ça demandeGuide
ClaudeS'enregistre seul. Coller l'URL, se connecter. Le plus court.Connecter Claude →
ChatGPTS'enregistre seul, via connecteurs / mode développeur (UI beta).Connecter ChatGPT →
Copilot StudioClient pré-enregistré + auth per-user + distribution Teams. Le plus long.Connecter Copilot Studio →

N'importe quel client MCP local ou open-source

Au-delà des trois assistants gérés, MCP est un standard ouvert : des clients locaux et open-source — Cline, Continue, LM Studio, Open WebUI, Goose, et des clients IDE comme VS Code, Cursor, Windsurf ou Zed — peuvent se connecter au même point de terminaison. Le support varie, d'où deux chemins :

Ces clients ne sont pas testés ni documentés individuellement ici — ils sont nombreux et évoluent vite. Les deux chemins ci-dessus fonctionnent pour tout client MCP conforme ; la documentation de l'assistant indique où coller l'URL ou le jeton.

Workflows pré-construits (MCP prompts)

Vaks livre un ensemble de workflows pré-construits — des gabarits en un clic qui pilotent l'assistant sur toute une tâche (rédiger un rapport de statut, staffer un projet, produire un radar de risque portefeuille). Techniquement, ce sont des MCP prompts : une instruction pré-rédigée injectée dans la conversation, rien de plus.

Seul Claude les affiche automatiquement. Claude montre la liste comme un menu de slash-commands. ChatGPT et Copilot Studio ne consomment pas cette primitive MCP — sur ces clients, vous recréez les mêmes workflows sous forme de starter prompts, topics ou instructions (le guide de chaque assistant montre comment : Copilot Studio, ChatGPT). Comme un prompt n'est que du texte, le recréer est un copier-coller.

La bibliothèque (les workflows d'écriture n'apparaissent que si Autoriser les actions d'écriture de l'IA est activé) :

WorkflowCe qu'il faitType
Portfolio · Risk radarAnalyse tous les projets et dresse une liste priorisée des risques.lecture
Portfolio · Deliverables awaiting reviewPriorise ce qui attend ma revue et prépare le passage en revue.lecture
Project · Status reportRédige un point d'avancement prêt à partager pour un projet.lecture
Team · Find work for someoneProjets/tâches adaptés aux compétences et à la disponibilité d'une personne.lecture
Finance · Budget auditBudget, consommé, prévisionnel et écarts — avec leviers de correction.lecture
Project · Start from a briefTransforme un brief en projet : tâches, dépendances, staffing.écriture
Project · Close outChecklist de clôture (tâches, livrables, compétences) puis passage en terminé.écriture
Project · Announce a changeRédige et envoie une annonce projet (PM / sponsors / membres / watchers).écriture
Team · Staff a projectPropose une affectation optimale (compétences + disponibilité réelle).écriture
Team · Onboard someoneRattachement à une équipe, compétences déclarées et première mission.écriture
Time · Timesheet catch-upRepère les jours creux de la semaine et propose les imputations manquantes.écriture

Chaque connexion passe par un écran de consentement pour que l'utilisateur voie, et approuve, exactement ce qu'il accorde. Il affiche le nom de votre organisation, le compte sous lequel il est connecté, et une liste à puces de ce que l'assistant pourra faire — consulter les projets et tâches, consulter la charge et les rapports, éventuellement consulter la finance, et, quand les écritures sont activées, une ligne en évidence sur la création et la modification en son nom. Il se termine par l'assurance que l'assistant ne peut pas dépasser les propres droits de l'utilisateur.

Si le connecteur est désactivé au moment du consentement, l'écran le signale et n'offre aucun bouton d'approbation — la connexion ne peut pas aboutir.

Gérer les connexions

Deux vues vous permettent de voir et de couper les connexions :

Suspendre ou supprimer un utilisateur coupe aussi ses connexions automatiquement.

Confirmation & limites de débit

Avant une action destructrice — supprimer une tâche, un client, une saisie de temps, retirer quelqu'un d'une équipe, ou notifier les sponsors — l'assistant est stoppé une fois. Il reçoit une description exacte de ce que l'action ferait, plus un jeton à usage unique, et doit vous la montrer puis répéter l'appel avec le jeton pour l'exécuter réellement. C'est un véritable aller-retour lié à cette action précise, pas une case que l'assistant peut cocher seul : le jeton expire après dix minutes et ne peut servir à rien d'autre.

Ceci concerne une personne pilotant un assistant — pas un agent autonome. Une confirmation n'a de sens que si quelqu'un est là pour la voir et l'approuver ; un agent sans surveillance n'a personne à qui la montrer, donc les agents passent ce contrôle sans y être soumis. Dans la pratique, cela intervient rarement : un agent autonome se voit déjà interdire la plupart de ces mêmes actions — supprimer des clients, des saisies de temps, des membres d'équipe — par ses propres droits, gérés séparément.

Un administrateur définit la politique sous Admin → Intégrations → Connecteur IA (MCP) → Garde-fou de confirmation :

PolitiqueEffet
DésactivéeAucune confirmation n'est demandée pour aucune action.
Actions destructrices (défaut)Les suppressions et actions sortantes (comme notifier les sponsors) demandent confirmation ; les modifications ordinaires non.
Toute action d'écritureTout changement, y compris une simple modification, demande confirmation.

La même section propose aussi des limites de débit par outil : un plafond optionnel sur le nombre d'appels d'un outil donné par minute, indépendant du garde-fou de confirmation — un filet de sécurité contre une boucle emballée qui resterait autrement invisible jusqu'à ce qu'elle se manifeste ailleurs. Un compteur en direct à côté montre ce que chaque outil se voit appeler en ce moment.

Y accéder depuis l'extérieur

Les assistants cloud doivent joindre votre point de terminaison MCP par Internet. La façon dont vous l'exposez est un choix de déploiement, et elle change l'URL — ce qui est le point de confusion le plus fréquent.

Le panneau admin affiche les deux URL, quand il y en a deux. Le champ principal reste toujours l'adresse propre du tenant. Si l'opérateur a déclaré un domaine MCP public séparé côté API (voir déclarer le domaine public plus bas), une seconde URL MCP publique apparaît automatiquement en dessous, calculée depuis cette déclaration — c'est celle-là qu'il faut remettre aux clients Internet, pas la première (qui ne joint que les clients sur le même réseau que l'instance). Elle ne manque de s'afficher que si une passerelle DMZ a été montée sans cette étape de déclaration — dans ce seul cas, confirmez le nom d'hôte public réel auprès de qui a déployé l'instance. Se tromper là-dessus est la raison habituelle de l'échec d'une première connexion.
À propos des domaines dans les exemples ci-dessous. example.com tient lieu de votre propre domaine — celui sur lequel votre instance est déjà servie. Partout où vous lisez example.com, lisez le vôtre ; partout où vous lisez demo, lisez l'un de vos propres slugs de tenant. Vaks PM est auto-hébergé : il n'existe aucune adresse partagée, et rien ne résout vers nous.

Trois topologies, détaillées pas à pas plus bas :

Sur Docker Swarm, c'est déjà fait pour vous. Le service MCP fait partie de la stack applicative standard, construit et roulé par le même circuit d'installation et de mise à jour que api/web/worker — deux réplicas avec un contrôle de santé, pour qu'une mise à jour glissante ne coupe jamais une requête d'assistant en cours. Si votre instance a été déployée ou mise à jour par le processus habituel, il tourne déjà : vérifiez avec docker service ls | grep mcp. Ce qui suit s'adresse à l'opérateur qui exploite les serveurs — pas à l'administrateur du tenant — et couvre en réalité trois autres cas : Kubernetes, où le MCP n'est pas encore templaté par le chart Helm et demande toujours une étape manuelle ; l'alternative DMZ durcie et totalement isolée pour exposer une instance privée ; et comprendre ce qui tourne, pour le dépannage.

Comment le serveur MCP se relie à une instance existante

Le conteneur MCP est délibérément « pauvre » : il ne détient aucun secret, ne parle à aucune base de données, et n'applique aucune règle lui-même. Il reçoit le jeton du client IA et le relaie à l'API interne de votre instance, qui valide tout — PKCE, écran de consentement, gating de licence, RBAC, limitation de débit. Autrement dit, on ajoute une porte d'entrée ; on ne déplace ni ne duplique aucune donnée.

PointDétail
Intégré par défaut (Swarm)L'image vaks-pm/mcp:latest fait partie de la stack standard, à côté d'api/web/worker — deux réplicas, répartis sur les nœuds, avec contrôle de santé. La topologie DMZ totalement isolée (Option A) reste disponible comme déploiement docker compose séparé pour les opérateurs qui veulent la passerelle entièrement hors de l'overlay Swarm.
Zéro secret sur la boxElle ne stocke rien de sensible. Toute la sécurité (PKCE, consentement, licence, RBAC, rate-limit) est appliquée par l'API interne, jamais par le MCP.
Un seul flux sortantDe la box vers l'API interne en :443, sur un ensemble de chemins borné (allow-list Traefik : /mcp local, surface d'auth forwardée). Tout le reste de l'app reste injoignable via cette porte.
Nommage d'hôteUn tenant est joint sur <slug>.mcp.<domaine>. Un seul certificat wildcard *.mcp.<domaine> couvre tous les tenants : le label mcp est retiré pour reconstituer le Host tenant (<slug>.<domaine>) passé à l'API.
Prérequis applicatifIndépendant du déploiement : le tenant doit avoir activé le connecteur (mcp.enabled, étape 1). Sinon l'API n'émet aucun jeton (403), quelle que soit la topologie. Aucune licence requise — le connecteur est inclus.
Mode serveur d'autorisationVAKS_AUTH_SERVER_MODE=self en DMZ (l'AS annoncé aux clients IA = la box, qui forwarde l'auth vers l'interne) ; =tenant en interne (l'AS = l'adresse du tenant, jointe directement).

Obtenir l'image MCP sur cet hôte

Uniquement nécessaire pour la passerelle DMZ isolée (Option A) ou une stack construite à la main — une installation Swarm standard construit et embarque déjà cette image sur chaque nœud, aux côtés d'api/web/worker.

Toutes les commandes de cette section — et des options A à C ci-dessous — se lancent directement sur le serveur qui hébergera le MCP (la box DMZ, ou un hôte interne), connecté en SSH. Il n'y a pas de poste de pilotage : cet hôte a Docker, puisqu'il fait tourner le conteneur.

Le plus simple est de construire l'image sur place, depuis une copie du dépôt. Le Dockerfile MCP attend la racine du dépôt comme contexte de build. Aucun registry n'est requis.

# On the MCP host: get a copy of the repository, then build from its root
git clone <repo-url> vakspm && cd vakspm
sudo docker build -t vaks-pm/mcp:latest -f mcp/Dockerfile .

sudo docker image ls vaks-pm/mcp        # check: the image is present
Cet hôte ne peut pas construire ? (dépôt indisponible, ou chaîne de build à garder hors DMZ) — construisez l'image ailleurs, exportez-la avec docker save -o mcp.tar vaks-pm/mcp:latest, copiez le fichier mcp.tar ainsi que le dossier infra/dmz/ sur cet hôte, puis importez l'image localement :
# On the MCP host, after copying mcp.tar here
sudo docker load -i mcp.tar

Option A — Passerelle DMZ isolée durci

La disposition recommandée pour exposer une instance privée à des assistants cloud. Une box de DMZ, qui ne rejoint pas l'overlay Swarm interne, publie /mcp à Internet et forwarde la surface d'auth (OAuth + connexion + consentement) vers l'API on-premise. L'API interne n'est jamais joignable directement depuis Internet.

Agnostique de l'orchestrateur. La box DMZ est une stack Docker Compose autonome qui ne rejoint aucun cluster — elle n'a besoin que d'un seul flux sortant HTTPS, vers l'INTERNAL_EDGE_URL qu'on lui donne. Ça marche à l'identique devant une installation Kubernetes : pointez-la vers l'Ingress du cluster au lieu du Traefik interne, le reste de cette section ne change pas. Voir Déploiement Kubernetes pour le côté Ingress.
Clients IA Claude · ChatGPT · Copilot Studio 443 BOX DMZ · HORS DE L'OVERLAY Traefik + mcp aucun secret · aucun accès DB sert /mcp + les métadonnées RFC 9728 en local surface d'auth seulement 443 CLUSTER INTERNE Traefik (interne) même edge qui sert l'appli api · web · worker · controlplane DB / Redis / Garage jamais exposés
La box DMZ ne détient aucun secret et ne touche jamais la base, Redis ou le stockage objet. Elle sert /mcp elle-même et ne forwarde que la surface d'authentification (OAuth, connexion, consentement, discovery) vers l'API interne — le seul flux sortant que le firewall autorise (DMZ → interne :443 uniquement, rien d'autre). Le cluster interne reste injoignable directement depuis Internet.

Prérequis, à fournir par l'opérateur :

ÉlémentDétail
Hôte DMZDocker + Docker Compose. Hors du Swarm interne.
DNS public*.mcp.example.com → IP publique de la box DMZ.
DNS interne (split-horizon)internal.example.com → Traefik on-prem, résolu depuis la DMZ.
Cert TLS publicWildcard *.mcp.example.comcerts/mcp-wildcard.{crt,key}.
CA interneSi l'edge interne est en cert auto-signé : certs/internal-ca.crt (sinon retirer NODE_EXTRA_CA_CERTS + insecureSkipVerify).
Pare-feuInternet → DMZ:443 et DMZ → internal.example.com:443 UNIQUEMENT. Aucun accès base de données / Redis / overlay.
Déclaration du domaine publicUne seule ligne côté API (voir « Déclarer le domaine public » ci-dessous), UNIQUEMENT si ce domaine diffère du domaine interne. Sur le même domaine, rien à déclarer.
Le nom public du serveur MCP est indépendant du nom interne de l'instance. L'application peut être servie sur un nom privé, résolu uniquement sur le réseau interne (par exemple vaks.local), pendant que le serveur MCP est publié sur un domaine public (par exemple mcp.exemple.fr, chaque organisation étant servie sur <slug>.mcp.exemple.fr). Rien n'impose que les deux se ressemblent.

Déclarer le domaine public

Le serveur MCP transmet à l'API le nom d'hôte public tel quel ; l'API en déduit l'organisation à partir du premier segment (<slug>.mcp.exemple.frslug). Une seule déclaration couvre toutes les organisations de ce domaine ; il n'y a jamais d'entrée par tenant. Ça ne compte qu'en mode pooled, où plusieurs organisations partagent une même instance et où l'API doit déterminer, à partir du Host, à laquelle une requête s'adresse. En mode dedicated, il n'y a qu'une seule organisation et le Host n'est jamais utilisé pour choisir un tenant : cette déclaration entière est sans objet, autant la sauter.

Cette déclaration n'est nécessaire que si le domaine public du MCP diffère du domaine interne. Si le MCP est publié sur le même domaine que l'application (par exemple <slug>.mcp.exemple.com avec une application sur exemple.com), la résolution est automatique et il n'y a rien à faire.

Plusieurs domaines publics distincts à la fois, sur un seul cluster. La liste n'est pas un slot mono-domaine : un opérateur pooled qui sert ses clients sous plusieurs marques publiques peut déclarer plusieurs domaines totalement distincts, chacun résolu de la même façon — "publicDomains": ["mcp.exemple.fr", "mcp.exemple.io"] permet à acme.mcp.exemple.fr et acme.mcp.exemple.io de joindre le même tenant. Ce que ça ne donne pas, c'est une seule passerelle MCP qui fronte plusieurs installations réellement séparées : chaque process MCP forwarde vers un seul backend fixe (son VAKS_API_URL), réglé une fois au déploiement. Des clusters distincts — chacun avec sa propre base, ses propres tenants — ont chacun besoin de leur propre déploiement MCP ; la liste multi-domaines élargit seulement la portée publique des tenants d'un même cluster, elle ne relie pas des clusters entre eux.
Mode de déploiementMarche à suivre
Générateur Le manifeste infra/cluster.json décrit le cluster. Il est créé au premier déploiement par l'assistant bash setup.sh (qui pose désormais la question du domaine public MCP), ou copié depuis infra/cluster.example.json.

Renseigner le bloc mcp — une liste, plusieurs domaines distincts peuvent être déclarés à la fois :

"mcp": { "publicDomains": ["mcp.exemple.fr", "mcp.exemple.io"] }

puis régénérer les artefacts :

bash setup.sh --manifest infra/cluster.json
Stack écrit à la main Renseigner TENANCY_MCP_DOMAINS sur les services api et controlplane de docker-stack.yml, où une ligne commentée sert de modèle :
TENANCY_MCP_DOMAINS="mcp.exemple.fr,mcp.exemple.io"
Séparés par une virgule ; chaque entrée est matchée indépendamment, donc des domaines sans rapport entre eux passent très bien.
Ne pas relancer bash setup.sh sans argument sur un cluster déjà installé. Sous cette forme, la commande relance l'assistant interactif et réécrit infra/cluster.json à partir des réponses saisies : toute modification manuelle du manifeste est perdue. Pour reconstruire les artefacts après une modification, utiliser la forme avec manifeste, qui lit le fichier sans le réécrire :
bash setup.sh --manifest infra/cluster.json

Toujours connecté en SSH sur la box DMZ :

1 · Obtenir l'image — construire ou importer vaks-pm/mcp:latest sur cet hôte (voir obtenir l'image sur cet hôte ci-dessus).

2 · Configurer la box — dans le dossier infra/dmz/ du dépôt cloné sur cet hôte :

cd infra/dmz
cp .env.example .env            # edit INTERNAL_EDGE_URL to your split-horizon internal edge
mkdir -p certs
#   certs/mcp-wildcard.crt   certs/mcp-wildcard.key   public *.mcp.example.com cert
#   certs/internal-ca.crt                             internal edge CA (only if self-signed)

Le docker-compose.yml fixe les variables clés : VAKS_API_URL (= INTERNAL_EDGE_URL), VAKS_API_PREFIX=api/v1, VAKS_MCP_LABEL=mcp et VAKS_AUTH_SERVER_MODE=self. En principe, seul INTERNAL_EDGE_URL est à éditer.

3 · Démarrer :

docker compose up -d
docker compose logs -f mcp

4 · Vérifier — depuis Internet (remplacer demo par un slug de tenant réel) :

# Resource metadata (served locally by mcp) — resource + authorization_servers = self
curl -sk https://demo.mcp.example.com/.well-known/oauth-protected-resource | jq

# AS discovery (forwarded to the internal API, URLs rewritten to the public host)
curl -sk https://demo.mcp.example.com/.well-known/oauth-authorization-server | jq

# POST /mcp with no token → 401 + WWW-Authenticate pointing at resource_metadata
curl -ski -X POST https://demo.mcp.example.com/mcp -d '{}' | grep -i www-authenticate
Une fois ces trois vérifications vertes, brancher le connecteur côté client IA (Claude / ChatGPT / Copilot Studio) sur https://<slug>.mcp.<votre-domaine>/mcp — le nom d'hôte public que vous avez choisi et mis en DNS, pas une adresse qui nous appartiendrait. Le client découvre l'AS, lance la connexion (forwardée vers l'app interne), obtient son jeton et appelle les tools. C'est ce nom d'hôte (pas l'URL du panneau d'administration) que vous remettez aux utilisateurs.

Option B — Interne / co-localisé défaut sur Swarm

C'est ce qu'une installation Swarm standard fait déjà tourner, dès le départ : la même image MCP, à côté de l'API, sur le réseau overlay Swarm, avec le serveur d'autorisation annoncé directement sur l'adresse du tenant. Il n'y a rien à ajouter. La suite sert à vérifier que c'est en bonne santé, ou à reproduire la même disposition sur Kubernetes ou une stack construite à la main.

Clients IA LAN ou *.example.com 443 MÊME OVERLAY SWARM QUE LE RESTE DE LA STACK Traefik route /mcp par chemin, priorité 150 /mcp /, /api, … mcp (2 réplicas) répartis sur les nœuds · /healthz api · web · worker controlplane · même overlay api:3000 overlay · sans TLS
Le service MCP tourne en deux réplicas sur le même overlay Swarm que api/web/worker. Traefik route /mcp vers lui et tout le reste vers la stack ; mcp joint ensuite api directement sur http://api:3000 — un seul saut, sans TLS, sans passerelle séparée. C'est exactement ce qu'une installation standard fait déjà tourner, sans étape supplémentaire.

Vérifier, sur une installation Swarm existante :

docker service ls | grep mcp
# vaks-pm_mcp   replicated   2/2   vaks-pm/mcp:latest

docker service ps vaks-pm_mcp --format "{{.Node}} {{.CurrentState}}"
# deux tâches, sur deux nœuds différents, toutes deux Running
« Edge interne » = simplement l'URL où le conteneur MCP joint l'API (la variable VAKS_API_URL), depuis là où il tourne. Ce n'est pas une valeur à retrouver ailleurs ; elle dépend de l'emplacement du MCP :
  • Sur le même overlay Swarm que l'API (co-localisé) → le nom de service interne : http://api:3000. C'est exactement la valeur que le service worker utilise déjà (INTERNAL_API_URL: http://api:3000 dans docker-stack.yml). Pas de TLS ni de Traefik sur ce saut — on est dans l'overlay. En Kubernetes, l'équivalent est le Service de l'API dans le namespace : http://<release>-api:3000.
  • Sur un hôte séparé / en DMZ → le MCP ne peut pas joindre api:3000 (overlay uniquement), il passe donc par le Traefik interne via un nom résolu en split-horizon, ex. https://internal.example.com — c'est le INTERNAL_EDGE_URL de l'Option A. Ce nom, c'est vous qui le choisissez : il doit résoudre vers le Traefik on-prem qui sert déjà l'app (IP du manager, ou VIP).
Quel que soit ce réglage, le MCP transmet l'identité du tenant dans un en-tête Host explicite (le host tenant, label mcp retiré), donc l'API résout le bon tenant en mode pooled indépendamment de l'URL de connexion.

Sur Docker Swarm — pour référence, ou pour reproduire ceci sur une stack construite à la main, voici ce qu'une installation standard déclare déjà :

  mcp:
    image: vaks-pm/mcp:latest
    networks: [vakspm]                 # le même overlay que le service `api`
    environment:
      VAKS_API_URL: http://api:3000    # le service API sur l'overlay (même valeur que le worker)
      VAKS_API_PREFIX: api/v1
      # VAKS_AUTH_SERVER_MODE est délibérément laissé non défini (défaut "self").
      # Sur un déploiement co-localisé sans adresse mcp.<tenant> séparée,
      # "self" et "tenant" résolvent vers la même origine — le définir
      # explicitement ne changerait rien ici.
    healthcheck:
      test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8080/healthz >/dev/null 2>&1 || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s
    stop_grace_period: 30s              # temps de finir les requêtes en vol avant de retirer un réplica
    deploy:
      replicas: 2
      placement:
        preferences: [{ spread: node.id }]           # les deux réplicas atterrissent sur des nœuds différents
      update_config: { parallelism: 1, order: start-first }
      labels:
        - traefik.enable=true
        - "traefik.http.routers.mcp.rule=PathPrefix(`/mcp`) || PathPrefix(`/.well-known/oauth-protected-resource`)"
        - traefik.http.routers.mcp.priority=150      # priorité explicite, pas laissée au matching par longueur
        - traefik.http.services.mcp.loadbalancer.server.port=8080

Ce routeur détourne /mcp (et les métadonnées RFC 9728) vers le conteneur MCP sur n'importe quel host tenant ; le Host entrant (ex. demo.example.com) est préservé, et un seul service MCP sert tous les tenants. On réutilise le certificat existant de l'instance — aucun wildcard *.mcp ni cert séparé.

Le contrôle de santé est la partie la plus importante à conserver si on adapte ceci à la main : sans lui, une mise à jour glissante peut envoyer la requête d'un client vers un réplica dont le processus n'a pas fini de démarrer, ce qui coupe brièvement la connexion. Avec lui, Swarm attend qu'un nouveau réplica réponde sur /healthz avant de retirer l'ancien — la mise à jour du serveur devient invisible pour un assistant connecté.

Sur Kubernetes — le chart Helm ne template pas (encore) le MCP : on applique les manifestes à côté, dans le même namespace que la release. Un Deployment + Service pour l'image, plus des chemins d'Ingress qui envoient /mcp vers ce Service sur le host tenant. Même logique qu'en Swarm : VAKS_API_URL pointe le Service de l'API du namespace, VAKS_AUTH_SERVER_MODE=tenant.

apiVersion: apps/v1
kind: Deployment
metadata: { name: mcp }
spec:
  replicas: 2
  selector: { matchLabels: { app: mcp } }
  template:
    metadata: { labels: { app: mcp } }
    spec:
      containers:
        - name: mcp
          image: vaks-pm/mcp:latest              # your registry / tag
          ports: [{ containerPort: 8080 }]
          env:
            - { name: VAKS_API_URL,          value: "http://<release>-api:3000" }  # the API Service in this namespace
            - { name: VAKS_API_PREFIX,       value: "api/v1" }
            - { name: VAKS_AUTH_SERVER_MODE, value: "tenant" }
---
apiVersion: v1
kind: Service
metadata: { name: mcp }
spec:
  selector: { app: mcp }
  ports: [{ name: http, port: 8080, targetPort: 8080 }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: { name: mcp }
spec:
  ingressClassName: nginx                        # same class as the chart Ingress
  tls:
    - hosts: ["demo.example.com"]
      secretName: vaks-pm-tls                    # same TLS Secret as the chart Ingress
  rules:
    - host: demo.example.com                     # the tenant host
      http:
        paths:
          - path: /mcp
            pathType: Prefix
            backend: { service: { name: mcp, port: { number: 8080 } } }
          - path: /.well-known/oauth-protected-resource
            pathType: Prefix
            backend: { service: { name: mcp, port: { number: 8080 } } }

Trouver le nom exact du Service de l'API : kubectl get svc -l app.kubernetes.io/component=api (typiquement <release>-api). Reprendre la même ingressClassName et le même secretName TLS que l'Ingress du chart ; le chemin /mcp l'emporte sur / (préfixe plus spécifique), donc il n'y a pas de conflit avec le routage existant. Un seul déploiement MCP sert tous les tenants : ajouter un host: par tenant à exposer.

Dans ce mode, l'URL du panneau d'administration est correcte telle qu'affichée — le point de terminaison est servi sur l'adresse propre du tenant. C'est le seul cas où l'on peut remettre aux utilisateurs l'URL du panneau sans la corriger.

Option C — Jeton statique, sans OAuth

Si le serveur d'autorisation doit rester strictement interne, ou pour un unique utilisateur avancé, on peut se passer complètement du flux OAuth : l'utilisateur crée un jeton API personnel scopé mcp:access dans ses réglages de compte et le colle dans le connecteur. Aucune connexion navigateur, aucune surface d'auth à forwarder. Le jeton reste soumis au même gating (mcp.enabled) et aux mêmes droits que l'utilisateur.

Ce qui est journalisé

Chaque action MCP est attribuée comme action d'agent dans le journal d'audit — délibérément distinguée de la même personne agissant dans le navigateur, et d'un simple script utilisant le même jeton. Chaque écriture porte aussi la justification fournie par l'assistant, une raison d'une ligne pour l'action, et quel assistant a fait l'appel (Claude Desktop, ChatGPT, etc.). La trace d'audit vous dit donc non seulement ce qui a changé, mais qu'un assistant l'a fait, au nom de qui, depuis quel client, et pourquoi. Les connexions elles-mêmes sont visibles et révocables comme ci-dessus.

La justification et le nom du client sont déclarés, pas vérifiés. Le connecteur demande aux assistants d'expliquer chaque écriture et de s'identifier, et les mieux élevés le font — mais rien côté serveur ne contrôle ces deux déclarations, et le champ justification est facultatif : une écriture peut donc réussir sans. Traitez les deux comme un contexte utile, pas comme une preuve.

Dépannage

SymptômeCause & correctif
L'assistant ne peut pas joindre le serveur du toutPresque toujours l'URL : dans un déploiement à passerelle, l'URL du panneau d'administration n'est pas l'URL publique. Confirmez le vrai nom d'hôte avec votre opérateur. Vérifiez aussi que l'assistant, un service cloud, peut la joindre par Internet.
L'écran de consentement refuse, aucun bouton d'approbationLe connecteur est désactivé. Activez-le dans la section d'administration.
L'assistant n'offre que des outils de lectureLes écritures sont désactivées, ou la connexion précède leur activation. Activez Autoriser les actions d'écriture de l'IA ; les connexions existantes le prennent en compte à leur prochain rafraîchissement, une reconnexion est donc le correctif rapide.
Les écritures fonctionnaient, puis ont brièvement disparuUne perte de contact passagère avec l'API fait retomber la session en lecture seule au lieu de produire une erreur. Elle se rétablit d'elle-même. Une perte persistante mérite une vérification avec votre opérateur.
La finance est invisible à l'assistantAttendu, sauf si Exposer la finance à l'IA est activé — et même alors, uniquement pour les utilisateurs qui ont déjà l'accès finance.
L'utilisateur voit du texte d'erreur en français dans son assistantLes descriptions d'outils destinées à l'assistant et certains messages d'exécution sont en français dans cette version, tandis que les écrans d'administration et de consentement sont en anglais. C'est cosmétique ; le comportement n'est pas affecté.
Copilot Studio ne peut pas se connecterIl ne s'enregistre pas seul, et a ses propres pièges de licence et de per-user. Voir le guide Copilot Studio.
L'assistant demande à l'utilisateur de confirmer avant de supprimer quelque choseComportement attendu — voir confirmation & limites. Réglez la politique sur Désactivée si vous préférez qu'il ne demande pas.
Un gros export (ex. un téléchargement complet des données personnelles) semble bloquéLes gros exports tournent en tâche de fond : l'assistant les lance puis interroge l'avancement au lieu de garder une connexion ouverte. Pour un très gros tenant, cela peut prendre un moment — c'est en cours, pas bloqué.

Guides par assistant : Copilot Studio · Claude · ChatGPT. À lire aussi : authentification unique OIDC, réutilisée pour la connexion MCP · produit & fonctionnalités pour le modèle de gouvernance des agents · toutes les intégrations.