Warum deine MCP-Tools dich ständig ausloggen: Der OAuth-Token-Lebenszyklus von innen


Du hast letzten Monat Linear, Figma und Notion in Hermes eingebunden. hermes mcp list zeigte drei gesunde Server, und jeder Tool-Aufruf funktionierte einfach. Heute Morgen antwortet jedes MCP-Tool mit einem Autorisierungsfehler – Desktop hat gestern Abend kurz einen Toast eingeblendet, du warst in einem Meeting und hast ihn weggeklickt. Du authentifizierst Linear neu, und bis zum Nachmittag ist auch Figma ausgefallen. Das Muster wiederholt sich fast wöchentlich, wann immer Hermes sowohl auf deinem Laptop als auch auf dem Bürorechner läuft, und langsam fragst du dich, was du falsch konfiguriert hast. Wahrscheinlich nichts: Der Fehler liegt im langen Token-Lebenszyklus hinter diesen Verbindungen, und Hermes hat gerade vier Fixes ausgeliefert, die genau darauf zielen.

Der Handshake hinter diesen Tools: Access Tokens, Refresh Tokens und „einmal verwenden“

MCP (Model Context Protocol) ist das Protokoll, mit dem Hermes Tools aufruft, die ein externer Dienst bereitstellt; Linear, Figma und Notion akzeptieren keinen statischen API-Key, sondern vergeben Zugangsdaten über OAuth. Wenn du einen OAuth-Login abschließt, bekommst du zwei Dinge:

  • Ein Access Token – kurzlebig, meist dreißig bis sechzig Minuten. Es reist bei jedem Tool-Aufruf mit.
  • Ein Refresh Token – langlebig. Seine einzige Aufgabe ist es, dir ein neues Access Token zu kaufen.

Damit ein gestohlenes Refresh Token schnell wertlos wird, aktivieren moderne Autorisierungsserver die Rotation: Jedes Mal, wenn du dein Refresh Token gegen ein neues Access Token tauschst, wird das alte Refresh Token auf der Stelle ungültig und ein frisches ausgegeben. Vernünftiges Design – und es bringt ein Sicherheitsnetz namens Reuse Detection mit: Taucht dasselbe Refresh Token ein zweites Mal auf, kann der Server einen wackligen Netzwerk-Retry nicht von einem gestohlenen Credential unterscheiden, also wählt er den sicheren Weg und widerruft den gesamten Grant (jedes Token für diesen Client und diesen Nutzer) und verlangt dann eine erneute Anmeldung.

Genau hier bekommt eine normale Installation Probleme. Desktop, das Dashboard und ein Hintergrund-Gateway können alle auf einer Maschine laufen, oder du hast zwei Profile, und sie teilen dieselbe Token-Datei unter ~/.hermes/mcp-tokens/linear.json. Wenn das Access Token abläuft, bemerken das zwei Prozesse ungefähr im selben Moment, und jeder präsentiert dasselbe Refresh Token:

  1. Prozess A ist zuerst da, erhält ein frisches Paar, und das alte Refresh Token wird für ungültig erklärt.
  2. Prozess B ist ein paar hundert Millisekunden später dran und präsentiert das nun ungültige Refresh Token.
  3. Der Server wertet das als Replay und widerruft den gesamten Grant – einschließlich der gesunden, brandneuen Tokens, die Prozess A gerade bekommen hat.

Beide Prozesse gehen dunkel, aus einem Grund, der von außen wie Unsinn aussieht: Vor einer Sekunde hat es doch funktioniert. Dieses Fenster zu schließen, darum geht es in PR #111186.

Fix eins: eine prozessübergreifende Fence, damit ein rotiertes Refresh Token genau einmal präsentiert wird

Hermes legt jetzt einen prozessübergreifenden Lock neben die Token-Datei – eine Fence. Der Lock kommt vom Kernel (flock unter Linux und macOS, msvcrt.locking unter Windows) und er sperrt ein Datei-Handle, nicht die Existenz einer Datei, sodass ein abgestürzter Prozess, der eine verwaiste Lock-Datei hinterlässt, ihn nicht täuschen kann. Der Lock liegt in einer Nachbardatei mit einem Namen wie linear.json.refresh.lock.

Die ganze Sequenz – Refresh Token lesen, POST an den Autorisierungsserver, Ergebnis zurück auf die Platte schreiben – läuft jetzt innerhalb dieses Locks, was das Verhalten so ändert:

  • Die Platte neu lesen, sobald der Lock gehalten wird. Hat ein Peer-Prozess eine Rotation abgeschlossen, während wir gewartet haben, und ist das zurückgeschriebene Paar noch gültig, dann übernimmt dieser Prozess einfach die frischen Tokens des Peers und überspringt den POST, statt ein Token zu präsentieren, das schon verbraucht ist.
  • Ist das Paar des Peers abgelaufen, sendet dieser Prozess den Refresh Token des Peers – das ist der einzig aktuell gültige – statt der veralteten Kopie im eigenen Speicher.
  • Eine fehlende Ablaufzeit gilt als gültig. Wenn der Server keine Lebensdauer angibt, nimmt Hermes nicht an, dass das Token tot ist.
  • Kein Lock, kein Refresh. Beim Fence-Timeout schlägt der Refresh fail-closed fehl, statt auf ein Replay zu setzen. Nur Contention-Fehlercodes bedeuten „ein Peer hält die Fence“; alles andere schlägt schnell fehl, statt zu warten.

Was das für dich bedeutet: Desktop, das Dashboard und ein Gateway gleichzeitig laufen zu lassen ist sicher, und Linear in zwei Profilen verbunden zu haben ebenfalls. Ausfälle sind nicht mehr zufällig und unerklärlich – ein Refresh funktioniert entweder, oder er schlägt mit einem klaren Fehler fehl.

Fix zwei: Refresh Tokens sind an den Issuer gebunden, der sie ausgestellt hat

Die nächste Gefahr ist leiser. Ein OAuth-Autorisierungsserver gibt in seinen Metadaten seine eigene Identitätsadresse bekannt; dieser Wert ist der Issuer. Hermes entdeckt diese Metadaten über die URL, die der MCP-Server veröffentlicht – und wenn das, was hinter dieser URL steckt, den Charakter wechselt (eine Dienstmigration, jemand, der die Metadaten bearbeitet, oder schlimmer: eine gekaperte Domain), würde Hermes ein langlebiges Refresh Token an einen Server geben, der es nie ausgestellt hat. Ein Refresh Token ist weit mehr wert als ein Access Token; diese Übergabe ist ein langfristiges Credential an einen Fremden.

Der Fix: Hermes zeichnet zusammen mit den gecachten Tokens ein Feld hermes_issuer auf, das festhält, wer sie ursprünglich ausgestellt hat. Dieses Feld ist reine Hermes-Buchhaltung – es wird entfernt, bevor das SDK das Token validiert, und geht nie über die Leitung. Jedes Mal, wenn Hermes eine Verbindung initialisiert, vergleicht es den gespeicherten Issuer mit dem aktuell entdeckten:

  • Mismatch → das Refresh Token wird verworfen, im Speicher und auf der Platte, während das noch gültige Access Token behalten wird, weil es für den Server, der es ausgestellt hat, weiterhin gültig ist. Es funktioniert bis zum Ablauf weiter, und danach läuft eine normale Neuautorisierung gegen den neuen Issuer.
  • Match → nichts passiert, die gespeicherten Tokens funktionieren weiter.
  • Legacy-Token-Dateien ohne aufgezeichneten Issuer (jede Installation, die älter ist als das Feld) → der aktuell entdeckte Issuer wird einmalig übernommen und aufgezeichnet, statt dich in einen erzwungenen Re-Login zu schicken. Das ist eine bewusste Entscheidung: Hermes hat viele bestehende Installationen, deren Token-Dateien älter sind als das Feld, und sie alle zur Neuauthentifizierung zu zwingen wäre reine Schikane.

Erwähnenswert: Das weicht absichtlich von einer verwandten Änderung in codex (openai/codex) ab, die bei fehlendem Issuer hart fehlschlägt, weil ihr Credential-Store das Feld von Anfang an mitgebracht hat.

Fix drei: RFC 9207 und die eine Figma-Ausnahme

RFC 9207 ist eine OAuth-Erweiterung: Der Autorisierungsserver hängt beim Redirect des Browsers zurück zum Client einen iss-Parameter an und erklärt damit „Ich bin, wer ich zu sein behaupte“. Damit kann der Client bestätigen, dass der Callback wirklich von dem Autorisierungsserver kam, dem er vertraut, und nicht ein Authorization Code, den ein anderer eingeschleust hat – eine Verteidigung gegen Mix-up-Angriffe auf Autorisierungsserver.

