UNPKG

@grebyn/toolflow-mcp-server

Version:

MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.

181 lines 7.7 kB
/** * OAuth Discovery Endpoints Handler * Implements MCP Authorization specification (RFC 9728) endpoints */ export class OAuthEndpointsHandler { config; constructor(config) { this.config = config; } /** * Handle OAuth Protected Resource Metadata endpoint * GET /.well-known/oauth-protected-resource */ handleProtectedResourceMetadata(req, res) { const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host; const protocol = Array.isArray(req.headers['x-forwarded-proto']) ? req.headers['x-forwarded-proto'][0] : req.headers['x-forwarded-proto'] || 'http'; const baseUrl = `${protocol}://${host || 'localhost'}`; const metadata = { resource: `${baseUrl}/mcp`, authorization_servers: [baseUrl] // Point to this server, not the Next.js app }; this.sendJsonResponse(res, 200, metadata); } /** * Handle OAuth Authorization Server Metadata endpoint * GET /.well-known/oauth-authorization-server */ handleAuthorizationServerMetadata(req, res) { const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host; const protocol = Array.isArray(req.headers['x-forwarded-proto']) ? req.headers['x-forwarded-proto'][0] : req.headers['x-forwarded-proto'] || 'http'; const baseUrl = `${protocol}://${host || 'localhost'}`; const metadata = { issuer: `${this.config.oauthBaseUrl}/auth/v1`, authorization_endpoint: `${this.config.oauthBaseUrl}/api/oauth/authorize`, token_endpoint: `${this.config.oauthBaseUrl}/api/oauth/token`, jwks_uri: `${this.config.oauthBaseUrl}/.well-known/jwks.json`, grant_types_supported: ["authorization_code", "refresh_token"], code_challenge_methods_supported: ["S256"], response_types_supported: ["code"], scopes_supported: ["openid", "email", "profile"], token_endpoint_auth_methods_supported: ["client_secret_post", "none"], registration_endpoint: `${baseUrl}/.well-known/oauth-dynamic-client-registration`, resource_indicators_supported: true, require_pushed_authorization_requests: false }; this.sendJsonResponse(res, 200, metadata); } /** * Handle OAuth Dynamic Client Registration endpoint * POST /.well-known/oauth-dynamic-client-registration */ handleDynamicClientRegistration(req, res) { if (req.method !== 'POST') { if (req.method === 'OPTIONS') { this.sendCorsResponse(res, 204); return; } this.sendJsonResponse(res, 405, { error: 'method_not_allowed', error_description: 'Only POST method is supported' }); return; } const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host; const protocol = Array.isArray(req.headers['x-forwarded-proto']) ? req.headers['x-forwarded-proto'][0] : req.headers['x-forwarded-proto'] || 'http'; // Generate comprehensive MCP-compliant OAuth redirect URIs const redirectUris = this.generateSecureRedirectUris(protocol, host || 'localhost'); const clientRegistration = { client_id: "mcp-client", client_secret: "", // Empty for public client compatibility token_endpoint_auth_method: "none", grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], redirect_uris: redirectUris, scope: "openid email profile", client_name: "MCP Client", application_type: "native" }; this.sendJsonResponse(res, 201, clientRegistration); } /** * Generate secure, comprehensive redirect URIs following MCP and OAuth 2.1 standards * Supports all major MCP clients while maintaining security */ generateSecureRedirectUris(protocol, host) { const redirectUris = []; // 1. Production HTTPS callback (MCP compliant) if (protocol === 'https') { redirectUris.push(`${protocol}://${host}/oauth/callback`); } // 2. Localhost patterns with common ports (RFC 8252 compliant) // These cover development and local testing scenarios const localhostPorts = [3000, 3001, 5000, 5173, 8080, 8000, 4000, 6274]; const localhostPaths = ['/oauth/callback', '/callback', '/auth/callback']; for (const port of localhostPorts) { for (const path of localhostPaths) { redirectUris.push(`http://localhost:${port}${path}`); redirectUris.push(`http://127.0.0.1:${port}${path}`); } } // 3. IDE Custom URI Schemes (MCP specification allows these) const ideSchemes = [ // VS Code family 'vscode://oauth/callback', 'vscode://auth/callback', 'vscode://callback', 'vscode-insiders://oauth/callback', 'vscode-insiders://auth/callback', // Cursor AI Editor 'cursor://oauth/callback', 'cursor://auth/callback', 'cursor://callback', 'cursor://mcp/oauth/callback', 'cursor://mcp/auth/callback', // JetBrains family 'jetbrains://oauth/callback', 'jetbrains://auth/callback', 'intellij://oauth/callback', 'intellij://auth/callback', 'pycharm://oauth/callback', 'webstorm://oauth/callback', 'phpstorm://oauth/callback', 'rubymine://oauth/callback', 'clion://oauth/callback', 'datagrip://oauth/callback', 'goland://oauth/callback', 'rider://oauth/callback', // Other popular editors/IDEs 'atom://oauth/callback', 'sublime://oauth/callback', 'brackets://oauth/callback', 'nova://oauth/callback', 'zed://oauth/callback', // MCP-specific schemes 'mcp://oauth/callback', 'mcp://auth/callback', // Generic AI/Agent tools 'agent://oauth/callback', 'ai-tool://oauth/callback' ]; redirectUris.push(...ideSchemes); // 4. Mobile app schemes (common patterns) const mobileSchemes = [ 'com.anthropic.claude://oauth/callback', 'com.openai.chatgpt://oauth/callback', 'app://oauth/callback' ]; redirectUris.push(...mobileSchemes); return redirectUris; } /** * Send JSON response with proper CORS headers */ sendJsonResponse(res, statusCode, data) { res.writeHead(statusCode, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, mcp-protocol-version', }); res.end(JSON.stringify(data)); } /** * Send CORS response for OPTIONS requests */ sendCorsResponse(res, statusCode) { res.writeHead(statusCode, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization, mcp-protocol-version', }); res.end(); } } //# sourceMappingURL=oauth-endpoints.js.map