anthropics / anthropics/claude-ai-mcp
Custom connector OAuth redirects to MCP server's own domain instead of Entra ID authorization server (works fine via Claude Code)
- Lingua principale
- Nessun dato sulla lingua
- Stelle
- 471
- Fork
- 76
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
## Summary
Adding a custom connector (hosted claude.ai surface) against an MCP server backed by Microsoft Entra ID consistently redirects the browser to `/authorize` on the **MCP server's own domain** instead of the authorization server listed in the server's protected resource metadata `authorization_servers` field. Reproduced identically across many attempts, including on a brand-new URL never seen before (rules out backend caching).
Critically: **the exact same MCP server, same Entra app registrations, same static Client ID work correctly via Claude Code's own MCP OAuth client** (`claude mcp login`). This isolates the bug to the hosted connector's OAuth implementation specifically, not anything about the server or Entra ID setup.
## Setup
- MCP server: ASP.NET Core, `ModelContextProtocol.AspNetCore` 2.2.0
- Authorization server: Microsoft Entra ID, single-tenant
- Connector added via claude.ai custom connector UI with a manually-supplied static OAuth Client ID (public client, PKCE, no secret) in Advanced Settings — since Entra has no open Dynamic Client Registration endpoint
## Symptom (hosted claude.ai connector)
After supplying the static Client ID and clicking Connect, the browser is sent to:
```
https://:5001/authorize?response_type=code&client_id=&redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback&code_challenge=...&code_challenge_method=S256&state=...
```
Our server has no `/authorize` route (it's a resource server, not an authorization server) — this 404s. This reproduced identically across:
- Multiple fresh connector add attempts
- A completely new, never-before-cached MCP server URL path (rules out backend caching tied to the original broken attempt)
- With and without a reverse proxy in front of the server (tested both through Caddy and with Caddy removed entirely, Kestrel terminating TLS directly)
- After fixing every documented discovery requirement (see below)
## What's verified correct on the server side (via direct curl, from multiple independent networks)
- `401` response includes `WWW-Authenticate: Bearer resource_metadata="https:///.well-known/oauth-protected-resource/"`
- That metadata document returns `200` with valid JSON:
```json
{"resource":"https:///","authorization_servers":["https://login.microsoftonline.com//v2.0"],"bearer_methods_supported":["header"],"scopes_supported":["https:////Mcp.Invoke"]}
```
- `resource` field matches the URL entered in the connector exactly, including path
- Entra's OIDC discovery at the listed `authorization_servers` issuer is reachable and returns valid metadata
- No 3xx redirects anywhere in the chain
- PKCE S256 supported by Entra
- Our own domain does not falsely answer any authorization-server-metadata well-known paths (ruled out the "same-host fallback" theory)
## Confirmation that the exact same setup works via Claude Code
Using `claude mcp add --transport http --client-id ` followed by `claude mcp login `, against the identical MCP server and identical Entra app registrations:
- Claude Code correctly redirected to `https://login.microsoftonline.com//oauth2/v2.0/authorize?...` (Entra's real endpoint, not our domain)
- Completed the full OAuth flow, including sign-in and token exchange
- `claude mcp get ` reports `Status: ✔ Connected`
This proves the server's discovery metadata, Entra app registrations, scopes, and Application ID URI configuration are all correct — Claude Code's separate OAuth client implementation (loopback redirect, distinct from the hosted `claude.ai/api/mcp/auth_callback` flow) consumes the exact same metadata correctly and completes the flow successfully. The hosted connector's OAuth client appears to not be acting on the `authorization_servers` field from the protected resource metadata document at all, falling back to treating the resource server as its own authorization server.
## Related issues
- #694 — same failure pattern (ignoring discovered `authorization_endpoint`, falling back to a hardcoded `/authorize` on the resource server's own domain) reported against a completely unrelated WordPress/Novamira OAuth server. Suggests this is a general bug in the hosted connector's OAuth client, not Entra-specific.
- anthropics/claude-code#76096 — related but distinct Entra-specific RFC 8707/RFC 9728 resource-parameter conflict (Claude Code side); closed as duplicate, no visible resolution.
- anthropics/claude-code#52871 — related Entra-specific trailing-slash bug on the `resource` parameter (Claude Code side).
## Reference IDs from early failed attempts (before some of the above was diagnosed/fixed)
- `ofid_2d4c54af0ee32c07`
- `ofid_b2198679a7687d27`
## What we'd like
Given the working Claude Code reference implementation against the identical backend, we believe this is squarely a hosted-connector-side bug in how `authorization_servers` from protected resource metadata is consumed when a static (non-DCR) Client ID is supplied. Happy to provide more logs/detail — currently working around this via `oauth_anthropic_creds` (email already sent to mcp-review@anthropic.com) since the standard custom-connector flow doesn't work for this setup.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.