mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-19 01:04:55 +02:00
ui : browse MCP resources from the server card
Make the Resources capability badge clickable so it opens the MCP resources browser dialog, and drop the page-only chrome from SettingsMcpServers. Assisted-by: pi
This commit is contained in:
@@ -1,13 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { Database, FileText, ListChecks, MessageSquare, Sparkles, Wrench } from '@lucide/svelte';
|
||||
import {
|
||||
Database,
|
||||
ExternalLink,
|
||||
FileText,
|
||||
ListChecks,
|
||||
MessageSquare,
|
||||
Sparkles,
|
||||
Wrench
|
||||
} from '@lucide/svelte';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import type { MCPCapabilitiesInfo } from '$lib/types';
|
||||
|
||||
interface Props {
|
||||
capabilities?: MCPCapabilitiesInfo;
|
||||
onBrowseResources?: () => void;
|
||||
}
|
||||
|
||||
let { capabilities }: Props = $props();
|
||||
let { capabilities, onBrowseResources }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if capabilities}
|
||||
@@ -20,10 +29,24 @@
|
||||
{/if}
|
||||
|
||||
{#if capabilities.server.resources}
|
||||
<Badge class="h-5 gap-1 bg-blue-50 px-1.5 text-[10px] dark:bg-blue-950" variant="outline">
|
||||
<Badge
|
||||
class="h-5 cursor-pointer gap-1 bg-blue-50 px-1.5 text-[10px] transition-colors hover:bg-blue-100 dark:bg-blue-950 dark:hover:bg-blue-900"
|
||||
onclick={onBrowseResources}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onBrowseResources?.();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabindex={0}
|
||||
variant="outline"
|
||||
>
|
||||
<Database class="h-3 w-3 text-blue-600 dark:text-blue-400" />
|
||||
|
||||
Resources
|
||||
|
||||
<ExternalLink class="h-3 w-3 text-blue-600 dark:text-blue-400" />
|
||||
</Badge>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
onToggle: (enabled: boolean) => void;
|
||||
onUpdate: (updates: Partial<MCPServerSettingsEntry>) => void;
|
||||
onDelete: () => void;
|
||||
onBrowseResources?: () => void;
|
||||
}
|
||||
|
||||
let { enabled, onDelete, onToggle, onUpdate, server }: Props = $props();
|
||||
let { enabled, onBrowseResources, onDelete, onToggle, onUpdate, server }: Props = $props();
|
||||
|
||||
let healthState = $derived<HealthCheckState>(mcpStore.getHealthCheckState(server.id));
|
||||
let displayName = $derived(mcpStore.getServerLabel(server));
|
||||
@@ -125,6 +126,7 @@
|
||||
{displayName}
|
||||
enabled={enabled ?? server.enabled}
|
||||
{faviconUrl}
|
||||
{onBrowseResources}
|
||||
{onToggle}
|
||||
{serverInfo}
|
||||
{transportType}
|
||||
@@ -191,12 +193,14 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<McpServerCardActions
|
||||
{isHealthChecking}
|
||||
onDelete={handleDeleteClick}
|
||||
onEdit={startEditing}
|
||||
onRefresh={handleHealthCheck}
|
||||
/>
|
||||
<div class="flex items-center gap-2">
|
||||
<McpServerCardActions
|
||||
{isHealthChecking}
|
||||
onDelete={handleDeleteClick}
|
||||
onEdit={startEditing}
|
||||
onRefresh={handleHealthCheck}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</Card.Root>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
enabled: boolean;
|
||||
disabled?: boolean;
|
||||
onToggle: (enabled: boolean) => void;
|
||||
onBrowseResources?: () => void;
|
||||
serverInfo?: MCPServerInfo;
|
||||
capabilities?: MCPCapabilitiesInfo;
|
||||
transportType?: MCPTransportType;
|
||||
@@ -23,6 +24,7 @@
|
||||
displayName,
|
||||
enabled,
|
||||
faviconUrl,
|
||||
onBrowseResources,
|
||||
onToggle,
|
||||
serverInfo,
|
||||
transportType
|
||||
@@ -57,7 +59,7 @@
|
||||
{/if}
|
||||
|
||||
{#if capabilities}
|
||||
<McpCapabilitiesBadges {capabilities} />
|
||||
<McpCapabilitiesBadges {capabilities} {onBrowseResources} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<script lang="ts">
|
||||
import McpLogo from '../mcp/McpLogo.svelte';
|
||||
import { Plus, X } from '@lucide/svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import { goto, replaceState } from '$app/navigation';
|
||||
import { Plus } from '@lucide/svelte';
|
||||
import { replaceState } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { ActionIcon, McpServerCard, McpServerCardSkeleton } from '$lib/components/app';
|
||||
import { DialogMcpServerAddNew } from '$lib/components/app/dialogs';
|
||||
import { McpServerCard, McpServerCardSkeleton } from '$lib/components/app';
|
||||
import { DialogMcpResourcesBrowser, DialogMcpServerAddNew } from '$lib/components/app/dialogs';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Empty from '$lib/components/ui/empty';
|
||||
import { ROUTES } from '$lib/constants';
|
||||
import { HealthCheckStatus } from '$lib/enums';
|
||||
import { conversationsStore, mcpStore, toolsStore } from '$lib/stores';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -23,26 +20,7 @@
|
||||
let servers = $derived(mcpStore.getServers());
|
||||
|
||||
let isAddingServer = $state(false);
|
||||
|
||||
let previousRouteId = $state<string | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
const currentId = page.route.id;
|
||||
|
||||
return () => {
|
||||
previousRouteId = currentId;
|
||||
};
|
||||
});
|
||||
|
||||
function handleClose() {
|
||||
const prevIsMcpServers = previousRouteId === '/mcp-servers';
|
||||
|
||||
if (browser && window.history.length > 1 && !prevIsMcpServers) {
|
||||
history.back();
|
||||
} else {
|
||||
goto(ROUTES.START);
|
||||
}
|
||||
}
|
||||
let isResourcesDialogOpen = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
if (page.url.searchParams.has('add')) {
|
||||
@@ -71,25 +49,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div in:fade={{ duration: 150 }} class="flex min-h-[calc(100dvh-4rem)] flex-col">
|
||||
<div class="fixed top-4.5 right-4 z-50 md:hidden">
|
||||
<ActionIcon icon={X} onclick={handleClose} tooltip="Close" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="sticky top-0 z-10 mt-4 mb-2 flex items-start gap-4 md:p-4 p-0 px-4 md:justify-between md:px-8"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<McpLogo class="h-5 w-5 md:h-6 md:w-6" />
|
||||
|
||||
<h1 class="text-lg font-semibold md:text-2xl">MCP Servers</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div in:fade={{ duration: 150 }} class="flex flex-col h-full">
|
||||
<DialogMcpServerAddNew bind:open={isAddingServer} />
|
||||
|
||||
<DialogMcpResourcesBrowser bind:open={isResourcesDialogOpen} />
|
||||
|
||||
{#if servers.length === 0}
|
||||
<div class="flex flex-1 items-center justify-center py-16">
|
||||
<div class="flex flex-1 items-center justify-center pb-20 pt-10 my-auto">
|
||||
<Empty.Root class="max-w-md">
|
||||
<Empty.Header>
|
||||
<Empty.Media variant="icon">
|
||||
@@ -113,7 +79,7 @@
|
||||
{:else}
|
||||
<div
|
||||
class="grid gap-3 {className}"
|
||||
style="grid-template-columns: repeat(auto-fill, minmax(min(32rem, calc(100dvw - 2rem)), 1fr));"
|
||||
style="grid-template-columns: repeat(auto-fill, minmax(min(25rem, calc(100dvw - 2rem)), 1fr));"
|
||||
>
|
||||
{#each servers as server (server.id)}
|
||||
{#if isServerPending(server.id, server.enabled)}
|
||||
@@ -121,6 +87,7 @@
|
||||
{:else}
|
||||
<McpServerCard
|
||||
enabled={conversationsStore.preferences.isMcpServerEnabledForChat(server.id)}
|
||||
onBrowseResources={() => (isResourcesDialogOpen = true)}
|
||||
onDelete={() => mcpStore.removeServer(server.id)}
|
||||
onToggle={async () => {
|
||||
const wasEnabled = conversationsStore.preferences.isMcpServerEnabledForChat(
|
||||
|
||||
Reference in New Issue
Block a user