Hier gab es zwei gegensätzliche Fehlerbilder, und Hermes hat je eines davon behoben:

Die Relays ließen iss fallen. Wenn ein Server in seinen Metadaten Unterstützung für diesen Parameter ankündigt und ihn dann weglässt, lehnt das zugrunde liegende SDK den Callback mit OAuthFlowError: Authorization response missing iss parameter ab. Der CLI-Pfad reichte ihn immer durch, aber die Callback-Relays von Dashboard, Desktop und TUI trugen iss nicht bis zum MCP-Client – gegen einen ordentlich RFC-9207-konformen Server wie Cloudflare oder Resend war die Anmeldung über die grafischen Oberflächen also eine Sackgasse. Jetzt reicht jedes Relay den Parameter weiter.

Figma kündigt an, was es nicht sendet. Figmas Autorisierungsserver (https://api.figma.com) behauptet Unterstützung für iss und fügt ihn nie hinzu, sodass jeder gültige Authorization Code vom SDK verworfen wurde und hermes mcp login figma nie abschließen konnte. Hermes öffnet ein Loch für genau diesen einen Issuer: Ein Callback, der einen Code, aber kein iss mitbringt, bekommt den fehlenden Wert aus dem entdeckten Metadaten-Issuer gefüllt, mit einer protokollierten Warnung. Ein vorhandenes, aber abweichendes iss wird weiterhin abgelehnt, und jeder andere Server behält die strikte Regel. Es gibt keinen Schalter und keine neue Umgebungsvariable.

Fix vier: ein einmal täglicher Re-Auth-Hinweis in Desktop

Wenn ein Token stirbt, wird der Server geparkt und versucht nicht mehr, einen Browser-Tab zu öffnen – niemand sitzt dort, um auf Allow zu klicken. Das ist gut, außer dass es ein totes Token unsichtbar machte: Der Health Check zeigte in dem Moment, in dem der Zustand umsprang, einen Warn-Toast und blieb dann für den Rest der App-Session still. Klickst du diesen Toast am Montag weg, bleibt der Server geparkt, bis du zufällig die Capabilities-Seite öffnest.

Desktop stupst jetzt stattdessen einmal am Tag an: beim Übergang in einen schlechten Zustand und noch einmal, wenn der 24-Stunden-Snooze abgelaufen ist und der Server immer noch kaputt ist. Solange die Tokens gesund sind, wird nie angestupst. Der Toast trägt zwei Buttons:

  • Sign in – führt zum bestehenden Authenticate-Button der MCP-Seite. Nichts startet von sich aus einen OAuth-Flow im Hintergrund.
  • Disable – ruft den bestehenden Endpunkt auf, der für diesen Server enabled: false setzt. Die Config-Reconcile-Chore des Gateways baut ihn innerhalb von etwa 60 Sekunden ab, und ein hermes serve-Backend lässt ihn beim nächsten /reload-mcp fallen.

Der Snooze wird pro Profile und pro Server aufgezeichnet, damit ein kaputter Server dich nicht ein Dutzend Mal am Tag nervt und ein Server, den du schon zur Kenntnis genommen hast, nicht für immer verstummt.

Welcher PR was behoben hat

PR Label Verhaltensänderung
#95246 type/security (2026-09-14) Gespeicherte Refresh Tokens können nicht mehr an einen anderen Issuer gesendet werden: Bei Mismatch wird das Refresh Token verworfen und das nicht abgelaufene Access Token behalten; Legacy-Token-Dateien übernehmen den entdeckten Issuer einmalig
#111186 type/bug, P1 (2026-09-15) Der Refresh ist prozessübergreifend abgesichert: Ein rotiertes Refresh Token wird genau einmal präsentiert, das frische Paar eines Peers wird unter Überspringen des POST übernommen, und ein Fence-Timeout schlägt fail-closed fehl
#111628 type/bug, P2 (2026-09-15) Der OAuth-Login läuft gegen RFC-9207-Server aus dem Dashboard, aus Desktop und aus der TUI durch (Cloudflare, Resend)
#112059 type/bug, P2 (2026-09-15) Der Figma-Login läuft trotz des weggelassenen iss-Parameters durch; die strikte Regel wird nur für diesen einen Issuer gelockert
#110078 type/feature (2026-09-13) Desktop stupst einmal täglich für MCP-Server an, die eine Neu-Authentifizierung brauchen, mit Sign in und Disable
#111481 fix (2026-09-15) Über das Dashboard ausgelieferte Profile behalten ihre eigenen MCP-Verbindungen und Credentials, statt die Session eines anderen Profiles auszuleihen

Was zu tun ist, wenn ein MCP-Server dich erneut um Anmeldung bittet

Arbeite diese Punkte der Reihe nach ab; die meisten Fälle lösen sich in ein bis zwei Schritten.

  1. Lies die Meldung, bevor du irgendetwas neu baust. „Refresh failed“ und „der Issuer hat sich geändert“ sind unterschiedliche Problemklassen – das erste ist ein abgelaufenes oder widerrufenes Token, das zweite bedeutet, dass der Autorisierungsserver auf der anderen Seite umgezogen ist.
  2. Finde den betroffenen Server. hermes mcp list zeigt, welche Server geparkt sind, und hermes mcp test linear prüft eine einzelne Verbindung.
  3. Autorisiere einmal neu. hermes mcp login linear für einen Server, hermes mcp reauth --all, um jeden OAuth-Server neu aufzusetzen. In Desktop klickst du einfach im Toast auf Sign in.
  4. Lass die Session die neuen Tokens sehen. Das Tool-Set einer Session wird beim Start eingefroren, also nutze /reload-mcp nach dem Neuautorisieren, oder öffne eine frische Session.
  5. Aktualisiere beide Maschinen. Die Fence ist kooperativ – sie braucht jeden Prozess als Teilnehmer. Aktualisiere einen Host, und der andere spielt Tokens weiter auf die alte Art ab, also stelle sicher, dass beide PR #111186 enthalten.
  6. Denk daran, dass ein Gateway nie einen Browser öffnet. Betreibst du ein Gateway auf einem Server, hinterlässt ein totes Token nur eine Warnung im Log; schließe den Flow woanders ab, und es verbindet sich beim nächsten Probe wieder.
  7. Im Zweifel zurücksetzen. Token-Dateien liegen unter ~/.hermes/mcp-tokens/, eine pro Servername. Die richtige zu löschen und hermes mcp login erneut auszuführen ist der sauberste verfügbare Reset.
  8. Deaktiviere, was du nicht mehr nutzt. Niemand braucht eine tägliche Erinnerung an eine Integration, die du letztes Quartal aufgegeben hast.

Wenn Hermes auf einem Remote-Host ohne Browser lebt, ist der Flow ein anderer – SSH-Port-Forwarding, das Zurückkopieren der Callback-URL und Device Codes sind in OAuth auf einem Remote-Server abschließen beschrieben. Für die genauen Subcommand-Flags siehe die hermes mcp Kommandoreferenz, und wenn du überhaupt erst kuratierst, welche Server du verbinden willst, ist der offizielle Remote-MCP-Katalog ein besserer Ausgangspunkt als zufällige Suchergebnisse.

Die Kurzfassung

Zwei der vier Fixes sind dasselbe Prinzip aus zwei Blickwinkeln: Ein Credential geht nur an den Server, der es ausgestellt hat (Issuer-Bindung), und ein rotiertes Refresh Token wird genau einmal verbraucht (die prozessübergreifende Fence). Der dritte erzwingt die Identität des Aufrufers auf jedem Pfad, den ein Callback nehmen kann (RFC 9207, mit Figmas einziger Ausnahme), und der vierte ist ein menschlicher Patch – wenn etwas deine Aufmerksamkeit braucht, sag es, einmal am Tag, mit einem Button, der es erledigt.

In der Praxis heißt das: MCP-Ausfälle fühlen sich nicht mehr wie Pech an. Betreibe Desktop, das Dashboard und ein Gateway gleichzeitig; wenn ein Server dich wirklich braucht, erfährst du es am selben Tag, statt zu entdecken, dass jedes Tool seit Dienstag kaputt ist. hermes update holt das alles ab – die Fixes sind in der 0.21.x-Linie, und alles nach dem v0.21.2-Stabilitätspatch enthält sie bereits.