Skip to content

Allow configurable redirect_uri for MCP OAuth clients #23787

@tahsinrahman

Description

@tahsinrahman

Problem

opencode currently hardcodes the OAuth callback as http://127.0.0.1:19876/mcp/oauth/callback when connecting to a remote MCP server that requires OAuth. This prevents users from reusing OAuth clients whose redirect URIs are already pinned on the provider side.

Concrete example

The official Claude Code Slack plugin ships a Slack MCP client:

{
  "mcpServers": {
    "slack": {
      "type": "http",
      "url": "https://mcp.slack.com/mcp",
      "oauth": {
        "clientId": "1601185624273.8899143856786",
        "callbackPort": 3118
      }
    }
  }
}

Attempting to reuse the same clientId in opencode produces:

redirect_uri did not match any configured URIs.
Passed URI: http://127.0.0.1:19876/mcp/oauth/callback

In the browser URL: redirect_uri=http%3A%2F%2F127.0.0.1%3A19876%2Fmcp%2Foauth%2Fcallback

The Slack OAuth app has http://127.0.0.1:3118/... registered, so it rejects opencode's callback. The user has no way to override this without registering their own Slack app.

Proposed solution

Make the redirect URI user-configurable alongside clientId:

{
  "mcp": {
    "slack": {
      "type": "remote",
      "url": "https://mcp.slack.com/mcp",
      "oauth": {
        "clientId": "1601185624273.8899143856786",
        "redirectUri": "http://127.0.0.1:3118/mcp/oauth/callback"
      }
    }
  }
}

Implementation note

Configurable output alone isn't enough — opencode must also bind the local callback listener to the host/port/path parsed from redirectUri, otherwise the provider will redirect to a port opencode isn't listening on.

For the common case (http://127.0.0.1:<port>/<path>), this means:

  • Parse host, port, path from redirectUri
  • Bind the OAuth callback HTTP server to that host:port
  • Serve the callback route at that path
  • Send the same URI as redirect_uri in the authorization request

Benefits

  • Enables reuse of OAuth clients pinned to other tools (Claude Code, Cursor, etc.)
  • Lets users put opencode behind a reverse proxy with a stable external callback URL
  • Matches the existing pattern of making clientId configurable

Workarounds today

  1. Register your own app on the provider and use its clientId
  2. Rely on dynamic client registration (RFC 7591) if the MCP server supports it
  3. Use a different transport (e.g., a local stdio MCP server that proxies to the remote)

None of these are viable when the provider has a fixed OAuth app and no DCR support.

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)needs:complianceThis means the issue will auto-close after 2 hours.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions