diff --git a/frontend/src/lib/components/flows/content/McpOAuthConnect.svelte b/frontend/src/lib/components/flows/content/McpOAuthConnect.svelte index 29e1aa3f70..cf9becefb5 100644 --- a/frontend/src/lib/components/flows/content/McpOAuthConnect.svelte +++ b/frontend/src/lib/components/flows/content/McpOAuthConnect.svelte @@ -17,26 +17,19 @@ interface Props { onConnected: (resourcePath: string, resourceName: string) => void onCancel?: () => void - onDiscoveryStatus?: ( - status: 'discovering' | 'supported' | 'unsupported', - error?: string - ) => void initialServerUrl?: string initialResourceName?: string initialResourcePath?: string updateExistingResource?: boolean - hideUntilDiscovered?: boolean } let { onConnected, onCancel, - onDiscoveryStatus, initialServerUrl = '', initialResourceName = '', initialResourcePath = '', - updateExistingResource = false, - hideUntilDiscovered = false + updateExistingResource = false }: Props = $props() let serverUrl = $state('') @@ -53,9 +46,6 @@ let connectDisabled = $derived( !resourcePath || (!updateExistingResource && pathError !== '') ) - let shouldRender = $derived( - !hideUntilDiscovered || status === 'discovered' || status === 'connecting' - ) initializeFromProps() @@ -77,7 +67,6 @@ async function discoverOAuth() { status = 'discovering' error = null - onDiscoveryStatus?.('discovering') try { discoveryResult = await McpOauthService.discoverMcpOauth({ requestBody: { mcp_server_url: serverUrl } @@ -92,13 +81,11 @@ } } status = 'discovered' - onDiscoveryStatus?.('supported') } catch (e: any) { console.error('Error discovering OAuth settings', e) const errorMessage = e.body?.message || e.body || e.message || 'Unknown error' error = `Failed to discover OAuth settings: ${errorMessage}` status = 'unsupported' - onDiscoveryStatus?.('unsupported', error) } } @@ -289,105 +276,103 @@ onDestroy(cleanup) -{#if shouldRender} -
-
- - {updateExistingResource - ? 'Connect selected MCP resource with OAuth' - : 'Connect MCP Server with OAuth'} - - {#if onCancel} - +
+
+ + {updateExistingResource + ? 'Connect selected MCP resource with OAuth' + : 'Connect MCP Server with OAuth'} + + {#if onCancel} + + {/if} +
+ + + + {#if status === 'idle'} + + {:else if status === 'discovering'} +
Discovering OAuth settings...
+ {:else if status === 'unsupported'} +
+
OAuth is not available for this MCP server.
+ {#if error} +
{error}
+ {/if} +
+ {:else if status === 'discovered' && discoveryResult} +
+ ✓ OAuth supported + {#if discoveryResult.supports_dynamic_registration} + (Dynamic Client Registration available) {/if}
-
-{/if} + + {:else if status === 'connecting'} +
Complete authentication in popup window...
+ {/if} + + {#if error && status !== 'unsupported'} +
{error}
+ {/if} +
diff --git a/frontend/src/lib/components/flows/content/McpToolEditor.svelte b/frontend/src/lib/components/flows/content/McpToolEditor.svelte index 5c0b9c30a6..789451bfd8 100644 --- a/frontend/src/lib/components/flows/content/McpToolEditor.svelte +++ b/frontend/src/lib/components/flows/content/McpToolEditor.svelte @@ -34,36 +34,6 @@ tool: McpTool } - type McpResourceValue = { - name?: string - url: string - token?: string | null - } - - type OAuthDiscoveryStatus = 'none' | 'discovering' | 'supported' | 'unsupported' - - function getMcpResourceValue(value: unknown): McpResourceValue | undefined { - if (!value || typeof value !== 'object') { - return undefined - } - - const candidate = value as Record - const url = typeof candidate.url === 'string' ? candidate.url : undefined - - if (!url) { - return undefined - } - - return { - name: typeof candidate.name === 'string' ? candidate.name : undefined, - url, - token: - typeof candidate.token === 'string' || candidate.token === null - ? candidate.token - : undefined - } - } - function getFallbackMcpResourceName(path: string, url?: string): string { if (path) { const parts = path.split('/') @@ -85,23 +55,10 @@ return error?.body?.message || error?.body || error?.message } - function isUnauthorizedError(error: any): boolean { - const status = error?.status - const message = String(getErrorMessage(error) ?? '') - - return ( - status === 401 || - status === 403 || - /\b(unauthorized|unauthorised|401|403)\b/i.test(message) - ) - } - let { tool = $bindable() }: Props = $props() let refreshCount = $state(0) let resourcePicker: ResourcePicker | undefined = $state() - let lastResourcePath = $state() - let oauthDiscoveryStatus = $state('none') let resourcePath = $derived(tool.value.resource_path) @@ -128,45 +85,12 @@ let toolOptions = $derived(safeSelectItems((tools.value ?? []).map((t) => t.name))) let error = $derived(getErrorMessage(tools.error)) - let selectedMcpResourceValue = $derived(getMcpResourceValue(selectedResource.value?.value)) - let selectedMcpHasToken = $derived( - typeof selectedMcpResourceValue?.token === 'string' && - selectedMcpResourceValue.token.trim().length > 0 - ) - let selectedMcpUrlOnly = $derived(Boolean(selectedMcpResourceValue?.url && !selectedMcpHasToken)) - let selectedMcpServerUrl = $derived(selectedMcpResourceValue?.url) - let selectedMcpResourceName = $derived( - selectedMcpResourceValue?.name || - getFallbackMcpResourceName(resourcePath, selectedMcpServerUrl) - ) - let selectedMcpUnauthorized = $derived( - Boolean(selectedMcpUrlOnly && isUnauthorizedError(tools.error)) - ) - let connectionEstablished = $derived( - Boolean(resourcePath?.length > 0 && tools.status !== 'loading' && !tools.error && tools.value) - ) - let oauthDiscoveryPending = $derived( - selectedMcpUnauthorized && - (oauthDiscoveryStatus === 'none' || oauthDiscoveryStatus === 'discovering') - ) - let showConnecting = $derived( - Boolean( - resourcePath?.length > 0 && - !connectionEstablished && - (tools.status === 'loading' || selectedResource.status === 'loading' || oauthDiscoveryPending) - ) - ) - let showOAuthFlow = $derived( - selectedMcpUnauthorized && oauthDiscoveryStatus !== 'unsupported' - ) - let connectionError = $derived( - resourcePath?.length > 0 && - !connectionEstablished && - !showConnecting && - (!selectedMcpUnauthorized || oauthDiscoveryStatus === 'unsupported') - ? error - : undefined - ) + // The backend returns 401 (NotAuthorized) when the MCP server requires authentication. + let unauthorized = $derived(tools.error?.status === 401) + + let resourceValue = $derived(selectedResource.value?.value as { url?: string; name?: string } | undefined) + let serverUrl = $derived(resourceValue?.url) + let resourceName = $derived(resourceValue?.name || getFallbackMcpResourceName(resourcePath, serverUrl)) $effect(() => { resourcePath @@ -191,22 +115,6 @@ }) }) - $effect(() => { - const currentPath = resourcePath - untrack(() => { - if (currentPath !== lastResourcePath) { - lastResourcePath = currentPath - oauthDiscoveryStatus = 'none' - } - }) - }) - - $effect(() => { - if (!selectedMcpUnauthorized && oauthDiscoveryStatus !== 'none') { - oauthDiscoveryStatus = 'none' - } - }) - $effect(() => { if (!tool.value.include_tools) { tool.value.include_tools = [] @@ -222,15 +130,10 @@ } }) - function handleOAuthDiscoveryStatus(status: Exclude) { - oauthDiscoveryStatus = status - } - - async function handleOAuthConnected(connectedResourcePath: string, resourceName: string) { + async function handleOAuthConnected(connectedResourcePath: string, connectedResourceName: string) { await resourcePicker?.refreshResources() tool.value.resource_path = connectedResourcePath - tool.summary = `MCP: ${resourceName}` - oauthDiscoveryStatus = 'none' + tool.summary = `MCP: ${connectedResourceName}` selectedResource.refresh() refreshCount += 1 } @@ -257,95 +160,89 @@
- {#if showConnecting} -
connecting to mcp server...
- {/if} - - {#if showOAuthFlow} - {#key `${resourcePath ?? ''}:${selectedMcpServerUrl ?? ''}`} - - {/key} - {/if} - - {#if connectionError} -
{`Failed to connect to MCP server: ${connectionError}`}
- {/if} - - {#if connectionEstablished} -
- -
- -
- {#snippet action()} - - {/snippet} -
-
- {#if (tools.value ?? []).length === 0} -
No tools returned by this MCP server.
- {:else} -
- {#each tools.value ?? [] as mcpTool (mcpTool.name)} -
- {mcpTool.name} - {#if mcpTool.description} - — {mcpTool.description} - {/if} -
- {/each} -
- {/if} -
+ {/key} + {:else if error} +
{`Failed to connect to MCP server: ${error}`}
+ {:else if tools.status === 'ok'} +
+
-
- {#if tool.value.include_tools && tool.value.exclude_tools} -
-
-
- -
-
- +
+ {#snippet action()} + + {/snippet} +
+
+ {#if (tools.value ?? []).length === 0} +
No tools returned by this MCP server.
+ {:else} +
+ {#each tools.value ?? [] as mcpTool (mcpTool.name)} +
+ {mcpTool.name} + {#if mcpTool.description} + — {mcpTool.description} + {/if} +
+ {/each} +
+ {/if}
+ + {#if tool.value.include_tools && tool.value.exclude_tools} +
+
+
+ +
+
+ +
+
+
+ {/if} {/if} {/if}