Falling back on CORS errors too

This commit is contained in:
Glen Maddern 2025-05-07 16:01:29 +10:00
commit 3bf0627ca6

View file

@ -2,7 +2,7 @@
import { CallToolResultSchema, JSONRPCMessage, ListToolsResultSchema, Tool } from '@modelcontextprotocol/sdk/types.js' import { CallToolResultSchema, JSONRPCMessage, ListToolsResultSchema, Tool } from '@modelcontextprotocol/sdk/types.js'
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
// Import both transport types // Import both transport types
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' import { SSEClientTransport, SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' // Added import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' // Added
import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { auth, UnauthorizedError, OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js' import { auth, UnauthorizedError, OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'
@ -181,7 +181,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult {
transportRef.current = null transportRef.current = null
} }
const commonOptions = { authProvider: authProviderRef.current } const commonOptions: SSEClientTransportOptions = { authProvider: authProviderRef.current }
const targetUrl = new URL(url) const targetUrl = new URL(url)
if (transportType === 'http') { if (transportType === 'http') {
@ -266,19 +266,21 @@ export function useMcp(options: UseMcpOptions): UseMcpResult {
const errorInstance = connectErr instanceof Error ? connectErr : new Error(String(connectErr)) const errorInstance = connectErr instanceof Error ? connectErr : new Error(String(connectErr))
// Check for 404/405 specifically for HTTP transport // Check for 404/405 specifically for HTTP transport
const is404 = errorInstance.message.includes('404') || errorInstance.message.includes('Not Found') const errorMessage = errorInstance.message
const is405 = errorInstance.message.includes('405') || errorInstance.message.includes('Method Not Allowed') const is404 = errorMessage.includes('404') || errorMessage.includes('Not Found')
const is405 = errorMessage.includes('405') || errorMessage.includes('Method Not Allowed')
const isLikelyCors = errorMessage === 'Failed to fetch' /* Chrome */ || errorMessage === 'NetworkError when attempting to fetch resource.' /* Firefox */ || errorMessage === 'Load failed' /* Safari */
if (transportType === 'http' && (is404 || is405)) { if (transportType === 'http' && (is404 || is405 || isLikelyCors)) {
addLog('warn', `HTTP transport failed (${is404 ? '404' : '405'}). Will attempt fallback to SSE.`) addLog('warn', `HTTP transport failed (${isLikelyCors ? 'CORS' : is404 ? '404' : '405'}). Will attempt fallback to SSE.`)
return 'fallback' // Signal that fallback should be attempted return 'fallback' // Signal that fallback should be attempted
} }
// Check for Auth error (Simplified - requires more thought for interaction with fallback) // Check for Auth error (Simplified - requires more thought for interaction with fallback)
if ( if (
errorInstance instanceof UnauthorizedError || errorInstance instanceof UnauthorizedError ||
errorInstance.message.includes('Unauthorized') || errorMessage.includes('Unauthorized') ||
errorInstance.message.includes('401') errorMessage.includes('401')
) { ) {
addLog('info', 'Authentication required. Initiating SDK auth flow...') addLog('info', 'Authentication required. Initiating SDK auth flow...')
// Ensure state is set only once if multiple attempts trigger auth // Ensure state is set only once if multiple attempts trigger auth
@ -324,8 +326,8 @@ export function useMcp(options: UseMcpOptions): UseMcpResult {
// Handle other connection errors // Handle other connection errors
// Use stable failConnection // Use stable failConnection
failConnection(`Failed to connect via ${transportType.toUpperCase()}: ${errorInstance.message}`, errorInstance) failConnection(`Failed to connect via ${transportType.toUpperCase()}: ${errorMessage}`, errorInstance)
return 'failed' // Indicate definitive failure for this transport return 'fallback' // If our logic above is wrong, we'd prevent HTTPs servers from ever working. So always fall back as a last resort.
} }
} // End of tryConnectWithTransport helper } // End of tryConnectWithTransport helper