[BUG] v2.1.196 regression: MCP OAuth sends no scope when none is configured → AADSTS900144 on Microsoft Entra ID
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
After auto-update 2.1.195 → 2.1.196, authenticating an OAuth-protected HTTP MCP server whose authorization server is Microsoft Entra ID fails. The browser lands on Entra's /oauth2/v2.0/authorize page with:
AADSTS900144: The request body must contain the following parameter: 'scope'.
The generated authorize URL contains response_type, client_id, code_challenge, and code_challenge_method — but no scope parameter. No scope is explicitly configured for the server (oauth.scopes unset, no --scope).
This looks like an over-correction of a fix shipped in 2.1.196. The 2.1.196 changelog says:
Fixed MCP OAuth requesting the authorization server's fullscopes_supportedcatalog when no scope is specified, causinginvalid_scopefailures on GitLab self-hosted and other enterprise IdPs
On the no-scope path the client now appears to send nothing. Entra's v2.0 /authorize endpoint requires a non-empty scope, so it rejects the request before the user can sign in. 2.1.195 sent the full scopes_supported catalog (per the changelog's description of the pre-fix behavior) — which happened to include the required resource scope, so Entra accepted it.
The server's discovery metadata is correct and advertises scopes properly:
/.well-known/oauth-protected-resource→scopes_supported: ["api://<resource-guid>/Data.Read"]/.well-known/oauth-authorization-server→scopes_supported: ["openid","profile","email","offline_access","api://<resource-guid>/Data.Read"], withauthorization_endpointpointing at the Entra v2.0 endpoint.
What Should Happen?
With no scope explicitly configured, the client should request a non-empty default rather than nothing — e.g. fall back to the protected-resource metadata's scopes_supported (RFC 9728), or at minimum the standard OIDC scopes. An empty/absent scope is never valid for Entra.
The two enterprise IdPs in play have opposite constraints — GitLab self-hosted rejects the full catalog (#55954), while Entra requires at least one scope — so "send everything" and "send nothing" each break one of them. The resource scope from the protected-resource metadata is the value that satisfies both.
Error Messages/Logs
AADSTS900144: The request body must contain the following parameter: 'scope'.
Authorize URL (sanitized):
https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize?response_type=code&client_id=<public-client>&code_challenge=<...>&code_challenge_method=S256
Note the absence of any &scope= parameter.
Steps to Reproduce
- On 2.1.195, add an HTTP MCP server fronted by Microsoft Entra ID (its RFC 8414 auth-server metadata points at Entra v2.0 endpoints; its protected-resource metadata advertises an
api://<guid>/Data.Readscope). Authenticate via/mcp→ succeeds. - Update to 2.1.196.
claude mcp logout <server>(or let the access token expire), then/mcp→ server → Authenticate.- Browser shows
AADSTS900144; the generated authorize URL carries noscope.
Inferred workaround (from the changelog wording — the regression only triggers "when no scope is specified"): set oauth.scopes explicitly, e.g. ["openid","offline_access","api://<guid>/Data.Read"].
Is this a regression?
Yes.
Last Working Version
2.1.195
Claude Code Version
2.1.196 (native install, Windows 11)
---
Related: #55954 (the "requests every scope" bug this 2.1.196 change fixed — opposite direction), #72384 (docs for unset oauth.scopes), #52871 (other Entra-specific OAuth breakage).