Summary
When connecting to an MCP server via Claude.ai's connector feature (for Anthropic team accounts), the OAuth 2.1 flow completes successfully and a valid access token is issued, but subsequent MCP requests are made without the Authorization: Bearer <token> header, causing authentication failures.
Environment
- Claude.ai (Anthropic team account)
- MCP server: Custom implementation at
https://nexus.zarhq.dev/mcp
- OAuth 2.1 with PKCE (RFC 7636)
- MCP Streamable HTTP transport
Steps to Reproduce
- Add a new MCP connector in Claude.ai team settings
- Enter MCP server URL:
https://nexus.zarhq.dev/mcp
- Complete the OAuth authorization flow (redirects to GitHub OAuth, then back)
- OAuth completes successfully (server logs show 200 OK on
/oauth/token)
- Claude.ai attempts to list MCP tools/resources
- Bug: Request fails because no Authorization header is sent
Expected Behavior
After successful OAuth token exchange, Claude.ai should include the access token in subsequent MCP requests:
GET /mcp HTTP/1.1
Host: nexus.zarhq.dev
Authorization: Bearer nx_abc123...
Actual Behavior
Claude.ai makes MCP requests without any Authorization header:
GET /mcp HTTP/1.1
Host: nexus.zarhq.dev
(no Authorization header)
Server Logs
OAuth token exchange (SUCCESS):
Started POST "/oauth/token" for 172.226.122.16
Completed 200 OK
Subsequent MCP request (FAIL - no token sent):
Started GET "/mcp" for 172.226.122.16
MCP auth header present: false, value:
MCP token extracted: false, prefix:
MCP token validation: token is blank
Unauthorized MCP request: Invalid or missing authentication token
Server Implementation Details
The MCP server correctly implements:
- OAuth 2.0 Authorization Server Metadata at
/.well-known/oauth-authorization-server
- OAuth 2.0 Protected Resource Metadata (RFC 9728) at
/.well-known/oauth-protected-resource
- PKCE (RFC 7636) with S256 code challenge
- Token response in standard OAuth 2.0 format:
{
"access_token": "nx_...",
"token_type": "Bearer",
"expires_in": 31536000,
"scope": "mcp"
}
- WWW-Authenticate header on 401 responses:
WWW-Authenticate: Bearer resource_metadata="https://nexus.zarhq.dev/.well-known/oauth-protected-resource"
Protected Resource Metadata Response
{
"resource": "https://nexus.zarhq.dev/mcp",
"authorization_servers": ["https://nexus.zarhq.dev"],
"scopes_supported": ["mcp"],
"bearer_methods_supported": ["header"]
}
Possible Causes
- Token not being stored after successful OAuth exchange
- Token being associated with wrong resource URL
- Token not being retrieved when making MCP requests
- Race condition between token storage and first MCP request
Impact
Unable to use any MCP server requiring OAuth authentication with Claude.ai connectors. The OAuth flow appears to complete successfully from the user's perspective, but the connection fails silently.
Summary
When connecting to an MCP server via Claude.ai's connector feature (for Anthropic team accounts), the OAuth 2.1 flow completes successfully and a valid access token is issued, but subsequent MCP requests are made without the
Authorization: Bearer <token>header, causing authentication failures.Environment
https://nexus.zarhq.dev/mcpSteps to Reproduce
https://nexus.zarhq.dev/mcp/oauth/token)Expected Behavior
After successful OAuth token exchange, Claude.ai should include the access token in subsequent MCP requests:
Actual Behavior
Claude.ai makes MCP requests without any Authorization header:
Server Logs
OAuth token exchange (SUCCESS):
Subsequent MCP request (FAIL - no token sent):
Server Implementation Details
The MCP server correctly implements:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource{ "access_token": "nx_...", "token_type": "Bearer", "expires_in": 31536000, "scope": "mcp" }Protected Resource Metadata Response
{ "resource": "https://nexus.zarhq.dev/mcp", "authorization_servers": ["https://nexus.zarhq.dev"], "scopes_supported": ["mcp"], "bearer_methods_supported": ["header"] }Possible Causes
Impact
Unable to use any MCP server requiring OAuth authentication with Claude.ai connectors. The OAuth flow appears to complete successfully from the user's perspective, but the connection fails silently.