From 8a95ec3ea634b1bbc92e7078dc07d60a0503ab35 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Mon, 19 Jan 2026 19:01:32 +0100 Subject: [PATCH] feat: Improve MCP Server selection UI + lazy load health checks --- .../webui/src/lib/clients/mcp.client.ts | 30 +++++++++++ .../ChatFormActionFileAttachments.svelte | 42 ++++++++++++--- .../ChatFormActions/ChatFormActions.svelte | 7 ++- .../dialogs/DialogMcpServersSettings.svelte | 20 ++++--- .../lib/components/app/mcp/McpSelector.svelte | 20 +++---- .../mcp/McpServerCard/McpServerCard.svelte | 52 +++++++++++++------ .../app/mcp/McpSettingsSection.svelte | 2 +- .../ui/tooltip/tooltip-content.svelte | 26 +++++++--- .../server/webui/src/lib/stores/mcp.svelte.ts | 6 +-- 9 files changed, 151 insertions(+), 54 deletions(-) diff --git a/tools/server/webui/src/lib/clients/mcp.client.ts b/tools/server/webui/src/lib/clients/mcp.client.ts index 455e3a9383..c89ac7f65a 100644 --- a/tools/server/webui/src/lib/clients/mcp.client.ts +++ b/tools/server/webui/src/lib/clients/mcp.client.ts @@ -494,6 +494,36 @@ export class MCPClient { return undefined; } + /** + * Run health checks for multiple servers that don't have a recent check. + * Useful for lazy-loading health checks when UI is opened. + * @param servers - Array of servers to check + * @param skipIfChecked - If true, skip servers that already have a health check result + */ + async runHealthChecksForServers( + servers: { + id: string; + enabled: boolean; + url: string; + requestTimeoutSeconds: number; + headers?: string; + }[], + skipIfChecked = true + ): Promise { + const serversToCheck = skipIfChecked + ? servers.filter((s) => !mcpStore.hasHealthCheck(s.id) && s.url.trim()) + : servers.filter((s) => s.url.trim()); + + if (serversToCheck.length === 0) return; + + const BATCH_SIZE = 5; + + for (let i = 0; i < serversToCheck.length; i += BATCH_SIZE) { + const batch = serversToCheck.slice(i, i + BATCH_SIZE); + await Promise.all(batch.map((server) => this.runHealthCheck(server))); + } + } + /** * Run health check for a specific server configuration. * Creates a temporary connection to test connectivity and list tools. diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionFileAttachments.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionFileAttachments.svelte index a49c680b40..ffe2041a4f 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionFileAttachments.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionFileAttachments.svelte @@ -4,6 +4,7 @@ import * as DropdownMenu from '$lib/components/ui/dropdown-menu'; import * as Tooltip from '$lib/components/ui/tooltip'; import { FILE_TYPE_ICONS } from '$lib/constants/icons'; + import { McpLogo } from '$lib/components/app'; interface Props { class?: string; @@ -12,6 +13,7 @@ hasVisionModality?: boolean; onFileUpload?: () => void; onSystemPromptClick?: () => void; + onMcpServersClick?: () => void; } let { @@ -20,18 +22,26 @@ hasAudioModality = false, hasVisionModality = false, onFileUpload, - onSystemPromptClick + onSystemPromptClick, + onMcpServersClick }: Props = $props(); + let dropdownOpen = $state(false); + + function handleMcpServersClick() { + dropdownOpen = false; + onMcpServersClick?.(); + } + const fileUploadTooltipText = $derived.by(() => { return !hasVisionModality ? 'Text files and PDFs supported. Images, audio, and video require vision models.' - : 'Attach files'; + : 'Add files, prompts and MCP Servers'; });
- + @@ -67,7 +77,7 @@ {#if !hasVisionModality} - +

Images require vision models to be processed

{/if} @@ -87,7 +97,7 @@ {#if !hasAudioModality} - +

Audio files require audio models to be processed

{/if} @@ -115,7 +125,7 @@ {#if !hasVisionModality} - +

PDFs will be converted to text. Image-based PDFs may not work properly.

{/if} @@ -134,10 +144,28 @@ - +

Add a custom system message for this conversation

+ + + + + + + + MCP Servers + + + + +

Configure MCP servers for agentic tool execution

+
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte index d3011912ab..95a2aff67f 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte @@ -16,6 +16,7 @@ import { isRouterMode } from '$lib/stores/server.svelte'; import { chatStore } from '$lib/stores/chat.svelte'; import { activeMessages } from '$lib/stores/conversations.svelte'; + import { mcpStore } from '$lib/stores/mcp.svelte'; interface Props { canSend?: boolean; @@ -157,6 +158,7 @@ } let showMcpDialog = $state(false); + let hasAvailableMcpServers = $derived(mcpStore.hasAvailableServers());
@@ -167,9 +169,12 @@ {hasVisionModality} {onFileUpload} {onSystemPromptClick} + onMcpServersClick={() => (showMcpDialog = true)} /> - (showMcpDialog = true)} /> + {#if hasAvailableMcpServers} + (showMcpDialog = true)} /> + {/if}
diff --git a/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte index f483b7682e..b3ba64b899 100644 --- a/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte +++ b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte @@ -1,7 +1,8 @@ {#if hasMcpServers} @@ -119,6 +114,7 @@ emptyMessage="No servers found" isEmpty={filteredMcpServers().length === 0} {disabled} + onOpenChange={handleDropdownOpen} > {#snippet trigger()}