Ziel
opencode (und andere DCR-fähige MCP-Clients) bleiben nach Server-Neustart verbunden, ohne dass der User erneut mcp auth ausführen muss.
Warum jetzt
Jeder Server-Neustart führt zu "MCP Authentication Required" bei opencode, weil die DCR-Client-Registrierung flüchtig im RAM gehalten wird. Sobald der Authorize-Endpoint die gespeicherte client_id nicht mehr kennt, lehnt er den Request mit unauthorized_client ab.
Aktueller Stand / Evidenz
internal/mcp/serverbootstrap/oauth.go:20-47 — oauthClientStore ist ein reiner In-Memory-Map, kein Load()/Save(), kein Persistenz-Pfad
internal/mcp/serverbootstrap/http.go:184 — newOAuthClientStore() ohne vaultDir-Parameter
internal/mcp/token.go:144-213 — TokenRegistry zeigt das existierende AtomicWrite-Pattern (Referenzimplementierung)
internal/mcp/serverbootstrap/oauth.go:164-171 — Bei unbekannter client_id: unauthorized_client statt RFC-konformem invalid_client, kein WWW-Authenticate-Header mit Registration-Hint
In Scope
oauthClientStore persistent machen: JSON-Datei <vault-dir>/mcp-oauth-clients.json mit Schema {"version": 1, "clients": {"<client_id>": {...}}}
Load()/Save()-Methoden analog zu TokenRegistry (fileutil.AtomicWriteFile, 0o600)
put() ruft am Ende Save() (best-effort; Fehler logged, lässt Register-Request nicht scheitern)
- Factory
loadOAuthClientStore(vaultDir) in http.go statt newOAuthClientStore()
- Fehlersignale verbessern:
invalid_client statt unauthorized_client, WWW-Authenticate-Header mit Registration-Hint
- Bestehenden 5-min-Cleanup in
http.go:78-80 optional für client-TTL-Cleanup nutzen
- Optionale TTL für Client-Einträge (default: unbegrenzt)
Out of Scope
oauthCodeStore-Persistenz (Auth-Codes haben 5 min TTL, one-shot — Restart während aktivem Flow ist Edge-Case)
- Migration auf SQLite (Filesystem-JSON reicht)
- Refresh-Token-Grant (eigenes Issue)
- Konfigurierbare TTLs (eigenes Issue)
Akzeptanzkriterien
Risiken / Abhängigkeiten
- Baut auf
fileutil.AtomicWriteFile auf (bereits in token.go verwendet)
- Ändert das Verhalten des Authorize-Fehlersignals — opencode muss den neuen
invalid_client-Error und Header parsen können
Ziel
opencode (und andere DCR-fähige MCP-Clients) bleiben nach Server-Neustart verbunden, ohne dass der User erneut
mcp authausführen muss.Warum jetzt
Jeder Server-Neustart führt zu "MCP Authentication Required" bei opencode, weil die DCR-Client-Registrierung flüchtig im RAM gehalten wird. Sobald der Authorize-Endpoint die gespeicherte
client_idnicht mehr kennt, lehnt er den Request mitunauthorized_clientab.Aktueller Stand / Evidenz
internal/mcp/serverbootstrap/oauth.go:20-47—oauthClientStoreist ein reiner In-Memory-Map, keinLoad()/Save(), kein Persistenz-Pfadinternal/mcp/serverbootstrap/http.go:184—newOAuthClientStore()ohnevaultDir-Parameterinternal/mcp/token.go:144-213—TokenRegistryzeigt das existierende AtomicWrite-Pattern (Referenzimplementierung)internal/mcp/serverbootstrap/oauth.go:164-171— Bei unbekannterclient_id:unauthorized_clientstatt RFC-konformeminvalid_client, keinWWW-Authenticate-Header mit Registration-HintIn Scope
oauthClientStorepersistent machen: JSON-Datei<vault-dir>/mcp-oauth-clients.jsonmit Schema{"version": 1, "clients": {"<client_id>": {...}}}Load()/Save()-Methoden analog zuTokenRegistry(fileutil.AtomicWriteFile,0o600)put()ruft am EndeSave()(best-effort; Fehler logged, lässt Register-Request nicht scheitern)loadOAuthClientStore(vaultDir)inhttp.gostattnewOAuthClientStore()invalid_clientstattunauthorized_client,WWW-Authenticate-Header mit Registration-Hinthttp.go:78-80optional für client-TTL-Cleanup nutzenOut of Scope
oauthCodeStore-Persistenz (Auth-Codes haben 5 min TTL, one-shot — Restart während aktivem Flow ist Edge-Case)Akzeptanzkriterien
oauth_persistence_test.go: registriere Client →Save()→ frisch geladener Store →get(clientID)liefert Clientserverbootstrap_test.go: Client registrieren, Server stoppen+neustarten, Authorize-Request alterclient_id→ successmcp-oauth-clients.jsonexistiert nach Registrierung mit0o600-PermissionsSave()logged ins Audit-Log, blockiert nicht den Register-Requestclient_id→invalid_client+WWW-Authenticate-Header mit Registration-HintRisiken / Abhängigkeiten
fileutil.AtomicWriteFileauf (bereits intoken.goverwendet)invalid_client-Error und Header parsen können