From f89bcb90ca86d68fc47b4b611a1aa03bde54a32c Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Wed, 14 Jan 2026 11:45:47 +0100 Subject: [PATCH] feat: MCP Server Details --- .../McpCapabilitiesBadges.svelte | 78 +++++++++++++ .../McpServerCard/McpConnectionLogs.svelte | 56 +++++++++ .../mcp/McpServerCard/McpServerCard.svelte | 70 ++++++++--- .../McpServerCard/McpServerCardHeader.svelte | 110 +++++++++--------- .../McpServerCardToolsList.svelte | 33 +++--- .../mcp/McpServerCard/McpServerInfo.svelte | 35 ++++++ .../components/app/mcp/McpServerCard/index.ts | 3 + .../app/mcp/McpSettingsSection.svelte | 2 - .../server/webui/src/lib/utils/formatters.ts | 15 +++ tools/server/webui/src/lib/utils/mcp.ts | 38 +++++- 10 files changed, 344 insertions(+), 96 deletions(-) create mode 100644 tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpCapabilitiesBadges.svelte create mode 100644 tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpConnectionLogs.svelte create mode 100644 tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerInfo.svelte diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpCapabilitiesBadges.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpCapabilitiesBadges.svelte new file mode 100644 index 0000000000..32668cff15 --- /dev/null +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpCapabilitiesBadges.svelte @@ -0,0 +1,78 @@ + + +{#if capabilities} +
+
+
+ {#if capabilities.server.tools} + + + + Tools + + {/if} + + {#if capabilities.server.resources} + + + + Resources + + {/if} + + {#if capabilities.server.prompts} + + + + Prompts + + {/if} + + {#if capabilities.server.logging} + + + + Logging + + {/if} + + {#if capabilities.server.completions} + + + + Completions + + {/if} + + {#if capabilities.server.tasks} + + + + Tasks + + {/if} +
+
+
+{/if} diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpConnectionLogs.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpConnectionLogs.svelte new file mode 100644 index 0000000000..bc5b35aa6c --- /dev/null +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpConnectionLogs.svelte @@ -0,0 +1,56 @@ + + +{#if logs.length > 0} + +
+ + {#if isExpanded} + + {:else} + + {/if} + Connection Log ({logs.length}) + {#if connectionTimeMs !== undefined} + · Connected in {connectionTimeMs}ms + {/if} + +
+ + +
+ {#each logs as log (log.timestamp.getTime() + log.message)} + {@const Icon = getMcpLogLevelIcon(log.level)} +
+ + {formatTime(log.timestamp)} + + + {log.message} +
+ {/each} +
+
+
+{/if} diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte index 5e59cedf55..6256df5062 100644 --- a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCard.svelte @@ -10,6 +10,9 @@ import McpServerCardToolsList from './McpServerCardToolsList.svelte'; import McpServerCardEditForm from './McpServerCardEditForm.svelte'; import McpServerCardDeleteDialog from './McpServerCardDeleteDialog.svelte'; + import McpServerInfo from './McpServerInfo.svelte'; + import McpConnectionLogs from './McpConnectionLogs.svelte'; + import Badge from '$lib/components/ui/badge/badge.svelte'; interface Props { server: MCPServerSettingsEntry; @@ -38,6 +41,7 @@ ? healthState.logs : [] ); + let serverInfo = $derived( healthState.status === HealthCheckStatus.Success ? healthState.serverInfo : undefined ); @@ -61,6 +65,12 @@ let showDeleteDialog = $state(false); let editFormRef: McpServerCardEditForm | null = $state(null); + const transportLabels: Record = { + websocket: 'WebSocket', + streamable_http: 'HTTP', + sse: 'SSE' + }; + onMount(() => { if (!mcpStore.hasHealthCheck(server.id) && server.enabled && server.url.trim()) { mcpClient.runHealthCheck(server); @@ -101,7 +111,7 @@ } - + {#if isEditing} {#if isError && errorMessage} -

{errorMessage}

+

{errorMessage}

{/if} - {#if tools.length === 0 && server.url.trim()} -
- -
+ {#if isConnected && serverInfo?.description} +

+ {serverInfo.description} +

{/if} - {#if tools.length > 0} - + {#if isConnected && instructions} + + {/if} + + {#if tools.length > 0} + + {/if} + + {#if connectionLogs.length > 0} + + {/if} + + +
+ {#if transportType || protocolVersion} +
+ {#if transportType} + + {transportLabels[transportType] || transportType} + + {/if} + + {#if protocolVersion} + + MCP {protocolVersion} + + {/if} +
+ {/if} + + - {/if} +
{/if}
diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte index 05b3f81ac1..f1db76e80f 100644 --- a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardHeader.svelte @@ -1,71 +1,71 @@ -
-
- {#if faviconUrl} - { - (e.currentTarget as HTMLImageElement).style.display = 'none'; - }} - /> - {:else} - - {/if} -

{displayName}

- {#if serverUrl} - - - - {/if} - {#if isHealthChecking} - Checking... - {:else if isConnected} - Connected - {:else if isError} - Error - {/if} -
+
+
+
+
+ {#if faviconUrl} + { + (e.currentTarget as HTMLImageElement).style.display = 'none'; + }} + /> + {:else} +
+ +
+ {/if} -
- +

+ {serverInfo?.title || serverInfo?.name || displayName} +

+ + {#if serverInfo?.version} + + v{serverInfo.version} + + {/if} + + {#if serverInfo?.websiteUrl} + + + + {/if} +
+ + {#if capabilities} + + {/if} +
+ +
+ +
diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardToolsList.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardToolsList.svelte index d82336601e..d0397c17a9 100644 --- a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardToolsList.svelte +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardToolsList.svelte @@ -2,7 +2,6 @@ import { ChevronDown, ChevronRight } from '@lucide/svelte'; import * as Collapsible from '$lib/components/ui/collapsible'; import { Badge } from '$lib/components/ui/badge'; - import McpServerCardActions from './McpServerCardActions.svelte'; interface Tool { name: string; @@ -11,37 +10,33 @@ interface Props { tools: Tool[]; - isHealthChecking: boolean; - onEdit: () => void; - onRefresh: () => void; - onDelete: () => void; } - let { tools, isHealthChecking, onEdit, onRefresh, onDelete }: Props = $props(); + let { tools }: Props = $props(); let isExpanded = $state(false); let toolsCount = $derived(tools.length); -
- - {#if isExpanded} - - {:else} - - {/if} - {toolsCount} tools available · Show details - - -
+ + {#if isExpanded} + + {:else} + + {/if} + + {toolsCount} tools available · Show details + +
{#each tools as tool (tool.name)}
{tool.name} + {#if tool.description}

{tool.description}

{/if} diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerInfo.svelte b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerInfo.svelte new file mode 100644 index 0000000000..aecae6e57b --- /dev/null +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerInfo.svelte @@ -0,0 +1,35 @@ + + +{#if instructions} + + + {#if isExpanded} + + {:else} + + {/if} + + Server instructions + + + +

+ {instructions} +

+
+
+{/if} diff --git a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/index.ts b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/index.ts index 567b6e6fb5..d4235e5053 100644 --- a/tools/server/webui/src/lib/components/app/mcp/McpServerCard/index.ts +++ b/tools/server/webui/src/lib/components/app/mcp/McpServerCard/index.ts @@ -4,3 +4,6 @@ export { default as McpServerCardActions } from './McpServerCardActions.svelte'; export { default as McpServerCardToolsList } from './McpServerCardToolsList.svelte'; export { default as McpServerCardEditForm } from './McpServerCardEditForm.svelte'; export { default as McpServerCardDeleteDialog } from './McpServerCardDeleteDialog.svelte'; +export { default as McpServerInfo } from './McpServerInfo.svelte'; +export { default as McpCapabilitiesBadges } from './McpCapabilitiesBadges.svelte'; +export { default as McpConnectionLogs } from './McpConnectionLogs.svelte'; diff --git a/tools/server/webui/src/lib/components/app/mcp/McpSettingsSection.svelte b/tools/server/webui/src/lib/components/app/mcp/McpSettingsSection.svelte index 3657d6228d..378b4250be 100644 --- a/tools/server/webui/src/lib/components/app/mcp/McpSettingsSection.svelte +++ b/tools/server/webui/src/lib/components/app/mcp/McpSettingsSection.svelte @@ -66,7 +66,6 @@ {/if}
- {#if isAddingServer}
@@ -113,7 +112,6 @@
{/if} - {#if servers.length > 0}
{#each servers as server (server.id)} diff --git a/tools/server/webui/src/lib/utils/formatters.ts b/tools/server/webui/src/lib/utils/formatters.ts index 8dd237542c..5885a23305 100644 --- a/tools/server/webui/src/lib/utils/formatters.ts +++ b/tools/server/webui/src/lib/utils/formatters.ts @@ -67,3 +67,18 @@ export function formatJsonPretty(jsonString: string): string { return jsonString; } } + +/** + * Format time as HH:MM:SS in 24-hour format + * + * @param date - Date object to format + * @returns Formatted time string (HH:MM:SS) + */ +export function formatTime(date: Date): string { + return date.toLocaleTimeString('en-US', { + hour12: false, + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); +} diff --git a/tools/server/webui/src/lib/utils/mcp.ts b/tools/server/webui/src/lib/utils/mcp.ts index 4c9d264ecc..d3e8dc67ef 100644 --- a/tools/server/webui/src/lib/utils/mcp.ts +++ b/tools/server/webui/src/lib/utils/mcp.ts @@ -1,9 +1,11 @@ import type { MCPClientConfig, MCPServerConfig, MCPServerSettingsEntry } from '$lib/types'; import type { SettingsConfigType } from '$lib/types/settings'; import type { McpServerOverride } from '$lib/types/database'; -import { MCPTransportType } from '$lib/enums'; +import { MCPTransportType, MCPLogLevel } from '$lib/enums'; import { DEFAULT_MCP_CONFIG } from '$lib/constants/mcp'; import { normalizePositiveNumber } from '$lib/utils/number'; +import { Info, AlertTriangle, XCircle } from '@lucide/svelte'; +import type { Component } from 'svelte'; /** * Detects the MCP transport type from a URL. @@ -251,3 +253,37 @@ export function buildMcpClientConfig( servers }; } + +/** + * Get the appropriate icon component for a log level + * + * @param level - MCP log level + * @returns Lucide icon component + */ +export function getMcpLogLevelIcon(level: MCPLogLevel): Component { + switch (level) { + case MCPLogLevel.Error: + return XCircle; + case MCPLogLevel.Warn: + return AlertTriangle; + default: + return Info; + } +} + +/** + * Get the appropriate CSS class for a log level + * + * @param level - MCP log level + * @returns Tailwind CSS class string + */ +export function getMcpLogLevelClass(level: MCPLogLevel): string { + switch (level) { + case MCPLogLevel.Error: + return 'text-destructive'; + case MCPLogLevel.Warn: + return 'text-yellow-600 dark:text-yellow-500'; + default: + return 'text-muted-foreground'; + } +}