🛜 feat: Support Legacy OAuth Servers without .well-known Metadata (#10917)

Adds support for MCP servers like StackOverflow that use OAuth but don't
provide standard discovery metadata at .well-known endpoints.

Changes:
- Add fallback OAuth endpoints (/authorize, /token, /register) when
  discoverAuthorizationServerMetadata returns undefined
- Add POST fallback in OAuth detection when HEAD returns non-401
  (StackOverflow returns 405 for HEAD, 401 for POST)
- Detect OAuth requirement from WWW-Authenticate: Bearer header even
  without resource_metadata URL
- Add fallback /token endpoint for token refresh when metadata
  discovery fails
- Add registration_endpoint to OAuthMetadata type

This mirrors the MCP SDK's behavior where it gracefully falls back to
default OAuth endpoint paths when .well-known metadata isn't available.

Tests:
- Add unit tests for detectOAuth.ts (POST fallback, Bearer detection)
- Add unit tests for handler.ts (fallback metadata, fallback refresh)
- Add StackOverflow to integration test servers

Fixes OAuth flow for servers that:
- Return 405 for HEAD requests (only support POST)
- Return 401 with simple "Bearer" in WWW-Authenticate
- Don't have .well-known/oauth-authorization-server endpoint
- Use standard /authorize, /token, /register paths
This commit is contained in:
Danny Avila 2025-12-11 12:31:24 -05:00 committed by GitHub
parent 4a2de417b6
commit 24c76c6cb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 556 additions and 35 deletions

View file

@ -429,13 +429,23 @@ router.get('/connection/status', requireJwtAuth, async (req, res) => {
const connectionStatus = {};
for (const [serverName] of Object.entries(mcpConfig)) {
connectionStatus[serverName] = await getServerConnectionStatus(
user.id,
serverName,
appConnections,
userConnections,
oauthServers,
);
try {
connectionStatus[serverName] = await getServerConnectionStatus(
user.id,
serverName,
appConnections,
userConnections,
oauthServers,
);
} catch (error) {
const message = `Failed to get status for server "${serverName}"`;
logger.error(`[MCP Connection Status] ${message},`, error);
connectionStatus[serverName] = {
connectionState: 'error',
requiresOAuth: oauthServers.has(serverName),
error: message,
};
}
}
res.json({