[FEATURE] .mcp.json oauth: allow overriding or omitting the RFC 8707 `resource` parameter (Entra ID AADSTS9010010)
Problem
MCP servers whose OAuth authorization server is Microsoft Entra ID are currently impossible to connect to with Claude Code's native MCP OAuth when the server's canonical URL differs from the Entra app's identifier URI — which is always the case for servers hosted on cloud infrastructure (e.g. AWS Bedrock AgentCore Gateway at https://<id>.gateway.bedrock-agentcore.<region>.amazonaws.com/mcp).
Per the MCP 2025-06-18 auth spec, Claude Code sends the RFC 8707 resource parameter in authorization/token requests, using the resource value from the server's protected-resource metadata. Entra now hard-enforces (rollout ~March 2026, observed on both login.microsoftonline.com and login.microsoftonline.us) that resource must byte-match the identifier URI of the app that owns the requested scopes (api://<app-id>). Since the MCP server URL can never equal the api:// URI, every sign-in fails at the authorize step:
invalid_target: AADSTS9010010: The resource parameter provided in the request
doesn't match with the requested scopes.
Evidence: no scope configuration can fix this
Probed Entra's authorize endpoint directly (validation errors return in the 302 redirect before login):
| scope | resource | Result |
|---|---|---|
| api://<app>/my.scope (pinned via oauth.scopes) | <MCP server URL> | ❌ AADSTS9010010 |
| bare my.scope (from scopes_supported discovery) | <MCP server URL> | ❌ AADSTS9010010 |
| api://<app>/my.scope | api://<app> | ✅ accepted |
| api://<app>/my.scope | (omitted) | ✅ accepted |
The resource value is the problem, and nothing in the current oauth config block (clientId, callbackPort, scopes, authServerMetadataUrl) can change it. Nor can the other layers help:
- Entra rejects arbitrary
https://identifier URIs on app registrations (verified-domain requirement), so the cloud hostname can't be registered to make them match. - Managed MCP hosts (e.g. AgentCore Gateway) don't allow customizing the advertised
resourcein their protected-resource metadata.
Proposed solution
Add a resource key to the .mcp.json oauth block that takes precedence over the protected-resource metadata value, mirroring how scopes already overrides scopes_supported:
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "https://<id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp",
"oauth": {
"clientId": "<entra-app-client-id>",
"scopes": "api://<entra-app-client-id>/my.scope offline_access",
"resource": "api://<entra-app-client-id>" // override; or false/null to omit the parameter
}
}
}
}
Either capability unblocks Entra:
- Override: send the configured value instead of the metadata value (
resource=api://<app>passes validation). - Omit:
resource: false(or similar) suppresses the parameter entirely — Entra accepts plain v2 scope-only requests.
An explicit user-configured escape hatch seems justified here: the server metadata is spec-compliant, Claude Code is spec-compliant, but the authorization server (Entra) implements only a degenerate form of RFC 8707, and neither end is likely to change soon. Microsoft's own MCP servers (Azure DevOps, Business Central) trip over the same enforcement.
Related issues
- #52871 — trailing-slash normalization of
resourcebreaking Entra (that case is fixable by byte-exact matching; this case is not, because the server URL can never match theapi://URI) - #55993 — Azure DevOps remote MCP, same AADSTS9010010 class (closed)
- #26675 — pre-configured OAuth client credentials (the
oauthblock this would extend)
Environment
- Claude Code 2.1.198, macOS (darwin arm64)
- MCP server: AWS Bedrock AgentCore Gateway (streamable HTTP), custom JWT authorizer
- Authorization server: Microsoft Entra ID (Azure Government,
login.microsoftonline.us— but reproduces identically on commercial)
🤖 Filed with Claude Code
3 Comments
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
We hit this independently on a production deployment (Microsoft Entra ID, MCP server on Azure App Service,
.azurewebsites.nethost) and can add a couple of data points that might help whoever picks this up.Same root failure, confirmed:
resource=<server URL>→AADSTS9010010at/authorize, exactly as documented above.A second manifestation worth flagging — different code path, same root cause. On a reconnect (not the initial connect) from both Claude Code's
/mcpand Claude Desktop's custom-connector "Reconnect," we saw a different failure:00000003-0000-0000-c000-000000000000is Microsoft Graph's well-known app ID — the client appears to drop the resource/scope qualification entirely on whatever request path a reconnect/refresh takes, and Entra silently defaults scope resolution to Graph instead of our API. Same underlying defect (client can't consistently carry the resource indicator through), but this shows up with no actionable Entra error page and no address-bar detail, just a generic client-side "Authorization failed" — worth knowing if you're trying to reproduce beyond the initial connect.One useful data point for the fix itself. We bypassed the Claude client entirely (MSAL device-code flow, straight to Entra) to confirm the server side was correct, and decoded the resulting token:
tested with the scope requested in both the
api://<app-id>/scopeform and the resource-URL form — identicalaudeither way. This matches the "no-resource /api://<app>" rows in the table above being accepted, and adds the specific detail that when the client and resource are the same app registration (the common single-app MCP pattern), Entra collapsesaudto the bare GUID, notapi://<guid>. Any fix that lets Claude successfully obtain a token here will need the resource-server side to validate that bare-GUID form too, not just theapi://form — we had to add it as a third accepted audience on our end before a manually-obtained token would validate against our own API.+1