You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: MCP OAuth paste-code fallback for headless/SSH sessions #54495
I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Re-requesting #36386, which the stale bot closed without review. I have a working implementation ready.
Problem.opencode mcp auth on a headless/SSH host starts a loopback listener on 127.0.0.1:<port>, but the browser runs on another machine, so the redirect never arrives. Without SSH port-forwarding there is no way to finish OAuth.
opencode mcp auth <name> --no-browser runs a listener-free attempt: the CLI prints the authorization URL and waits for the user to paste the redirect URL (or the bare authorization code) back. It rides on the existing code mode of the integration OAuth protocol — no API change.
A pasted URL is bound to the attempt's state before any token exchange; iss is forwarded; an error/error_description in the URL fails the attempt. A bare code is accepted as-is, matching the trust level of the existing provider paste-code logins. This folds in the state-binding that fix(opencode): secure manual MCP OAuth callback #33725 was going to add — it was closed unmerged, and per the [FEATURE]: MCP OAuth paste-code fallback for headless/SSH sessions #36386 discussion that work then belongs in the paste-fallback PR.
The default loopback flow is unchanged.
Questions for design review:
Explicit --no-browser flag (gcloud-style) vs. automatic fallback when no local browser is reachable? I went with the flag because "the browser is on another machine" is not detectable.
The flag currently reaches core through the OAuth method's form answer ({ noBrowser: true }) to avoid a protocol change; happy to make it a first-class field on integration.oauth.connect if you prefer.
Accept a bare code without state, or always require the full callback URL?
@GyeongHoKim offered to take #36386 — happy to hand this over or collaborate; the fork is there either way. cc @rekram1-node
Thanks for filing this again and for putting together a working implementation!
To restate: you're asking for a listener-free option for opencode mcp auth (e.g. --no-browser). The CLI would print the authorization URL and accept the pasted redirect URL (checked against the attempt's state) or a bare code. That would let MCP OAuth finish on headless or SSH hosts without port forwarding. Maintainers will consider it, including your three design questions (explicit flag vs. automatic fallback, form answer vs. a first-class field on integration.oauth.connect, and whether to accept a bare code).
A related use case for this headless OAuth request is a client with its own HTTP server in front of opencode serve. When MCP oauth.redirect_uri points to that front server, it receives the browser's callback and needs to deliver it to the specific attempt that started authorization.
In v2.0.29, native MCP OAuth returns mode: auto. The existing per-attempt .../complete route accepts only an optional code; it cannot submit the OAuth response to that automatic attempt. The attempt reply also has no separate handle for its bound loopback listener. The authorization URL can contain redirect_uri and state, but a public redirect URI does not identify the loopback endpoint. See the MCP flow, automatic attempt, and completion handler.
A forwarder cannot prove the listener it reaches belongs to the attempt.
The requested addition is either:
A route addressed by integration and attempt ID that accepts the OAuth response parameters, including code, state, iss, error, and error_description. It would deliver them through the existing validation and completion flow and reject attempts that are no longer pending.
A listener handle returned for each attempt, such as its bound address and an unguessable path, that a front server can use to reach that attempt.
The existing local loopback flow could continue unchanged. The listener-free code mode proposed here and delivery to an existing auto attempt are different cases worth considering in the same design discussion. Earlier external-callback proposals include #34626 and #21704.
Activity
opencode-agent commentedon Oct 11, 2026
Thanks for filing this again and for putting together a working implementation!
To restate: you're asking for a listener-free option for
opencode mcp auth(e.g.--no-browser). The CLI would print the authorization URL and accept the pasted redirect URL (checked against the attempt'sstate) or a bare code. That would let MCP OAuth finish on headless or SSH hosts without port forwarding. Maintainers will consider it, including your three design questions (explicit flag vs. automatic fallback, form answer vs. a first-class field onintegration.oauth.connect, and whether to accept a bare code).Related issues and PRs:
opencode mcp authfails to open a browser for the OAuth flowopencode webbehind a reverse proxy: OAuth callbacks should use the external URL, notlocalhost:1455#24455: OAuth callbacks behind a reverse proxy should use the external URLSkeptomenos commentedon Oct 15, 2026
Describe the enhancement you want to request
A related use case for this headless OAuth request is a client with its own HTTP server in front of
opencode serve. When MCPoauth.redirect_uripoints to that front server, it receives the browser's callback and needs to deliver it to the specific attempt that started authorization.In v2.0.29, native MCP OAuth returns
mode: auto. The existing per-attempt.../completeroute accepts only an optionalcode; it cannot submit the OAuth response to that automatic attempt. The attempt reply also has no separate handle for its bound loopback listener. The authorization URL can containredirect_uriandstate, but a public redirect URI does not identify the loopback endpoint. See the MCP flow, automatic attempt, and completion handler.A forwarder cannot prove the listener it reaches belongs to the attempt.
The requested addition is either:
code,state,iss,error, anderror_description. It would deliver them through the existing validation and completion flow and reject attempts that are no longer pending.The existing local loopback flow could continue unchanged. The listener-free
codemode proposed here and delivery to an existingautoattempt are different cases worth considering in the same design discussion. Earlier external-callback proposals include #34626 and #21704.