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
mcp: snake_case keys in a remote server's oauth block are silently dropped, so auth falls back to dynamic client registration with a misleading error #55335
In opencode.json, a remote MCP server's oauth block written with snake_case keys (client_id, client_secret, callback_port, redirect_uri) is silently dropped. The server resolves to oauth: {}. opencode then tries dynamic client registration. For servers that do not support it, login fails with an error that does not mention the config:
■ Authentication failed
■ Incompatible auth server: does not support dynamic client registration
The camelCase keys (clientId, callbackPort, redirectUri) work. The docs/schema mismatch is #52177. This issue is about the runtime: an oauth key it does not know is discarded with no diagnostic. opencode already logs configuration normalization diagnostic … action="omitted unsupported legacy setting" for other unsupported fields, for example a model's attachment and reasoning. That warning never appears for unknown oauth keys.
Steps to reproduce
Add a remote server whose auth server requires a pre-registered client, for example Slack's MCP server:
opencode debug config shows "oauth": {} for the server.
opencode mcp auth slack fails with "does not support dynamic client registration". The log shows mcp oauth client registration selected … registration=dcr.
Change the keys to camelCase (clientId, callbackPort, redirectUri). debug config now shows the values, and auth succeeds.
Expected
Either accept both spellings, or emit a configuration diagnostic and a CLI warning for unknown oauth keys. The failure should say that the client ID was ignored, not that the server is incompatible.
Thanks for the detailed report. I was able to reproduce this on 2.0.28, and it still happens on the latest dev (3a69bae).
I used a local mock MCP server whose auth server has no dynamic client registration endpoint:
With the flat mcp.<name> entry and "oauth": {"client_id": "abc123", "callback_port": 3118}, opencode mcp auth fails with Incompatible auth server: does not support dynamic client registration, and the log shows registration=dcr. No configuration diagnostic mentions the ignored oauth keys.
With clientId / callbackPort, the same server gets client_id=abc123 in the authorize URL and registration=static.
It looks like the flat (v1-style) mcp.<name> form only reads camelCase OAuth fields and drops the rest without a diagnostic. The v2 mcp.servers.<name>.oauth form uses the snake_case names.
One side note: in my runs opencode debug config showed "oauth": {} even for camelCase config, so it may not be a reliable way to check these values.
The script below reproduces it. Related: #52177 (docs/schema naming mismatch) and #50807 (MCP timeout silently dropped from the normalized config).
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces anomalyco/opencode#55335: snake_case keys in a (flat, V1-style) remote MCP# server's `oauth` block are dropped without a diagnostic, so `opencode mcp auth` falls back to# dynamic client registration and fails with "Incompatible auth server".# Usage: ./repro.sh (uses `opencode` from PATH)# OC_DEV=/path/to/checkout ./repro.sh (runs the CLI from a source checkout)set -u
HERE=$(cd "$(dirname "$0")"&& pwd)
P=$(mktemp -d); mkdir -p "$P/proj";cd"$P/proj"; git init -q
# Mock MCP resource + OAuth authorization server that has NO registration_endpoint (pre-registered clients only).
cat >"$P/mock.ts"<<'TS'const port = 4555, base = `http://127.0.0.1:${port}`Bun.serve({ port, fetch(req) { const u = new URL(req.url) if (u.pathname.startsWith("/.well-known/oauth-protected-resource")) return Response.json({ resource: `${base}/mcp`, authorization_servers: [base] }) if (u.pathname.startsWith("/.well-known/oauth-authorization-server") || u.pathname.startsWith("/.well-known/openid-configuration")) return Response.json({ issuer: base, authorization_endpoint: `${base}/authorize`, token_endpoint: `${base}/token`, response_types_supported: ["code"], code_challenge_methods_supported: ["S256"] }) if (u.pathname === "/mcp") return new Response("unauthorized", { status: 401, headers: { "www-authenticate": `Bearer resource_metadata="${base}/.well-known/oauth-protected-resource/mcp"` } }) return new Response("not found", { status: 404 })}})TS
bun "$P/mock.ts"& MOCK=$!;trap'kill $MOCK 2>/dev/null' EXIT; sleep 1
if [ -n"${OC_DEV:-}" ];then
OC=(bun --conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js $OC_DEV/packages/cli/src/index.ts)
else OC=(opencode);fioc() { "${OC[@]}""$@"; }
restart_service() {
kill$(pgrep -f "serve --servic[e]")2>/dev/null; sleep 1
# From source, the auto-spawned service lacks the preload, so start it by hand.if [ -n"${OC_DEV:-}" ];then (cd "$P/proj"&&"${OC[@]}" serve --service >"$P/service.log"2>&1&); sleep 8;fi
}
echo"version: $(oc --version)"
i=0
forOin'{"client_id":"abc123","callback_port":3118}''{"clientId":"abc123","callbackPort":3118}';do
i=$((i+1)); name="srv$RANDOM$i"
restart_service # make sure the background service re-reads configecho"{\"mcp\":{\"$name\":{\"type\":\"remote\",\"url\":\"http://127.0.0.1:4555/mcp\",\"oauth\":$O}}}"> opencode.json
echo;echo"=== oauth block: $O"
BROWSER=true timeout 20 "${OC[@]}" mcp auth "$name"< /dev/null 2>&1| grep -a -o -E "client_id=[A-Za-z0-9]+|Incompatible auth server.*|Authentication failed"| sort -u
LOG=$(oc debug paths 2>/dev/null | awk '$1=="log"{print $2}')/opencode.log
grep -a "server=$name""$LOG"2>/dev/null | grep -a -o "registration=[a-z]*"| tail -1
grep -a -i "normalization diagnostic""$LOG"2>/dev/null | grep -a -i oauth | tail -1 ||truedonekill$(pgrep -f "serve --servic[e]")2>/dev/null
Activity
opencode-agent commentedon Oct 14, 2026
Thanks for the detailed report. I was able to reproduce this on 2.0.28, and it still happens on the latest
dev(3a69bae).I used a local mock MCP server whose auth server has no dynamic client registration endpoint:
mcp.<name>entry and"oauth": {"client_id": "abc123", "callback_port": 3118},opencode mcp authfails withIncompatible auth server: does not support dynamic client registration, and the log showsregistration=dcr. No configuration diagnostic mentions the ignoredoauthkeys.clientId/callbackPort, the same server getsclient_id=abc123in the authorize URL andregistration=static.It looks like the flat (v1-style)
mcp.<name>form only reads camelCase OAuth fields and drops the rest without a diagnostic. The v2mcp.servers.<name>.oauthform uses the snake_case names.One side note: in my runs
opencode debug configshowed"oauth": {}even for camelCase config, so it may not be a reliable way to check these values.The script below reproduces it. Related: #52177 (docs/schema naming mismatch) and #50807 (MCP
timeoutsilently dropped from the normalized config).Reproduction script (repro.sh)
Run it:
bash repro.sh