Pourquoi vos outils MCP vous déconnectent sans cesse : dans les coulisses du cycle de vie des tokens OAuth

Vous avez branché Linear, Figma et Notion dans Hermes le mois dernier. hermes mcp list affichait trois serveurs en bonne santé et chaque appel d’outil fonctionnait. Ce matin, chaque outil MCP répond par une erreur d’autorisation — Desktop a affiché une notification hier soir, vous étiez en réunion et vous l’avez fermée. Vous réauthentifiez Linear, et dans l’après-midi c’est Figma qui décroche à son tour. Le schéma se répète presque chaque semaine dès que Hermes tourne à la fois sur votre portable et sur la machine du bureau, et vous commencez à vous demander ce que vous avez mal configuré. Probablement rien : la faute est dans le long cycle de vie des tokens qui se cache derrière ces connexions, et Hermes vient de livrer quatre correctifs visant précisément ce point.
La poignée de main derrière ces outils : access tokens, refresh tokens et le « usage unique »
MCP (Model Context Protocol) est le protocole qui permet à Hermes d’appeler les outils qu’expose un service externe ; Linear, Figma et Notion n’acceptent pas de clé API statique, ils délivrent des identifiants via OAuth. Quand vous terminez une connexion OAuth, vous obtenez deux choses :
- Un access token — courte durée de vie, généralement trente à soixante minutes. Il accompagne chaque appel d’outil.
- Un refresh token — longue durée de vie. Son seul rôle est de vous procurer un nouvel access token.
Pour qu’un refresh token volé cesse vite d’être utile, les serveurs d’autorisation modernes activent la rotation : chaque fois que vous échangez votre refresh token contre un nouvel access token, l’ancien refresh token est invalidé sur-le-champ et un nouveau est émis. Un design sensé — assorti d’un filet de sécurité appelé détection de réutilisation : si le même refresh token se présente une deuxième fois, le serveur ne peut pas distinguer une nouvelle tentative réseau capricieuse d’un identifiant volé, alors il choisit la voie sûre, révoque l’intégralité du grant (tous les tokens de ce client et de cet utilisateur), puis vous demande de vous reconnecter.
C’est là qu’une installation normale rencontre des ennuis. Desktop, le dashboard et un gateway en arrière-plan peuvent tourner sur une seule machine, ou vous avez deux profiles, et ils partagent le même fichier de tokens dans ~/.hermes/mcp-tokens/linear.json. Quand l’access token expire, deux processus s’en aperçoivent au même moment et présentent chacun le même refresh token :
- Le processus A arrive le premier, reçoit une nouvelle paire, et l’ancien refresh token est invalidé.
- Le processus B suit à quelques centaines de millisecondes de là et présente ce refresh token désormais invalidé.
- Le serveur y voit un rejeu et révoque tout le grant — y compris les tokens sains et tout neufs que le processus A vient de recevoir.
Les deux processus tombent en panne, pour une raison qui, vue de l’extérieur, n’a aucun sens : ça marchait il y a une seconde. C’est cette fenêtre que la PR #111186 vient fermer.
Correctif un : une barrière inter-processus pour qu’un refresh token après rotation ne soit présenté qu’une seule fois
Hermes place désormais un verrou inter-processus à côté du fichier de tokens — une barrière. Le verrou vient du noyau (flock sous Linux et macOS, msvcrt.locking sous Windows) et il verrouille un descripteur de fichier, pas l’existence d’un fichier : un processus planté qui laisse traîner un fichier de verrou ne peut donc pas le tromper. Le verrou vit dans un fichier voisin nommé linear.json.refresh.lock.
Toute la séquence — lire le refresh token, faire un POST au serveur d’autorisation, réécrire le résultat sur disque — se déroule maintenant à l’intérieur de ce verrou, ce qui change le comportement ainsi :
- Relire le disque une fois le verrou tenu. Si un processus pair a terminé une rotation pendant l’attente, et que la paire qu’il a écrite est toujours valide, ce processus adopte les tokens frais du pair et saute le POST au lieu de présenter un token déjà dépensé.
- Si la paire du pair a expiré, ce processus envoie le refresh token du pair — le seul encore valide — plutôt que la copie périmée qu’il garde en mémoire.
- Une expiration absente compte comme valide. Quand le serveur n’annonce pas de durée de vie, Hermes ne suppose pas que le token est mort.
- Pas de verrou, pas de rafraîchissement. Au timeout de la barrière, le rafraîchissement se ferme en échec au lieu de parier sur un rejeu. Seuls les codes d’erreur de contention signifient « un pair détient la barrière » ; tout le reste échoue immédiatement plutôt que d’attendre.
Ce que cela change pour vous : faire tourner Desktop, le dashboard et un gateway en même temps est sans danger, tout comme avoir Linear connecté dans deux profiles. Les décrochages cessent d’être aléatoires et inexplicables — un rafraîchissement fonctionne, ou il échoue avec une erreur claire.
Correctif deux : les refresh tokens sont épinglés à l’émetteur qui les a accordés
Le danger suivant est plus discret. Un serveur d’autorisation OAuth annonce sa propre adresse d’identité dans ses métadonnées ; cette valeur est l’issuer. Hermes découvre ces métadonnées à partir de l’URL que publie le serveur MCP — et si ce qui se trouve derrière cette URL change de nature (migration de service, quelqu’un qui modifie les métadonnées, ou pire, un domaine détourné), Hermes remettrait un refresh token à longue durée de vie à un serveur qui ne l’a jamais émis. Un refresh token vaut bien plus qu’un access token ; ce transfert, c’est un identifiant de long terme confié à un inconnu.
Le correctif : Hermes enregistre un champ hermes_issuer à côté des tokens mis en cache, indiquant qui les a accordés à l’origine. Ce champ ne relève que de la comptabilité interne d’Hermes — il est retiré avant que le SDK ne valide le token et ne part jamais sur le réseau. À chaque initialisation d’une connexion, Hermes compare l’issuer stocké avec celui découvert à cet instant :
- Incohérence → le refresh token est supprimé, en mémoire comme sur disque, tandis que l’access token encore valide est conservé, car il reste valable pour le serveur qui l’a émis. Il continue de fonctionner jusqu’à son expiration, puis une réautorisation normale s’exécute contre le nouvel issuer.
- Correspondance → rien ne se passe, les tokens stockés continuent de fonctionner.
- Fichiers de tokens hérités sans issuer enregistré (toute installation antérieure à ce champ) → l’issuer découvert est adopté une fois et enregistré, au lieu de vous forcer à vous reconnecter. C’est un choix délibéré : Hermes compte beaucoup d’installations existantes dont les fichiers de tokens précèdent le champ, et les obliger toutes à se réauthentifier serait purement pénible.
À noter : cela s’écarte volontairement d’un changement cousin dans codex (openai/codex), qui échoue durement sur un issuer manquant parce que son magasin d’identifiants embarque ce champ depuis le premier jour.
Correctif trois : RFC 9207, et l’unique exception Figma
RFC 9207 est une extension OAuth : le serveur d’autorisation ajoute un paramètre iss quand il redirige le navigateur vers le client, déclarant « je suis bien qui je prétends être ». Grâce à lui, le client peut confirmer que le callback provient réellement du serveur d’autorisation auquel il fait confiance, et non d’un code d’autorisation injecté par un autre — une défense contre les attaques de confusion entre serveurs d’autorisation.
Il y avait ici deux modes de défaillance opposés, et Hermes a corrigé l’un de chaque :
Les relais laissaient tomber iss. Quand un serveur annonce prendre en charge ce paramètre dans ses métadonnées puis l’omet, le SDK sous-jacent rejette le callback avec OAuthFlowError: Authorization response missing iss parameter. Le chemin CLI l’a toujours transmis, mais les relais de callback du dashboard, de Desktop et du TUI ne portaient pas iss jusqu’au client MCP — si bien que face à un serveur réellement conforme à la RFC 9207 comme Cloudflare ou Resend, se connecter depuis les interfaces graphiques était une impasse. Chaque relais transmet désormais le paramètre.
Figma annonce ce qu’il n’envoie pas. Le serveur d’autorisation de Figma (https://api.figma.com) déclare prendre en charge iss et ne l’inclut jamais, si bien que chaque code d’autorisation valide était écarté par le SDK et que hermes mcp login figma ne pouvait jamais aboutir. Hermes ouvre une brèche pour exactement cet issuer : un callback portant un code mais pas d’iss voit la valeur manquante remplie depuis l’issuer des métadonnées découvertes, avec un avertissement consigné. Un iss présent mais différent reste rejeté, et tous les autres serveurs conservent la règle stricte. Aucun réglage à basculer, aucune nouvelle variable d’environnement.
Correctif quatre : une relance de réauthentification une fois par jour dans Desktop
Une fois qu’un token meurt, le serveur est mis au repos et n’essaie plus d’ouvrir un onglet de navigateur — personne n’est là pour cliquer sur Autoriser. C’est une bonne chose, sauf que cela rendait un token mort invisible : le contrôle de santé levait une notification d’avertissement au moment où l’état basculait, puis restait silencieux pour le reste de la session de l’application. Fermez cette notification le lundi et le serveur reste au repos jusqu’à ce que vous ouvriez par hasard la page Capabilities.
Desktop relance désormais une fois par jour : au moment de la transition vers un mauvais état, puis de nouveau une fois le report de 24 heures écoulé si le serveur est toujours en panne. Aucune relance tant que les tokens sont sains. La notification porte deux boutons :
- Sign in — renvoie vers le bouton d’authentification existant de la page MCP. Rien ne lance un flux OAuth en arrière-plan de sa propre initiative.
- Disable — appelle l’endpoint existant pour mettre
enabled: falsesur ce serveur. La tâche de réconciliation de config du gateway le démonte en une soixantaine de secondes, et un backendhermes servel’abandonne au prochain/reload-mcp.
Le report est enregistré par profile et par serveur, si bien qu’un serveur cassé ne peut pas vous harceler douze fois par jour, et qu’un serveur que vous avez déjà acquitté ne passe pas au silence pour toujours.
Quel PR a corrigé quoi
| PR | Label | Changement de comportement |
|---|---|---|
| #95246 | type/security (2026-09-14) | Les refresh tokens stockés ne peuvent plus être envoyés à un autre issuer : en cas d’incohérence le refresh token est supprimé et l’access token non expiré conservé ; les fichiers de tokens hérités adoptent l’issuer découvert une fois |
| #111186 | type/bug, P1 (2026-09-15) | Le rafraîchissement est encadré par une barrière inter-processus : un refresh token après rotation est présenté exactement une fois, la paire fraîche d’un pair est adoptée avec le POST sauté, et un timeout de la barrière se ferme en échec |
| #111628 | type/bug, P2 (2026-09-15) | La connexion OAuth aboutit face aux serveurs RFC 9207 depuis le dashboard, Desktop et le TUI (Cloudflare, Resend) |
| #112059 | type/bug, P2 (2026-09-15) | La connexion Figma aboutit malgré le paramètre iss omis ; la règle stricte est assouplie pour ce seul issuer |
| #110078 | type/feature (2026-09-13) | Desktop relance une fois par jour pour les serveurs MCP nécessitant une réauthentification, avec Sign in et Disable |
| #111481 | fix (2026-09-15) | Les profiles servis par le dashboard gardent leurs propres connexions MCP et identifiants au lieu d’emprunter la session d’un autre profile |
Que faire quand un serveur MCP vous redemande de vous connecter
Parcourez ces étapes dans l’ordre ; la plupart des cas se règlent en une ou deux étapes.
- Lisez le message avant de reconstruire quoi que ce soit. « Refresh failed » et « the issuer changed » sont deux classes de problèmes différentes — le premier est un token expiré ou révoqué, le second signifie que le serveur d’autorisation d’en face a déménagé.
- Identifiez le serveur concerné.
hermes mcp listmontre quels serveurs sont au repos, ethermes mcp test linearteste une connexion unique. - Réautorisez une fois.
hermes mcp login linearpour un serveur,hermes mcp reauth --allpour refaire tous les serveurs OAuth. Dans Desktop, cliquez simplement sur Sign in dans la notification. - Laissez la session voir les nouveaux tokens. Le jeu d’outils d’une session est figé à son démarrage, donc utilisez
/reload-mcpaprès la réautorisation, ou ouvrez une session neuve. - Mettez à jour les deux machines. La barrière est coopérative — elle exige que chaque processus y participe. Mettez à jour un hôte et l’autre continue de rejouer les tokens à l’ancienne : assurez-vous que les deux embarquent la PR #111186.
- Rappelez-vous qu’un gateway n’ouvre jamais de navigateur. Si vous faites tourner un gateway sur un serveur, un token mort ne laisse qu’un avertissement dans le log ; terminez le flux ailleurs et il se reconnectera à la prochaine sonde.
- En cas de doute, réinitialisez. Les fichiers de tokens vivent sous
~/.hermes/mcp-tokens/, un par nom de serveur. Supprimer le bon fichier et relancerhermes mcp loginest la remise à zéro la plus propre disponible. - Désactivez ce que vous n’utilisez plus. Personne n’a besoin d’un rappel quotidien pour une intégration abandonnée le trimestre dernier.
Si Hermes tourne sur un hôte distant sans navigateur, le flux est différent — le transfert de port SSH, le collage de l’URL de callback et les device codes sont couverts dans terminer OAuth sur un serveur distant. Pour les flags exacts des sous-commandes, voir la référence de la commande hermes mcp, et si vous cherchez d’abord à choisir quels serveurs connecter, le catalogue MCP distant officiel est un meilleur point de départ que des résultats de recherche au hasard.
La version courte
Deux des quatre correctifs sont le même principe vu sous deux angles : un identifiant ne va qu’au serveur qui l’a émis (liaison à l’issuer), et un refresh token après rotation est consommé exactement une fois (la barrière inter-processus). Le troisième impose l’identité de l’appelant sur chaque chemin que peut emprunter un callback (RFC 9207, avec l’unique exception de Figma), et le quatrième est un correctif humain — quand quelque chose réclame votre attention, dites-le, une fois par jour, avec un bouton qui fait le travail.
Concrètement, les décrochages MCP cessent de ressembler à de la malchance. Faites tourner Desktop, le dashboard et un gateway en même temps ; quand un serveur a réellement besoin de vous, vous le saurez le jour même au lieu de découvrir que tous vos outils sont cassés depuis mardi. hermes update récupère tout cela — les correctifs sont dans la ligne 0.21.x, et tout ce qui suit le patch de stabilité v0.21.2 les inclut déjà.