Architecture Decision Record · 0020

Auth-Failure-Protokoll für autonome Figma-MCP-Läufe

TL;DR — Bei Figma-MCP-Auth-Failure: maximal zwei Versuche, dann sofortiger Abbruch mit strukturierter JSON-Message (status: "NEEDS_REAUTH"). Kein Polling, kein Retry-Loop, kein stilles Weitermachen mit gecachten Daten.

Kontext

Der Skill figma-autonomous-implementer (.claude/skills/figma-autonomous-implementer/) läuft nach einer einzigen User-Bestätigung der Slice-Liste headless durch fünf Phasen (Auth-Probe → Decompose → Implement-per-Slice → Visual-Verify-per-Slice → PR-Open). In mindestens drei dieser Phasen ist die Figma-MCP-Verbindung Pflicht-Quelle:

Die Figma-MCP-Auth läuft im Hintergrund über den Claude.ai-Connector und kann zwischen Aufrufen unbemerkt ablaufen (Token-Expiry, Cookie-Verlust, Connector-Fehlkonfiguration). Der Fehlertext im MCP-Tool-Result enthält dann konsistent eine der Substrings expired, 401, session, unauthenticated oder unauthorized — aber das exakte Wording schwankt zwischen Connector-Versionen und Endpoints.

In der Vergangenheit haben autonome MCP-Konsumenten in solchen Fällen drei Anti-Pattern gezeigt:

Im Solcom-Repo existiert bereits ein etabliertes Gegen-Pattern im .claude/agents/solcom-figma-reviewer.md-Reviewer-Agent (Sektion „Fallback-Fetch via Figma-MCP"):

„Pre-Flight für eigene Calls: whoami einmal, dann gezielt nachladen. Beim Fehlerfall (z. B. „session expired"): nicht retry-loopen, in residual_risks melden und an Caller zurück."

Das gibt die Richtung vor, ist aber für den Reviewer-Kontext formuliert (Reviewer hat einen Caller, der die Reauth selbst ausführen kann). Der figma-autonomous-implementer hat keinen Caller — er kommuniziert direkt mit dem User. Es braucht ein maschinenlesbares, smoke-test-bares Auth-Failure-Protokoll.

Entscheidung

Bei Figma-MCP-Auth-Failure bricht der figma-autonomous-implementer nach maximal zwei Versuchen ab und gibt eine einzige strukturierte JSON-Message mit status: "NEEDS_REAUTH" aus. Kein dritter Versuch, kein Polling, kein User-Prompt mit der Frage „soll ich nochmal?".

Das Protokoll hat fünf Bestandteile:

1. Trigger-Substrings (eindeutig auth, kein anderer Fehlertyp)

Der Skill klassifiziert einen Fehler als Auth-Failure genau dann, wenn der Roh-Text der MCP-Tool-Antwort (case-insensitive) eine der folgenden Substrings enthält:

expired
401
session
unauthenticated
unauthorized

Andere Fehler (timeout, 429, 503, generische Netzwerkfehler ohne Auth-Substring) sind nicht Auth-Failure — sie laufen in den parallelen NEEDS_FIGMA_TRANSPORT-Pfad, das ist Domäne des Skills, nicht dieses ADRs.

2. Retry-Cap: genau zwei Versuche

Begründung 2-statt-3: ein dritter Versuch fügt keine Information hinzu, die ein zweiter nicht schon liefert. Drei Versuche kosten doppelten Wartetask, ohne den NEEDS_REAUTH-Pfad zu verbessern.

3. Output-JSON-Schema (Pflicht-Felder, fixe Reihenfolge)

{
  "status": "NEEDS_REAUTH",
  "service": "figma-mcp",
  "attempts": 2,
  "trigger": "<wörtlicher Auszug aus dem Fehlertext, max 200 Zeichen>",
  "instruction": "Figma-MCP-Verbindung im Claude.ai-Connector neu autorisieren, dann denselben /figma-autonomous-implement-Aufruf wiederholen."
}

Der Smoke-Test (smoke/run.sh) parst dieses JSON mit jq und prüft die fünf Pflichtfelder + status === "NEEDS_REAUTH".

4. Single-Message-Discipline

Der Skill gibt genau eine Message mit dem JSON aus, dann beendet er den Lauf. Keine nachfolgende Erklärung, keine zweite Message mit Tipps, keine AskUserQuestion mit Optionen. Das JSON ist parse-fähig — Sekundärtext wäre Rauschen für Skript-Wrapper.

5. Resume-Pfad

Nach erfolgter Reauth ruft der User denselben Aufruf erneut. Der Skill startet bei Phase 1, nicht in der Mitte. Zwischen Auth-Verlust und Reauth kann sich am Figma-Frame etwas geändert haben; ein Mid-Resume mit gecachten Decomp-Daten würde gegen einen veralteten State bauen.

Rejected alternatives

Drei Versuche statt zwei rejected

Auth-Tokens drehen nicht in Sekunden ihre Gültigkeit hin und her. Drei Versuche fügen eine Sekunde Wartezeit hinzu, aber keine echte Recovery-Wahrscheinlichkeit. Plus: der Smoke-Test-Schwellwert wird unschärfer.

Exponentielles Backoff (1s → 2s → 4s → …) rejected

Verzögert nur den Anti-Pattern „User wartet, bis er endlich erfährt, dass er reauthen muss". Auth-Failures sind kein Rate-Limit-Problem, das Pause helfen würde.

AskUserQuestion-Prompt im Loop rejected

„Auth ist abgelaufen — soll ich's nochmal versuchen?" — Verworfen aus zwei Gründen: verletzt die Single-Allowed-Prompt-Discipline des Skills; die Antwort ist immer dieselbe (Reauth + Retry), also ist die Frage Konversations-Theater.

Auto-Reauth via OAuth-Refresh-Token rejected

Der Claude-Code-Connector steuert den Refresh-Pfad zentral. Ein Skill-eigener Refresh würde den Connector-Zustand desynchronisieren. Der Reauth-Pfad ist UI-getrieben (User klickt im Connector-Dialog), nicht skript-getrieben.

Stilles Weitergehen mit dem letzten gültigen Decomp-Output rejected

Nach Auth-Verlust kann der Skill nicht mehr garantieren, dass get_screenshot (Phase 4) einen aktuellen Frame liefert. Visual-Verify gegen einen Schatten-Frame ist schlechter als gar kein Visual-Verify, weil es scheinbar grünes Verifikations-Theater liefert.

Eigene Status-Werte pro Tool (AUTH_FAILED_WHOAMI, …) rejected

Die User-Action ist immer dieselbe (im Connector reauthen). Die Tool-Granularität gehört in trigger als Roh-Beweis, nicht in einen aufgeblähten Status-Enum-Raum.

Strukturlose Freitext-Message rejected

„Ich konnte nicht auf Figma zugreifen, bitte reauthenticate" — nicht maschinen-parsbar. Der Smoke-Test könnte nur grep'pen, was fehleranfällig ist; ein Skript-Wrapper könnte den Output gar nicht abfangen.

Skill bei Auth-Failure stumm beenden ohne JSON rejected

Der User hätte keine Diagnose, was passiert ist. Ein stummer Exit würde sich nicht von einem normalen Lauf-Ende unterscheiden lassen.

Consequences