claude-code - 💡(How to fix) Fix [BUG] Remote MCP OAuth requests every scope from oauth-authorization-server.scopes_supported, causing invalid_scope on GitLab self-hosted [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#55954Fetched 2026-05-05 06:01:59
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×5commented ×1

Error Message

Error Messages/Logs

Root Cause

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.

Fix Action

Workaround

Two options that work today:

  1. 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.
  2. Use mcp-remote instead of native HTTP (recommended): claude mcp add GitLab -- npx -y mcp-remote https://<gitlab-host>/api/v4/mcp

Code Example



---

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*

---

[
    "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:*"
  ]
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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:

  1. The WWW-Authenticate: Bearer ... scope="..." header on the MCP endpoint's 401 response, or
  2. 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

  1. Register the GitLab MCP endpoint with the native HTTP transport: claude mcp add --transport http GitLab https://<gitlab-host>/api/v4/mcp
  2. Run /mcp in Claude Code and trigger GitLab authentication.
  3. The browser opens an authorize URL (captured below).
  4. 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:

  1. 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.
  2. 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:

  1. The scope parameter from WWW-Authenticate: Bearer on the MCP endpoint's 401 response.
  2. scopes_supported from oauth-protected-resource metadata (RFC 9728).
  3. 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

extent analysis

TL;DR

The most likely fix is to modify Claude Code's OAuth scope-selection logic to request only the scopes required by the protected resource, rather than all scopes advertised by the authorization server.

Guidance

  • Identify the required scopes for the protected MCP resource by checking the WWW-Authenticate: Bearer header on the MCP endpoint's 401 response or the scopes_supported from the oauth-protected-resource metadata.
  • Update the OAuth authorize request to include only the required scopes, rather than the full list of scopes supported by the authorization server.
  • Consider implementing a minimal default scope of mcp as a fallback, if the required scopes cannot be determined.
  • Verify the fix by testing the OAuth flow with the updated scope selection logic and ensuring that the authorization server issues a token with the requested scopes.

Example

No code snippet is provided, as the issue is related to the OAuth scope-selection logic, which is not explicitly shown in the provided code.

Notes

The fix should be applied to the native HTTP transport in Claude Code, as the issue is specific to this implementation. The mcp-remote stdio bridge is not affected, as it uses a different scope-selection logic.

Recommendation

Apply the suggested fix to update the OAuth scope-selection logic, as it is the most straightforward solution to resolve the issue. This fix will ensure that Claude Code requests only the required scopes for the protected MCP resource, rather than all scopes advertised by the authorization server.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Remote MCP OAuth requests every scope from oauth-authorization-server.scopes_supported, causing invalid_scope on GitLab self-hosted [1 comments, 2 participants]