Connecteur d'assistant IA (MCP) Module Inclus
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é.
- L'assistant agit en tant que personne, pas en son nom propre. L'utilisateur se connecte via sa connexion habituelle — mot de passe local ou votre SSO. L'assistant opère ensuite strictement dans les limites des droits de cet utilisateur.
- Lecture par défaut, écriture uniquement si vous l'autorisez. D'emblée, l'assistant peut observer et suggérer. Les outils d'écriture n'apparaissent que lorsqu'un administrateur les active.
- La finance est gardée séparément. Les données financières restent invisibles aux assistants tant que vous ne les exposez pas délibérément, même pour un utilisateur qui les voit dans le navigateur.
- Chaque action est attribuée. Une action MCP est enregistrée comme action d'agent dans le journal d'audit, distincte de la même personne cliquant dans le navigateur, et porte la raison donnée par l'assistant ainsi que quel assistant c'était.
- Les actions destructrices demandent deux fois. Avant de supprimer quelque chose ou de notifier les sponsors, l'assistant doit vous montrer ce qu'il s'apprête à faire et répéter l'appel pour le faire réellement — voir confirmation & limites.
- C'est inclus, gratuit. Aucune licence requise — il suffit de l'activer par organisation (opt-in,
mcp.enabled).
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 :
Concrètement, trois interrupteurs contrôlés par un administrateur décident du plafond :
| Bascule | Désactivée (défaut) | Activée |
|---|---|---|
| Activer le connecteur IA | Personne ne peut se connecter ; le consentement est refusé. | Les utilisateurs peuvent se connecter et s'authentifier. |
| Autoriser les actions d'écriture de l'IA | Lecture seule : l'assistant observe et suggère. | Les outils d'écriture apparaissent, bornés par les droits de chaque utilisateur. |
| Exposer la finance à l'IA | Budgets, 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 |
|---|---|
| Licence | Aucune. Le connecteur est inclus dans le cœur gratuit ; il suffit de l'activer (opt-in, mcp.enabled). |
| Vaks PM | org:manage pour l'activer et le configurer. Les utilisateurs individuels n'ont besoin que de leur compte habituel pour se connecter. |
| L'assistant | Un 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).
- Activez Activer le connecteur IA (MCP).
- 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.
- Décidez de Exposer la finance à l'IA. Laissez désactivé, sauf si vous voulez spécifiquement que les assistants lisent les chiffres financiers.
- Copiez l'URL du serveur MCP affichée dans la première carte — c'est ce que les utilisateurs collent dans leur assistant.
É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 :
- L'assistant envoie l'utilisateur vers Vaks PM pour s'authentifier (local ou SSO).
- L'utilisateur voit un écran de consentement indiquant exactement ce que l'assistant pourra faire.
- À 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 :
| Assistant | Ce que ça demande | Guide |
|---|---|---|
| Claude | S'enregistre seul. Coller l'URL, se connecter. Le plus court. | Connecter Claude → |
| ChatGPT | S'enregistre seul, via connecteurs / mode développeur (UI beta). | Connecter ChatGPT → |
| Copilot Studio | Client 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 :
- OAuth (auto-enregistrement) — les clients qui implémentent l'OAuth distant découvrent et se connectent exactement comme Claude : coller l'URL MCP, approuver le consentement dans le navigateur. Rien à pré-enregistrer.
- Coller un jeton — les clients sans OAuth distant (ou configurés pour un serveur local) prennent un jeton API personnel scopé
mcp:accessque l'utilisateur crée dans ses paramètres de compte et colle dans le connecteur. Même gating, mêmes droits per-user, pas de connexion navigateur — voir Option C.
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.
La bibliothèque (les workflows d'écriture n'apparaissent que si Autoriser les actions d'écriture de l'IA est activé) :
| Workflow | Ce qu'il fait | Type |
|---|---|---|
| Portfolio · Risk radar | Analyse tous les projets et dresse une liste priorisée des risques. | lecture |
| Portfolio · Deliverables awaiting review | Priorise ce qui attend ma revue et prépare le passage en revue. | lecture |
| Project · Status report | Rédige un point d'avancement prêt à partager pour un projet. | lecture |
| Team · Find work for someone | Projets/tâches adaptés aux compétences et à la disponibilité d'une personne. | lecture |
| Finance · Budget audit | Budget, consommé, prévisionnel et écarts — avec leviers de correction. | lecture |
| Project · Start from a brief | Transforme un brief en projet : tâches, dépendances, staffing. | écriture |
| Project · Close out | Checklist de clôture (tâches, livrables, compétences) puis passage en terminé. | écriture |
| Project · Announce a change | Rédige et envoie une annonce projet (PM / sponsors / membres / watchers). | écriture |
| Team · Staff a project | Propose une affectation optimale (compétences + disponibilité réelle). | écriture |
| Team · Onboard someone | Rattachement à une équipe, compétences déclarées et première mission. | écriture |
| Time · Timesheet catch-up | Repère les jours creux de la semaine et propose les imputations manquantes. | écriture |
L'écran de consentement
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 :
- Administrateurs : Admin → Sécurité → Connexions actives → Connexions IA liste l'assistant connecté de chaque utilisateur — qui, quel client, dernière utilisation — avec un bouton Déconnecter. La déconnexion est immédiate : le jeton à courte durée de vie est révoqué, pas laissé à expirer.
- Chaque utilisateur : ses propres réglages Sécurité listent ses connexions IA avec la même déconnexion en libre-service.
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.
Un administrateur définit la politique sous Admin → Intégrations → Connecteur IA (MCP) → Garde-fou de confirmation :
| Politique | Effet |
|---|---|
| Désactivée | Aucune 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'écriture | Tout 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.
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 :
- Assistants sur Internet, instance privée — un opérateur publie MCP à travers une passerelle isolée qui ne détient aucun secret et n'a aucun accès à la base de données. C'est la disposition durcie recommandée pour exposer une instance interne. Voir Option A.
- Assistants et instance sur le même réseau — aucune passerelle distincte nécessaire ; le point de terminaison est servi sur l'adresse du tenant et l'URL du panneau d'administration est correcte telle qu'affichée. Voir Option B.
- Aucun flux OAuth du tout — un utilisateur crée un jeton API personnel scopé à l'accès MCP dans ses réglages de compte et le colle dans le connecteur. Le plus simple pour un unique utilisateur avancé ; pas de connexion navigateur. Voir Option C.
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.
| Point | Dé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 box | Elle 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 sortant | De 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ôte | Un 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 applicatif | Indé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'autorisation | VAKS_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.
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
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.
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.
/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ément | Détail |
|---|---|
| Hôte DMZ | Docker + 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 public | Wildcard *.mcp.example.com → certs/mcp-wildcard.{crt,key}. |
| CA interne | Si l'edge interne est en cert auto-signé : certs/internal-ca.crt (sinon retirer NODE_EXTRA_CA_CERTS + insecureSkipVerify). |
| Pare-feu | Internet → DMZ:443 et DMZ → internal.example.com:443 UNIQUEMENT. Aucun accès base de données / Redis / overlay. |
| Déclaration du domaine public | Une 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. |
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.fr →
slug). 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.
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éploiement | Marche à 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
puis régénérer les artefacts : |
| 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 :
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. |
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
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.
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
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 serviceworkerutilise déjà (INTERNAL_API_URL: http://api:3000dansdocker-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 leINTERNAL_EDGE_URLde 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).
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.
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.
Dépannage
| Symptôme | Cause & correctif |
|---|---|
| L'assistant ne peut pas joindre le serveur du tout | Presque 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'approbation | Le connecteur est désactivé. Activez-le dans la section d'administration. |
| L'assistant n'offre que des outils de lecture | Les é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 disparu | Une 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'assistant | Attendu, 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 assistant | Les 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 connecter | Il 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 chose | Comportement 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.