Skip to content

Remote MCP OAuth: resource_metadata URL from WWW-Authenticate is ignored (origin-only RFC 9728 lookup) — breaks AWS Bedrock AgentCore runtimes #44790

Description

@mustafa89

Summary

For remote MCP servers, opencode ignores the resource_metadata URL announced in the WWW-Authenticate header of the 401 challenge and only looks for RFC 9728 protected-resource metadata at the domain root (https://<host>/.well-known/oauth-protected-resource). When the server publishes its metadata at a path-shaped URL (announced in the header), the root lookup 404s and opencode falls back to treating the resource host itself as the authorization server, building the authorize URL on the wrong host. The browser then opens a dead/error page and the whole flow fails or degrades.

This breaks a whole class of real-world servers, e.g. AWS Bedrock AgentCore runtime endpoints, whose MCP URL has the resource ARN in the path:

https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/<url-encoded-arn>/invocations?qualifier=<name>

Likely also relevant to opencode2 / V2 (unverified there, but the discovery logic decides everything downstream, and existing V2 OAuth issues like #40767 suggest the same code path).

Environment

  • opencode 1.18.21, macOS (darwin/arm64)
  • Server: AWS Bedrock AgentCore runtime (streamable HTTP MCP, JWT authorizer, IdP = JumpCloud/Ory Hydra, static PKCE client — the IdP has no dynamic client registration)

Reproduction / evidence

Unauthenticated POST to the MCP endpoint — the server correctly announces where its metadata lives:

$ curl -s -D - -X POST 'https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-east-1%3A<ACCOUNT_ID>%3Aruntime%2F<RUNTIME_ID>/invocations?qualifier=<NAME>' \
    -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3A...%2F<RUNTIME_ID>/invocations/.well-known/oauth-protected-resource?qualifier=<NAME>"

That metadata URL resolves fine and names the real authorization server:

$ curl -s 'https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/.../invocations/.well-known/oauth-protected-resource?qualifier=<NAME>'
{"authorization_servers":["https://oauth.id.jumpcloud.com"],"resource":"https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/..."}

The domain-root lookup (the only one opencode appears to try) 404s:

$ curl -s 'https://bedrock-agentcore.us-east-1.amazonaws.com/.well-known/oauth-protected-resource'
Invalid api path            (HTTP 404)

What opencode then does (opencode mcp auth <name>) — authorize URL built on the resource host, not the IdP:

https://bedrock-agentcore.us-east-1.amazonaws.com/authorize?response_type=code&client_id=<CLIENT_ID>&code_challenge=...&code_challenge_method=S256&redirect_uri=http%3A%2F%2F127.0.0.1%3A53712%2Fcallback&state=...

That path does not exist on the resource host ("Invalid api path"), so the user lands on an error page.

Expected behavior

Per RFC 9728 §5.1 (and the MCP authorization spec, which builds on it): when the 401 WWW-Authenticate header carries resource_metadata="<url>", the client should fetch that exact URL and use its authorization_servers entry for AS metadata discovery — not derive a well-known URL from the origin, and never assume the resource host is the AS.

Asks

  1. Bug fix: honor the resource_metadata URL from the WWW-Authenticate challenge (including path + query string forms) before/instead of the origin-root lookup.
  2. Feature (escape hatch): an explicit override in the oauth config block — e.g. authServerMetadataUrl — pointing directly at the AS metadata document, for servers whose discovery is broken or unusual. (Claude Code ships exactly this field, and it is the practical workaround there.) The current schema (clientId / clientSecret / scope / callbackPort / redirectUri) offers no way to steer discovery at all.

Notes

  • The same server works in clients that follow the header (verified with mcp-remote and with Claude Code's native remote MCP support).
  • No DCR on the IdP, so the automatic dynamic-registration fallback can't save the flow either — everything hinges on discovery landing on the right AS.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions