[BUG] Remote MCP OAuth requests every scope from oauth-authorization-server.scopes_supported, causing invalid_scope on GitLab self-hosted
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When Claude Code connects to a remote MCP server via the native HTTP transport, the OAuth 2.1 + PKCE authorize request includes every scope advertised in the authorization server's scopes_supported rather than only the scopes required by the protected MCP resource. On GitLab self-hosted instances this list contains admin-only and dynamic-template scopes that a normal user cannot be granted, so the entire OAuth flow aborts with invalid_scope before the MCP handshake even begins.
The community mcp-remote stdio↔HTTP bridge authenticates against the same endpoint, same user, same OAuth server without issue. This isolates the defect to Claude Code's native HTTP transport OAuth scope-selection logic.
What Should Happen?
Per RFC 9728 (OAuth 2.0 Protected Resource Metadata) and the MCP authorization specification, the client should request only the scopes required by the protected resource, discovered via either:
- The WWW-Authenticate: Bearer ... scope="..." header on the MCP endpoint's 401 response, or
- scopes_supported in https://<gitlab-host>/.well-known/oauth-protected-resource[/api/v4/mcp]
For GitLab MCP this resolves to mcp (optionally augmented with openid read_user for identity).
scopes_supported from the authorization server metadata represents what the AS can issue across all clients — not what the resource requires. Treating the two as equivalent is the root cause here.
Confirming the bug is client-side, not server-side
Replacing the native HTTP registration with the community mcp-remote stdio bridge — pointing at the same URL, same OAuth server, same user account — succeeds:
claude mcp remove GitLab
claude mcp add GitLab -- npx -y mcp-remote https://<gitlab-host>/api/v4/mcp
mcp-remote requests scope=mcp only, and GitLab issues the token. Claude Desktop also relies on mcp-remote for this same endpoint and works for the same reason. This rules out:
- GitLab not supporting HTTP transport for MCP (it does — mcp-remote speaks HTTP to it)
- GitLab not supporting OAuth (it does — full RFC-compliant metadata)
- GitLab not issuing the mcp scope (it does — to mcp-remote)
The OAuth failure happens before the MCP transport handshake, so transport-level differences cannot explain it. The defect is in Claude Code's OAuth scope-selection step.
Error Messages/Logs
Steps to Reproduce
- Register the GitLab MCP endpoint with the native HTTP transport: claude mcp add --transport http GitLab https://<gitlab-host>/api/v4/mcp
- Run /mcp in Claude Code and trigger GitLab authentication.
- The browser opens an authorize URL (captured below).
- GitLab responds with: The requested scope is invalid, unknown, or malformed.
Actual authorize URL produced by Claude Code
https://<gitlab-host>/oauth/authorize
?response_type=code
&client_id=<dcr-client-id>
&code_challenge=<...>
&code_challenge_method=S256
&redirect_uri=http%3A%2F%2Flocalhost%3A59251%2Fcallback
&state=<...>
&scope=api+read_api+read_user+create_runner+manage_runner+k8s_proxy
+self_rotate+mcp+read_repository+write_repository
+read_virtual_registry+write_virtual_registry
+read_observability+write_observability+ai_features
+sudo+admin_mode+read_service_ping
+openid+profile+email+ai_workflows+user%3A*
The 23-element scope= parameter is byte-identical to scopes_supported from https://<gitlab-host>/.well-known/oauth-authorization-server:
[
"api","read_api","read_user","create_runner","manage_runner","k8s_proxy",
"self_rotate","mcp","read_repository","write_repository",
"read_virtual_registry","write_virtual_registry",
"read_observability","write_observability","ai_features",
"sudo","admin_mode","read_service_ping",
"openid","profile","email","ai_workflows","user:*"
]
Scopes that cause the failure:
- sudo, admin_mode — admin-only, not grantable to regular users
- user:* — dynamic scope template (user:<id>), not a literal value
- Any one of these triggers invalid_scope and aborts the entire flow
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.126 (Claude Code)
Platform
Other
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Environment
- Claude Code version: 2.1.126 (Claude Code)
- OS: macOS 15 (Darwin 25.4.0)
- MCP server: GitLab Community Edition 18.7.0, self-hosted
- MCP endpoint: https://<gitlab-host>/api/v4/mcp
- Transport tested: --transport http (native, fails) vs. mcp-remote stdio bridge (works)
Workaround
Two options that work today:
- Manual scope override — keep the original client_id, code_challenge, state, redirect_uri, replace scope=... with scope=mcp (or scope=mcp+openid+read_user), open in a browser tab while Claude Code is still listening on the callback port. Token exchange completes normally.
- Use mcp-remote instead of native HTTP (recommended):
claude mcp add GitLab -- npx -y mcp-remote https://<gitlab-host>/api/v4/mcp
Suggested fix
When initiating OAuth for a remote MCP server, derive the requested scope set in this order:
- The scope parameter from WWW-Authenticate: Bearer on the MCP endpoint's 401 response.
- scopes_supported from oauth-protected-resource metadata (RFC 9728).
- A minimal default of mcp — never the full scopes_supported from the authorization server metadata.
Impact / scope of the bug
This will affect any OAuth server whose oauth-authorization-server.scopes_supported advertises scopes the user cannot be granted — extremely common in enterprise IdPs (GitLab, Keycloak, Okta with custom scopes, etc.). The reproduction here is GitLab self-hosted, but the fix benefits all native-HTTP MCP integrations.
References
- RFC 9728 — OAuth 2.0 Protected Resource Metadata
- MCP Authorization specification — https://modelcontextprotocol.io/specification/draft/basic/authorization
- GitLab MCP server documentation — https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_server/
- mcp-remote (working reference implementation) — https://www.npmjs.com/package/mcp-remote
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