Update tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsList.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActionAttachmentsDropdown.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormActions/ChatFormActions.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPicker.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatForm/ChatFormPromptPicker/ChatFormPromptPickerArgumentForm.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessages.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/index.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/index.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/chat/index.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/content/CollapsibleContentBlock.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/content/CollapsibleContentBlock.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/content/MarkdownContent.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/content/MarkdownContent.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/content/MarkdownContent.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServerCard/McpServerCardDeleteDialog.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpCapabilitiesBadges.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpConnectionLogs.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServerForm.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServerSelector.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServersSettings.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServersSettings.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServersSettings.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/mcp/McpServersSettings.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/misc/index.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/misc/TruncatedText.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/misc/TruncatedText.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/components/app/misc/TruncatedText.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Update tools/server/webui/src/lib/services/mcp.service.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
Load more...
This commit is contained in:
Pascal 2026-02-02 16:38:24 +01:00 committed by Aleksander Grygier
parent 65e8bb6df4
commit ec604a03e1
21 changed files with 108 additions and 19 deletions

View File

@ -171,6 +171,7 @@
arguments: item.uploadedFile.mcpPrompt.arguments
}
: null}
{#if mcpPrompt}
<ChatAttachmentMcpPrompt
class="max-w-[300px] min-w-[200px]"

View File

@ -94,6 +94,7 @@
onclick={() => onFileUpload?.()}
>
<FILE_TYPE_ICONS.image class="h-4 w-4" />
<span>Images</span>
</DropdownMenu.Item>
{:else}
@ -104,9 +105,11 @@
disabled
>
<FILE_TYPE_ICONS.image class="h-4 w-4" />
<span>Images</span>
</DropdownMenu.Item>
</Tooltip.Trigger>
<Tooltip.Content side="right">
<p>Images require vision models to be processed</p>
</Tooltip.Content>
@ -119,6 +122,7 @@
onclick={() => onFileUpload?.()}
>
<FILE_TYPE_ICONS.audio class="h-4 w-4" />
<span>Audio Files</span>
</DropdownMenu.Item>
{:else}
@ -126,9 +130,11 @@
<Tooltip.Trigger class="w-full">
<DropdownMenu.Item class="audio-button flex cursor-pointer items-center gap-2" disabled>
<FILE_TYPE_ICONS.audio class="h-4 w-4" />
<span>Audio Files</span>
</DropdownMenu.Item>
</Tooltip.Trigger>
<Tooltip.Content side="right">
<p>Audio files require audio models to be processed</p>
</Tooltip.Content>
@ -140,6 +146,7 @@
onclick={() => onFileUpload?.()}
>
<FILE_TYPE_ICONS.text class="h-4 w-4" />
<span>Text Files</span>
</DropdownMenu.Item>
@ -149,6 +156,7 @@
onclick={() => onFileUpload?.()}
>
<FILE_TYPE_ICONS.pdf class="h-4 w-4" />
<span>PDF Files</span>
</DropdownMenu.Item>
{:else}
@ -159,9 +167,11 @@
onclick={() => onFileUpload?.()}
>
<FILE_TYPE_ICONS.pdf class="h-4 w-4" />
<span>PDF Files</span>
</DropdownMenu.Item>
</Tooltip.Trigger>
<Tooltip.Content side="right">
<p>PDFs will be converted to text. Image-based PDFs may not work properly.</p>
</Tooltip.Content>
@ -175,9 +185,11 @@
onclick={() => onSystemPromptClick?.()}
>
<MessageSquare class="h-4 w-4" />
<span>System Message</span>
</DropdownMenu.Item>
</Tooltip.Trigger>
<Tooltip.Content side="right">
<p>{systemMessageTooltip}</p>
</Tooltip.Content>

View File

@ -165,11 +165,13 @@
let hasMcpPromptsSupport = $derived.by(() => {
const perChatOverrides = conversationsStore.getAllMcpServerOverrides();
return mcpStore.hasPromptsCapability(perChatOverrides);
});
let hasMcpResourcesSupport = $derived.by(() => {
const perChatOverrides = conversationsStore.getAllMcpServerOverrides();
return mcpStore.hasResourcesCapability(perChatOverrides);
});
</script>

View File

@ -84,6 +84,7 @@
if (!initialized) {
prompts = [];
return;
}
@ -142,6 +143,7 @@
const fetchCompletions = debounce(async (argName: string, value: string) => {
if (!selectedPrompt || value.length < 1) {
suggestions[argName] = [];
return;
}

View File

@ -60,6 +60,7 @@
role="alert"
>
<span class="shrink-0"></span>
<span>{promptError}</span>
</div>
{/if}

View File

@ -162,13 +162,16 @@
onclick={() => (activeView = ChatMessageStatsView.TOOLS)}
>
<Wrench class="h-3 w-3" />
<span class="sr-only">Tools</span>
</button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>Tool calls</p>
</Tooltip.Content>
</Tooltip.Root>
<Tooltip.Root>
<Tooltip.Trigger>
<button
@ -180,9 +183,11 @@
onclick={() => (activeView = ChatMessageStatsView.SUMMARY)}
>
<Layers class="h-3 w-3" />
<span class="sr-only">Summary</span>
</button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>Agentic summary</p>
</Tooltip.Content>

View File

@ -105,9 +105,11 @@
: messages.filter((msg) => msg.type !== MessageRole.SYSTEM);
let lastAssistantIndex = -1;
for (let i = filteredMessages.length - 1; i >= 0; i--) {
if (filteredMessages[i].role === MessageRole.ASSISTANT) {
lastAssistantIndex = i;
break;
}
}

View File

@ -2,7 +2,7 @@
*
* ATTACHMENTS
*
* Components for displaying and managing file attachments in chat messages.
* Components for displaying and managing different attachment types in chat messages.
* Supports two operational modes:
* - **Readonly mode**: For displaying stored attachments in sent messages (DatabaseMessageExtra[])
* - **Editable mode**: For managing pending uploads in the input form (ChatUploadedFile[])
@ -96,7 +96,7 @@ export { default as ChatAttachmentsViewAll } from './ChatAttachments/ChatAttachm
* FORM
*
* Components for the chat input area. The form handles user input, file attachments,
* audio recording, and MCP prompt selection. It integrates with multiple stores:
* audio recording, and MCP prompts & resources selection. It integrates with multiple stores:
* - `chatStore` for message submission and generation control
* - `modelsStore` for model selection and validation
* - `mcpStore` for MCP prompt browsing and loading
@ -130,6 +130,7 @@ export { default as ChatAttachmentsViewAll } from './ChatAttachments/ChatAttachm
* - File upload via button dropdown (images/text/PDF), drag-drop, or paste
* - Audio recording with WAV conversion (when model supports audio)
* - MCP prompt picker with search and argument forms
* - MCP reource picker with component to list attached resources at the bottom of Chat Form
* - Model selector integration (router mode)
* - Loading state with stop button, disabled state for errors
*

View File

@ -62,7 +62,9 @@
{#if Icon}
<Icon class={iconClass} />
{/if}
<span class="font-mono text-sm font-medium">{title}</span>
{#if subtitle}
<span class="text-xs italic">{subtitle}</span>
{/if}
@ -76,6 +78,7 @@
})}
>
<ChevronsUpDownIcon class="h-4 w-4" />
<span class="sr-only">Toggle content</span>
</div>
</Collapsible.Trigger>

View File

@ -197,9 +197,11 @@
type?: string;
position?: { start?: { offset?: number }; end?: { offset?: number } };
};
if (n.position?.start?.offset != null && n.position?.end?.offset != null) {
return `${n.type}-${n.position.start.offset}-${n.position.end.offset}`;
}
return `${n.type}-idx${index}`;
}
@ -414,6 +416,7 @@
{ position: (child as { position?: unknown }).position } as HastRootContent,
index
);
nextBlocks.push({ id, html, contentHash: hash });
}
@ -425,6 +428,7 @@
const transformedRoot = (await processorInstance.run(
singleNodeRoot as MdastRoot
)) as HastRoot;
unstableHtml = processorInstance.stringify(transformedRoot);
}

View File

@ -40,6 +40,7 @@
async function loadResources() {
const perChatOverrides = conversationsStore.getAllMcpServerOverrides();
const initialized = await mcpStore.ensureInitialized(perChatOverrides);
if (initialized) {
await mcpStore.fetchAllResources();
}
@ -48,6 +49,7 @@
function handleOpenChange(newOpen: boolean) {
open = newOpen;
onOpenChange?.(newOpen);
if (!newOpen) {
selectedResources.clear();
lastSelectedUri = null;
@ -81,6 +83,7 @@
} else {
selectedResources.delete(resource.uri);
}
lastSelectedUri = resource.uri;
}
@ -101,6 +104,7 @@
if (selectedResources.size === 0) return;
isAttaching = true;
try {
const allResources = getAllResourcesFlat();
const resourcesToAttach = allResources.filter((r) => selectedResources.has(r.uri));
@ -111,6 +115,7 @@
}
const count = resourcesToAttach.length;
toast.success(
count === 1
? `Resource attached: ${resourcesToAttach[0].name}`
@ -127,9 +132,12 @@
async function handleQuickAttach(resource: MCPResourceInfo) {
isAttaching = true;
try {
await mcpStore.attachResource(resource.uri);
onAttach?.(resource);
toast.success(`Resource attached: ${resource.name}`);
} catch (error) {
console.error('Failed to attach resource:', error);
@ -144,11 +152,14 @@
<Dialog.Header class="border-b px-6 py-4">
<Dialog.Title class="flex items-center gap-2">
<FolderOpen class="h-5 w-5" />
<span>MCP Resources</span>
{#if totalCount > 0}
<span class="text-sm font-normal text-muted-foreground">({totalCount})</span>
{/if}
</Dialog.Title>
<Dialog.Description>
Browse and attach resources from connected MCP servers to your chat context.
</Dialog.Description>
@ -169,6 +180,7 @@
{#if selectedResources.size === 1}
{@const allResources = getAllResourcesFlat()}
{@const selectedResource = allResources.find((r) => selectedResources.has(r.uri))}
<McpResourcePreview resource={selectedResource ?? null} />
{:else if selectedResources.size > 1}
<div class="flex h-full items-center justify-center text-sm text-muted-foreground">
@ -184,12 +196,14 @@
<Dialog.Footer class="border-t px-6 py-4">
<Button variant="outline" onclick={() => handleOpenChange(false)}>Cancel</Button>
<Button onclick={handleAttach} disabled={selectedResources.size === 0 || isAttaching}>
{#if isAttaching}
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
{:else}
<Plus class="mr-2 h-4 w-4" />
{/if}
Attach {selectedResources.size > 0 ? `(${selectedResources.size})` : 'Resource'}
</Button>
</Dialog.Footer>

View File

@ -14,6 +14,7 @@
{#if capabilities.server.tools}
<Badge variant="outline" class="h-5 gap-1 bg-green-50 px-1.5 text-[10px] dark:bg-green-950">
<Wrench class="h-3 w-3 text-green-600 dark:text-green-400" />
Tools
</Badge>
{/if}
@ -21,6 +22,7 @@
{#if capabilities.server.resources}
<Badge variant="outline" class="h-5 gap-1 bg-blue-50 px-1.5 text-[10px] dark:bg-blue-950">
<Database class="h-3 w-3 text-blue-600 dark:text-blue-400" />
Resources
</Badge>
{/if}
@ -28,6 +30,7 @@
{#if capabilities.server.prompts}
<Badge variant="outline" class="h-5 gap-1 bg-purple-50 px-1.5 text-[10px] dark:bg-purple-950">
<MessageSquare class="h-3 w-3 text-purple-600 dark:text-purple-400" />
Prompts
</Badge>
{/if}
@ -35,6 +38,7 @@
{#if capabilities.server.logging}
<Badge variant="outline" class="h-5 gap-1 bg-orange-50 px-1.5 text-[10px] dark:bg-orange-950">
<FileText class="h-3 w-3 text-orange-600 dark:text-orange-400" />
Logging
</Badge>
{/if}
@ -42,6 +46,7 @@
{#if capabilities.server.completions}
<Badge variant="outline" class="h-5 gap-1 bg-cyan-50 px-1.5 text-[10px] dark:bg-cyan-950">
<Sparkles class="h-3 w-3 text-cyan-600 dark:text-cyan-400" />
Completions
</Badge>
{/if}
@ -49,6 +54,7 @@
{#if capabilities.server.tasks}
<Badge variant="outline" class="h-5 gap-1 bg-pink-50 px-1.5 text-[10px] dark:bg-pink-950">
<ListChecks class="h-3 w-3 text-pink-600 dark:text-pink-400" />
Tasks
</Badge>
{/if}

View File

@ -28,6 +28,7 @@
{:else}
<ChevronRight class="h-3.5 w-3.5" />
{/if}
<span>Connection Log ({logs.length})</span>
{#if connectionTimeMs !== undefined}

View File

@ -94,18 +94,21 @@
{#if !resource}
<div class="flex flex-col items-center justify-center gap-2 py-8 text-muted-foreground">
<FileText class="h-8 w-8 opacity-50" />
<span class="text-sm">Select a resource to preview</span>
</div>
{:else}
<!-- Header -->
<div class="flex items-start justify-between gap-2">
<div class="min-w-0 flex-1">
<h3 class="truncate font-medium">{resource.title || resource.name}</h3>
<p class="truncate text-xs text-muted-foreground">{resource.uri}</p>
{#if resource.description}
<p class="mt-1 text-sm text-muted-foreground">{resource.description}</p>
{/if}
</div>
<div class="flex gap-1">
<Button
variant="ghost"
@ -121,6 +124,7 @@
<Copy class="h-3.5 w-3.5" />
{/if}
</Button>
<Button
variant="ghost"
size="sm"
@ -134,7 +138,6 @@
</div>
</div>
<!-- Content -->
<div class="min-h-[200px] overflow-auto rounded-md border bg-muted/30 p-3">
{#if isLoading}
<div class="flex items-center justify-center py-8">
@ -143,6 +146,7 @@
{:else if error}
<div class="flex flex-col items-center justify-center gap-2 py-8 text-red-500">
<AlertCircle class="h-6 w-6" />
<span class="text-sm">{error}</span>
</div>
{:else if content}
@ -163,6 +167,7 @@
{:else}
<div class="flex items-center gap-2 rounded bg-muted p-2 text-sm text-muted-foreground">
<FileText class="h-4 w-4" />
<span>Binary content ({blob.mimeType || 'unknown type'})</span>
</div>
{/if}
@ -174,17 +179,18 @@
{/if}
</div>
<!-- Metadata -->
{#if resource.mimeType || resource.annotations}
<div class="flex flex-wrap gap-2 text-xs text-muted-foreground">
{#if resource.mimeType}
<span class="rounded bg-muted px-1.5 py-0.5">{resource.mimeType}</span>
{/if}
{#if resource.annotations?.priority !== undefined}
<span class="rounded bg-muted px-1.5 py-0.5">
Priority: {resource.annotations.priority}
</span>
{/if}
<span class="rounded bg-muted px-1.5 py-0.5">
Server: {resource.serverName}
</span>

View File

@ -15,13 +15,16 @@
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Delete Server</AlertDialog.Title>
<AlertDialog.Description>
Are you sure you want to delete <strong>{displayName}</strong>? This action cannot be
undone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action
class="text-destructive-foreground bg-destructive hover:bg-destructive/90"
onclick={onConfirm}

View File

@ -65,6 +65,7 @@
checked={useProxy}
onCheckedChange={(checked) => onUseProxyChange?.(checked)}
/>
<span class="text-xs text-muted-foreground">Use llama-server proxy</span>
</label>
{/if}

View File

@ -110,14 +110,18 @@
}}
/>
{/if}
<span class="truncate text-sm">{getServerLabel(server)}</span>
{#if hasError}
<span
class="shrink-0 rounded bg-destructive/15 px-1.5 py-0.5 text-xs text-destructive"
>Error</span
>
Error
</span>
{/if}
</div>
<Switch
checked={isEnabledForChat}
disabled={hasError}
@ -131,6 +135,7 @@
{#snippet footer()}
<DropdownMenu.Item class="flex cursor-pointer items-center gap-2" onclick={onSettingsClick}>
<Settings class="h-4 w-4" />
<span>Manage MCP Servers</span>
</DropdownMenu.Item>
{/snippet}

View File

@ -19,6 +19,7 @@
servers.length > 0 &&
servers.every((server) => {
const state = mcpStore.getHealthCheckState(server.id);
return state.status === 'success' || state.status === 'error';
});
@ -55,11 +56,13 @@
function saveNewServer() {
if (newServerUrlError) return;
mcpStore.addServer({
enabled: true,
url: newServerUrl.trim(),
headers: newServerHeaders.trim() || undefined
});
isAddingServer = false;
newServerUrl = '';
newServerHeaders = '';
@ -75,6 +78,7 @@
{#if !isAddingServer}
<Button variant="outline" size="sm" class="shrink-0" onclick={showAddServerForm}>
<Plus class="h-4 w-4" />
Add New Server
</Button>
{/if}
@ -85,6 +89,7 @@
<div class="space-y-4">
<div class="flex items-center justify-between">
<p class="font-medium">Add New Server</p>
<Button
variant="ghost"
size="icon"

View File

@ -1,7 +1,4 @@
<script lang="ts">
/**
* TruncatedText - Shows tooltip only when text is actually truncated
*/
import * as Tooltip from '$lib/components/ui/tooltip';
interface Props {
@ -23,9 +20,10 @@
$effect(() => {
if (textElement) {
checkTruncation();
// Re-check on resize
const observer = new ResizeObserver(checkTruncation);
observer.observe(textElement);
return () => observer.disconnect();
}
});
@ -38,6 +36,7 @@
{text}
</span>
</Tooltip.Trigger>
<Tooltip.Content class="z-[9999]">
<p>{text}</p>
</Tooltip.Content>

View File

@ -6,14 +6,6 @@
*
*/
/**
* **TruncatedText** - Text with ellipsis and tooltip
*
* Displays text with automatic truncation and full content in tooltip.
* Useful for long names or paths in constrained spaces.
*/
export { default as TruncatedText } from './TruncatedText.svelte';
/**
* **ConversationSelection** - Multi-select conversation picker
*
@ -35,3 +27,11 @@ export { default as ConversationSelection } from './ConversationSelection.svelte
* with left/right navigation buttons that appear on hover.
*/
export { default as HorizontalScrollCarousel } from './HorizontalScrollCarousel.svelte';
/**
* **TruncatedText** - Text with ellipsis and tooltip
*
* Displays text with automatic truncation and full content in tooltip.
* Useful for long names or paths in constrained spaces.
*/
export { default as TruncatedText } from './TruncatedText.svelte';

View File

@ -200,7 +200,9 @@ export class MCPService {
)
);
console.log(`[MCPService][${serverName}] Creating transport...`);
if (import.meta.env.DEV) {
console.log(`[MCPService][${serverName}] Creating transport...`);
}
const { transport, type: transportType } = this.createTransport(serverConfig);
// Phase: Transport ready
@ -414,8 +416,10 @@ export class MCPService {
if (content.type === 'resource' && content.resource) {
const resource = content.resource;
if (resource.text) return resource.text;
if (resource.blob) return resource.blob;
return JSON.stringify(resource);
}
@ -453,9 +457,11 @@ export class MCPService {
ref,
argument
});
return result.completion;
} catch (error) {
console.error(`[MCPService] Failed to get completions:`, error);
return null;
}
}
@ -480,12 +486,14 @@ export class MCPService {
): Promise<{ resources: MCPResource[]; nextCursor?: string }> {
try {
const result = await connection.client.listResources(cursor ? { cursor } : undefined);
return {
resources: (result.resources ?? []) as MCPResource[],
nextCursor: result.nextCursor
};
} catch (error) {
console.warn(`[MCPService][${connection.serverName}] Failed to list resources:`, error);
return { resources: [] };
}
}
@ -520,6 +528,7 @@ export class MCPService {
): Promise<{ resourceTemplates: MCPResourceTemplate[]; nextCursor?: string }> {
try {
const result = await connection.client.listResourceTemplates(cursor ? { cursor } : undefined);
return {
resourceTemplates: (result.resourceTemplates ?? []) as MCPResourceTemplate[],
nextCursor: result.nextCursor
@ -529,6 +538,7 @@ export class MCPService {
`[MCPService][${connection.serverName}] Failed to list resource templates:`,
error
);
return { resourceTemplates: [] };
}
}
@ -563,12 +573,14 @@ export class MCPService {
): Promise<MCPReadResourceResult> {
try {
const result = await connection.client.readResource({ uri });
return {
contents: (result.contents ?? []) as MCPResourceContent[],
_meta: result._meta
};
} catch (error) {
console.error(`[MCPService][${connection.serverName}] Failed to read resource:`, error);
throw error;
}
}
@ -582,12 +594,14 @@ export class MCPService {
static async subscribeResource(connection: MCPConnection, uri: string): Promise<void> {
try {
await connection.client.subscribeResource({ uri });
console.log(`[MCPService][${connection.serverName}] Subscribed to resource: ${uri}`);
} catch (error) {
console.error(
`[MCPService][${connection.serverName}] Failed to subscribe to resource:`,
error
);
throw error;
}
}
@ -600,12 +614,14 @@ export class MCPService {
static async unsubscribeResource(connection: MCPConnection, uri: string): Promise<void> {
try {
await connection.client.unsubscribeResource({ uri });
console.log(`[MCPService][${connection.serverName}] Unsubscribed from resource: ${uri}`);
} catch (error) {
console.error(
`[MCPService][${connection.serverName}] Failed to unsubscribe from resource:`,
error
);
throw error;
}
}