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
- Register your own app on the provider and use its clientId
- Rely on dynamic client registration (RFC 7591) if the MCP server supports it
- 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.
Problem
opencode currently hardcodes the OAuth callback as
http://127.0.0.1:19876/mcp/oauth/callbackwhen 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
clientIdin opencode produces:In the browser URL:
redirect_uri=http%3A%2F%2F127.0.0.1%3A19876%2Fmcp%2Foauth%2FcallbackThe 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:redirectUriredirect_uriin the authorization requestBenefits
clientIdconfigurableWorkarounds today
None of these are viable when the provider has a fixed OAuth app and no DCR support.