fix(mcp): reliable OAuth across runtimes and honest pre-restart UI

MCP authorization was broken in several stacked ways. The browser return
leg landed on the SPA behind the auth gate, so the system browser saw a
login page instead of finishing; the pending-context store silently
saved nothing because its route had no JSON body parser; and the
callback-URL config write started deferring behind Apply & Restart, so
authorization ran against a runtime without the URL and dead-ended on
OpenCode's loopback listener.

The return leg is now completed entirely server-side by an
unauthenticated GET /mcp/oauth/callback that only forwards a code whose
state matches a parked context. Desktop with the local server and VS
Code switch to OpenCode's native flow over its fixed loopback port —
no config writes or restarts at all, with a one-time cleanup of the
previously written callback URL — and its completion signal drives the
page instead of blind status polling. Remote, hosted-web, and mobile
keep the server-callback flow, applying a queued callback-URL write
immediately since authorization cannot wait for a manual restart.

Also: a server queued behind Apply & Restart now shows an Awaiting
restart badge and explanation instead of connect/reauthorize buttons
that can only fail, and Reauthorize is offered only while the server is
actually connected.
This commit is contained in:
Bohdan Triapitsyn
2026-08-10 20:23:45 +03:00
parent 3feee346da
commit 75978cf188
19 changed files with 466 additions and 22 deletions
@@ -1459,6 +1459,8 @@ export const settingsDict = {
'settings.mcp.page.status.label.failed': '실패',
'settings.mcp.page.status.label.needsAuth': '인증 필요',
'settings.mcp.page.status.label.needsRegistration': '등록 필요',
'settings.mcp.page.status.label.awaitingRestart': '재시작 대기 중',
'settings.mcp.page.status.description.awaitingRestart': '이 서버는 저장되었지만 아직 적용되지 않았습니다. Apply & Restart를 사용해 OpenCode에 불러오면 연결과 인증을 사용할 수 있습니다.',
'settings.mcp.page.status.description.connected': '연결되었습니다. OpenCode가 도구와 리소스를 찾을 준비가 됐습니다.',
'settings.mcp.page.status.description.failedDefault': 'OpenCode가 이 MCP 서버에 연결할 수 없습니다.',
'settings.mcp.page.status.description.needsAuth': '이 원격 MCP 서버는 연결 전에 권한 부여가 필요합니다.',