Skip to content

[FEATURE]: MCP OAuth paste-code fallback for headless/SSH sessions #54495

Description

@enikey87
  • 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.

Proposed design (implemented in a fork: compare):

  • 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:

  1. 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.
  2. 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.
  3. 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

Activity

  1. opencode-agent commented on Oct 11, 2026

    @opencode-agent
    Contributor

    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).

    Related issues and PRs:

  2. Skeptomenos commented on Oct 15, 2026

    @Skeptomenos

    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 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.

  3. self-assigned this
    on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions