Repository navigation
Description
Summary
OpenCode's built-in MCP OAuth flow cannot complete against authorization servers that only accept HTTPS redirect_uri values (e.g. iManage Cloud). Registration succeeds only with an https:// loopback, but the local callback server speaks plain HTTP, so the browser redirect fails the TLS handshake.
Environment
- opencode version: 2.0.28 (channel
latest, release build,local=false); also observed on 1.18.35 - OS: Linux 7.2.5-3-omarchy (linux x64)
- Terminal: TERM=xterm-256color, COLORTERM=truecolor
- Shell: /usr/bin/zsh
- Install/channel: mise, release channel
latest - Active plugins: none found in config
Reproduction
- Configure a remote MCP server whose authorization server enforces HTTPS redirect URIs:
{ "mcp": { "imanage-work": { "type": "remote", "url": "https://cloudimanage.com/mcp/work", "oauth": {} } } - Run
opencode mcp auth imanage-work. - Dynamic client registration posts
redirect_uris: ["http://127.0.0.1:<port>/mcp/oauth/callback"]and the server rejects it:(Verified by replaying the DCR request manually:{ "error": "invalid_client_metadata", "error_description": "All redirect_uris must be absolute URLs with allowed protocols" }http://redirect → rejected;https://127.0.0.1:19876/mcp/oauth/callback→ accepted withclient_id+client_secret.) - Workaround attempt: pin the DCR-issued
client_id/client_secretplus"redirectUri": "https://127.0.0.1:19876/mcp/oauth/callback"in config. The browser then opens the IdP authorize URL fine, but the redirect tohttps://127.0.0.1:19876/...fails withERR_SSL_PROTOCOL_ERROR, because the local callback server is plain HTTP.
Expected Behavior
There should be a supported way to complete built-in MCP OAuth against HTTPS-only authorization servers — e.g. serve TLS on the loopback callback (user-provided or auto-generated cert), or document a supported reverse-proxy/adapter pattern.
Actual Behavior
No overlap is possible: the IdP requires https:// redirect URIs while OpenCode only serves http:// on the callback. Relevant code in packages/core/src/mcp/oauth.ts (dev branch):
- default redirect is
http://127.0.0.1/callback(~line 152), - the callback listener is created via
node:httpwith no TLS option (~line 358), - the code even notes "a mismatch strands the browser" (~line 384) when the redirect port/URL doesn't match the listener.
So HTTPS-only IdPs can never finish opencode mcp auth, and the failure surfaces as a raw browser TLS error with no actionable message.
Additional Context
- Consistently reproducible against iManage Cloud (
https://cloudimanage.com/mcp/work, scopes likeimanage_work_mcp_scopes). RFC 8252 §7.3 recommends native apps use HTTP loopbacks, but some enterprise IdPs (iManage here) mandate HTTPS even for loopback — OpenCode has no answer for that class of server today. - Current workaround: an external helper script that performs DCR + PKCE itself, serves the callback over HTTPS with a self-signed cert, and writes tokens into the v1
mcp-auth.jsonstore (which only works on v1 — see companion compat issue). Refresh-token rotation thereafter works without a browser, but any fresh login needs the helper. - Feature suggestion: accept
oauth.tlsCert/oauth.tlsKey(orcertFile/keyFile) for the callback listener, use them whenredirect_uriishttps://, and surface a clear error when anhttps://redirect is configured without TLS material.
Activity
opencode-agent commentedon Oct 16, 2026
Thanks for the detailed write-up and for checking the DCR behaviour by hand. You're asking for OpenCode's MCP OAuth flow to support authorization servers that only accept
https://redirect URIs, by serving TLS on the loopback callback listener (with a user-provided cert/key) and showing a clear error when anhttps://redirect is configured without one. Maintainers will consider it.In the meantime, you might be able to get this working on v2 without the external helper script. The configured
redirectUriis used as-is for client registration and the browser redirect. The local listener binds tocallbackPortfirst and only uses the port fromredirectUriifcallbackPortisn't set. So you can point them at different ports and put a local TLS-terminating proxy in between:{ "mcp": { "imanage-work": { "type": "remote", "url": "https://cloudimanage.com/mcp/work", "oauth": { "redirectUri": "https://127.0.0.1:19876/mcp/oauth/callback", "callbackPort": 19877 } } } }Then, while
opencode mcp auth imanage-workruns, forwardhttps://127.0.0.1:19876tohttp://127.0.0.1:19877with any local TLS proxy. For example, with Caddy:caddy reverse-proxy --from https://127.0.0.1:19876 --to 127.0.0.1:19877, or use a self-signed cert with your proxy of choice. BecauseredirectUriis set, dynamic client registration sends thehttps://URI, so you shouldn't need to pinclientId/clientSecretby hand. I haven't tested this against iManage, so please let us know whether it works.Related issues:
mcp-auth.jsonoauthblock are silently dropped, so use the camelCase names shown above