(error: unknown)
| 388 | }; |
| 389 | |
| 390 | const handleAuthError = async (error: unknown) => { |
| 391 | if (isConnectionAuthError(error)) { |
| 392 | let scope = oauthScope?.trim(); |
| 393 | const fetchFn = |
| 394 | connectionType === "proxy" ? createProxyFetch(config) : undefined; |
| 395 | |
| 396 | if (!scope) { |
| 397 | // Only discover resource metadata when we need to discover scopes |
| 398 | let resourceMetadata; |
| 399 | try { |
| 400 | resourceMetadata = await discoverOAuthProtectedResourceMetadata( |
| 401 | new URL("/", sseUrl), |
| 402 | {}, |
| 403 | fetchFn, |
| 404 | ); |
| 405 | } catch { |
| 406 | // Resource metadata is optional, continue without it |
| 407 | } |
| 408 | scope = await discoverScopes(sseUrl, resourceMetadata, fetchFn); |
| 409 | } |
| 410 | |
| 411 | saveScopeToSessionStorage(sseUrl, scope); |
| 412 | const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl); |
| 413 | |
| 414 | try { |
| 415 | const result = await auth(serverAuthProvider, { |
| 416 | serverUrl: sseUrl, |
| 417 | scope, |
| 418 | ...(fetchFn && { fetchFn }), |
| 419 | }); |
| 420 | return result === "AUTHORIZED"; |
| 421 | } catch (authError) { |
| 422 | // Show user-friendly error message for OAuth failures |
| 423 | toast({ |
| 424 | title: "OAuth Authentication Failed", |
| 425 | description: |
| 426 | authError instanceof Error ? authError.message : String(authError), |
| 427 | variant: "destructive", |
| 428 | }); |
| 429 | return false; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | return false; |
| 434 | }; |
| 435 | |
| 436 | const captureResponseHeaders = (response: Response): void => { |
| 437 | const sessionId = response.headers.get("mcp-session-id"); |
no test coverage detected