-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Description
GitHub Issue: Claude Desktop/Web Unable to Connect to OAuth-Protected Custom MCP Server
Issue Title
[BUG] Claude Desktop and claude.ai fail to connect to custom OAuth-protected MCP server - infinite about:blank loop
Environment
- Claude Desktop Version: [Latest as of November 2025]
- Claude Web: claude.ai (tested in browser)
- OS: macOS
- MCP Server: Custom FastAPI-based MCP server
- MCP Protocol Version: 2024-11-05
- OAuth Version: OAuth 2.1 (RFC 6749, RFC 7636 PKCE)
- Transport: SSE (Server-Sent Events) over HTTPS
Summary
A fully spec-compliant custom MCP server with OAuth 2.1 authentication fails to connect in both Claude Desktop and claude.ai web interface. The OAuth flow never initiates - instead, Claude opens an about:blank page and immediately prompts to reopen Claude Desktop, creating an infinite loop. The same server works perfectly with Claude Code CLI using --transport http.
Expected Behavior
When adding a custom MCP server URL in Claude Desktop/claude.ai Settings → Connectors:
- User enters MCP server base URL
- Claude discovers OAuth endpoints via
.well-known/oauth-authorization-server - Claude registers itself via dynamic client registration
- Browser opens to authorization endpoint
- User sees login form
- After successful login, redirects to
https://claude.ai/api/mcp/auth_callback - Claude exchanges authorization code for access token
- Connection establishes successfully
- MCP tools become available
Actual Behavior
In Claude Desktop:
- User enters MCP server URL in Settings → Connectors
- Clicks "Connect"
- Browser opens to
about:blank(no actual URL) - Dialog appears: "Open Claude?"
- User clicks "Open Claude"
- Returns to Claude Desktop
- Connection fails - can click "Connect" again
- Infinite loop - no OAuth flow ever initiates
In claude.ai Web Interface:
- User enters MCP server URL in Settings → Connectors
- Clicks "Connect"
- Browser opens to
about:blankor refreshes - Error message appears: "There was an error connecting to [ServerName]. Please check your server URL and make sure your server handles auth correctly."
- No OAuth flow initiates
- Connection never establishes
Server Implementation Details
Our custom MCP server is fully compliant with:
- MCP Specification 2024-11-05
- RFC 8414 (OAuth 2.0 Authorization Server Metadata)
- RFC 9728 (OAuth 2.0 Protected Resource Metadata)
- RFC 7636 (PKCE for OAuth 2.0)
- RFC 6749 (OAuth 2.0 Authorization Framework)
Implemented Endpoints
1. OAuth Discovery (RFC 8414)
Endpoint: GET /.well-known/oauth-authorization-server
Response (200 OK):
{
"issuer": "https://example-server.com:8383",
"authorization_endpoint": "https://example-server.com:8383/oauth/authorize",
"token_endpoint": "https://example-server.com:8383/oauth/token",
"registration_endpoint": "https://example-server.com:8383/oauth/register",
"code_challenge_methods_supported": ["S256"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"]
}Status: ✅ Working correctly, returns proper metadata
2. MCP SSE Endpoint (RFC 9728 Compliant)
Endpoint: GET /mcp
Unauthenticated Request → Returns 401:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://example-server.com:8383/.well-known/oauth-authorization-server"
Content-Type: application/json
{"error": "unauthorized", "message": "Bearer token required for MCP access"}Authenticated Request → Returns SSE stream:
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: endpoint
data: {"protocol":"mcp","version":"2024-11-05","capabilities":{"tools":{}},"user":"username"}
event: ping
data: authenticatedStatus: ✅ Working correctly per MCP spec
3. OAuth Registration (RFC 7591 - Dynamic Client Registration)
Endpoint: POST /oauth/register
Request:
{
"client_name": "Claude",
"redirect_uris": ["https://claude.ai/api/mcp/auth_callback"]
}Response (201 Created):
{
"client_id": "generated_client_id",
"client_name": "Claude",
"redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
"client_secret_expires_at": 0
}Status: ✅ Working correctly, tested manually with curl
4. OAuth Authorization (RFC 6749 with PKCE)
Endpoint: GET /oauth/authorize
Parameters Required:
client_id- Registered client identifierredirect_uri- Must match registered URIresponse_type- Must be "code"code_challenge- PKCE challenge (S256 method)state- CSRF protection token
Response: Returns HTML login form
Status: ✅ Working correctly, displays login form when accessed manually
5. OAuth Token Exchange
Endpoint: POST /oauth/token
Request (application/x-www-form-urlencoded):
grant_type=authorization_code
code=<authorization_code>
client_id=<client_id>
code_verifier=<pkce_verifier>
redirect_uri=<redirect_uri>
Response:
{
"access_token": "generated_token",
"token_type": "Bearer",
"expires_in": 28800,
"refresh_token": "refresh_token"
}Status: ✅ Working correctly
CORS Configuration
Server includes proper CORS headers:
CORSMiddleware(
allow_origins=[
"https://claude.ai",
"https://claude.com",
"https://www.anthropic.com",
"https://api.anthropic.com",
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)TLS/SSL Configuration
- Valid SSL certificate from trusted CA (No-IP TLS ICA)
- TLS 1.3 enabled
- Modern cipher suites
- Certificate chain validates correctly
Testing Performed
Test 1: Manual OAuth Flow ✅ WORKS
# Step 1: Get OAuth discovery metadata
curl https://example-server.com:8383/.well-known/oauth-authorization-server
# Returns: Correct OAuth endpoints
# Step 2: Register client
curl -X POST https://example-server.com:8383/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name":"TestClient","redirect_uris":["https://claude.ai/api/mcp/auth_callback"]}'
# Returns: client_id and registration details
# Step 3: Test MCP endpoint without auth
curl https://example-server.com:8383/mcp
# Returns: HTTP 401 with WWW-Authenticate header (CORRECT)
# Step 4: Test MCP endpoint with Bearer token
curl -H "Authorization: Bearer <valid_token>" https://example-server.com:8383/mcp
# Returns: HTTP 200 with SSE stream (CORRECT)Result: All endpoints work correctly via curl
Test 2: Claude Code CLI with HTTP Transport ✅ WORKS
# Configure Claude Code CLI to use HTTP transport with OAuth
claude mcp add --transport http custom-server https://example-server.com:8383
# OAuth flow initiates correctly:
# - Browser opens to authorization endpoint
# - Login form displays
# - After login, redirects to callback
# - Claude Code CLI receives token
# - Connection establishes successfully
# - MCP tools available and functionalResult: OAuth flow works perfectly with Claude Code CLI
Test 3: Claude Desktop ❌ FAILS
Steps:
- Open Claude Desktop
- Settings → Connectors → Add Connector
- Enter URL:
https://example-server.com:8383 - Click "Connect"
Observed:
- Browser opens to
about:blank(no actual URL in address bar) - "Open Claude?" dialog appears immediately
- No network requests to server (checked server logs)
- Clicking "Open Claude" returns to app
- Connection fails, can retry indefinitely
Expected:
- Browser should open to:
https://example-server.com:8383/oauth/authorize?client_id=...&redirect_uri=...&code_challenge=...&state=...
Test 4: claude.ai Web Interface ❌ FAILS
Steps:
- Go to https://claude.ai/settings/connectors
- Add connector
- Enter URL:
https://example-server.com:8383 - Click "Connect"
Observed:
- Page refreshes or opens
about:blank - Error: "There was an error connecting to [ServerName]. Please check your server URL and make sure your server handles auth correctly."
- No network requests to server (checked server logs)
- Connection never establishes
Expected:
- Should discover OAuth endpoints
- Should register client
- Should redirect to authorization endpoint
Test 5: MCP Inspector ✅ WORKS
Using the official MCP Inspector tool:
npx @modelcontextprotocol/inspector sse https://example-server.com:8383/mcpResult:
- SSE connection establishes (after auth)
- Can complete OAuth flow via Inspector
- All MCP methods work correctly
Investigation Results
Server Logs Show No Requests from Claude Desktop/Web
When attempting to connect via Claude Desktop or claude.ai:
- No requests to
/.well-known/oauth-authorization-server - No requests to
/oauth/register - No requests to
/oauth/authorize - No requests to
/mcpendpoint
This indicates Claude is failing before even attempting to contact the server.
Network Analysis
Using browser DevTools and curl:
- All OAuth endpoints respond correctly
- No CORS errors
- No TLS errors
- All HTTP status codes correct
- All headers properly formatted
Comparison: Working vs Not Working
| Transport | Status | OAuth Flow | Notes |
|---|---|---|---|
| Claude Code CLI (STDIO) | ✅ Works | N/A (no OAuth) | Local subprocess |
| Claude Code CLI (HTTP) | ✅ Works | ✅ Full flow | OAuth works perfectly |
| Claude Desktop | ❌ Fails | ❌ Never starts | about:blank loop |
| claude.ai Web | ❌ Fails | ❌ Never starts | Error message |
| MCP Inspector | ✅ Works | ✅ Full flow | OAuth works |
| Manual curl | ✅ Works | ✅ All endpoints | OAuth works |
Troubleshooting Steps Attempted
1. Changed MCP Endpoint Authentication ✅ Fixed Spec Compliance
Initial Problem: MCP endpoint returned SSE stream for unauthenticated requests (violated MCP spec)
Fix Applied: Changed to return HTTP 401 with WWW-Authenticate header per RFC 9728
Result: Server now fully spec-compliant, but Claude still fails to connect
2. Verified OAuth Discovery Metadata ✅ Correct
- Checked all URLs use correct scheme (https)
- Verified issuer matches actual server URL
- Confirmed all endpoints are accessible
- Tested with multiple tools - all work correctly
3. Fixed OAuth Issuer URL Configuration ✅ Correct
Initial Problem: OAuth discovery returned hardcoded localhost:8000 URLs
Fix Applied: Made issuer configurable via CIDX_ISSUER_URL environment variable
Result: Discovery metadata now returns correct production URLs
4. Tested Different URL Formats ❌ No Change
Tried entering:
https://example-server.com:8383(base URL)https://example-server.com:8383/mcp(SSE endpoint)- Without port number
- With trailing slash
Result: All fail with same behavior
5. Verified CORS Configuration ✅ Correct
- Added all Anthropic domains to CORS allowlist
- Enabled credentials
- Allowed all necessary headers
- OPTIONS preflight requests work correctly
Result: CORS is not the issue
6. Checked SSL/TLS Certificate ✅ Valid
curl -v https://example-server.com:8383/.well-known/oauth-authorization-server 2>&1 | grep certificate
# Certificate validates successfully
# Issued by trusted CA (No-IP TLS ICA)
# Valid until November 2026Result: Certificate is valid and trusted
Code Implementation
Our server implementation follows the MCP specification exactly. Key code snippets:
MCP Endpoint (401 Response for Unauthenticated)
from fastapi import Response, Header
from typing import Optional, Union
from sse_starlette.sse import EventSourceResponse
@mcp_router.get("/mcp")
async def mcp_sse_endpoint(
authorization: Optional[str] = Header(None),
) -> Union[Response, EventSourceResponse]:
"""MCP SSE endpoint - returns 401 for unauth per RFC 9728"""
user = None
# Validate Bearer token if provided
if authorization and authorization.startswith("Bearer "):
token = authorization.split(" ", 1)[1]
user = validate_token(token) # Returns User or None
# Return 401 for unauthenticated requests per MCP spec
if user is None:
issuer_url = os.getenv("CIDX_ISSUER_URL")
return Response(
status_code=401,
headers={
"WWW-Authenticate": f'Bearer realm="mcp", resource_metadata="{issuer_url}/.well-known/oauth-authorization-server"',
"Content-Type": "application/json",
},
content='{"error": "unauthorized", "message": "Bearer token required for MCP access"}',
)
# Authenticated: return SSE stream
return EventSourceResponse(authenticated_sse_generator(user))OAuth Discovery Endpoint
@app.get("/.well-known/oauth-authorization-server")
async def root_oauth_discovery():
"""OAuth 2.1 discovery endpoint (RFC 8414 compliance)"""
issuer = os.getenv("CIDX_ISSUER_URL")
return {
"issuer": issuer,
"authorization_endpoint": f"{issuer}/oauth/authorize",
"token_endpoint": f"{issuer}/oauth/token",
"registration_endpoint": f"{issuer}/oauth/register",
"code_challenge_methods_supported": ["S256"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"]
}Similar Issues Found
After extensive research, found these related issues:
-
Issue [BUG] Claude Desktop doesn't connect to Custom MCPs altogether (not with OAuth 2.1 nor with SSE) #5826: "Claude Desktop doesn't connect to Custom MCPs altogether"
- Same symptoms: about:blank, loop back to app
- Multiple users reporting OAuth-based custom servers fail
-
Issue [BUG] MCP OAuth Integration Fails on Production Deployments with step=start_error #3515: "Claude Desktop MCP OAuth Integration Issue Report"
- OAuth flow fails with
step=start_error - Failure occurs in Claude's OAuth proxy, not reaching MCP server
- OAuth flow fails with
-
fastmcp Issue [BUG] : Cannot use AWS Bedrock with Claude Code. Getting API Error (429 Too many tokens) #1466: "Claude connector issue with remote MCP and oauth enabled"
- Third-party MCP framework users experiencing same issue
- OAuth works with MCP Inspector but not Claude
Hypothesis
Based on all evidence, the issue appears to be in Claude's OAuth proxy/client implementation:
- Claude Desktop/Web never attempt to contact the MCP server
- The
about:blanksuggests Claude is failing to construct the authorization URL - Server logs show zero incoming requests
- Same server works perfectly with Claude Code CLI and MCP Inspector
- All OAuth endpoints work correctly when tested manually
Possible causes:
- Bug in Claude's OAuth discovery implementation
- Issue with dynamic client registration flow
- Problem with Claude's internal OAuth proxy
- URL validation/sanitization breaking the flow
- Hardcoded assumptions about MCP server structure
Workaround
Currently, the only way to use our OAuth-protected MCP server with Claude is:
Use Claude Code CLI with HTTP transport:
claude mcp add --transport http server-name https://example-server.com:8383This works perfectly but is not available in Claude Desktop or claude.ai web interface.
Request
Could the Anthropic team investigate why Claude Desktop and claude.ai fail to connect to OAuth-protected custom MCP servers that work correctly with:
- Claude Code CLI (HTTP transport)
- MCP Inspector
- Manual OAuth testing
Our server is fully spec-compliant (RFC 8414, RFC 9728, RFC 7636, RFC 6749) and works with all testing tools except Claude Desktop/Web.
Is there additional configuration or specific requirements for Claude Desktop/Web that differ from the MCP specification?
Additional Information
- Willing to provide additional debugging information
- Can provide access to test server for Anthropic engineers (privately)
- Can provide complete server implementation code if helpful
- Available for testing any fixes or workarounds
Attachments
- Server implementation: FastAPI-based MCP server
- Testing scripts: Complete OAuth flow validation
- Network captures: Showing no requests from Claude Desktop/Web
- Success examples: Claude Code CLI working correctly
Thank you for investigating this issue!