diff --git a/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentMcpResource.svelte b/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentMcpResource.svelte
new file mode 100644
index 0000000000..6814391321
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentMcpResource.svelte
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+ {#if favicon}
+

{
+ (e.currentTarget as HTMLImageElement).style.display = 'none';
+ }}
+ />
+ {/if}
+
+
+ {serverName}
+
+
+
+
diff --git a/tools/server/webui/src/lib/components/app/chat/index.ts b/tools/server/webui/src/lib/components/app/chat/index.ts
index e01ae9942f..c5f1b74420 100644
--- a/tools/server/webui/src/lib/components/app/chat/index.ts
+++ b/tools/server/webui/src/lib/components/app/chat/index.ts
@@ -58,6 +58,11 @@ export { default as ChatAttachmentsList } from './ChatAttachments/ChatAttachment
*/
export { default as ChatAttachmentMcpPrompt } from './ChatAttachments/ChatAttachmentMcpPrompt.svelte';
+/**
+ * Todo - add description
+ */
+export { default as ChatAttachmentMcpResource } from './ChatAttachments/ChatAttachmentMcpResource.svelte';
+
/**
* Full-size attachment preview component for dialog display. Handles different file types:
* images (full-size display), text files (syntax highlighted), PDFs (text extraction or image preview),
diff --git a/tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte
new file mode 100644
index 0000000000..791d33c887
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpResources.svelte
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+ MCP Resources
+ {#if totalCount > 0}
+ ({totalCount})
+ {/if}
+
+
+ Browse and attach resources from connected MCP servers to your chat context.
+
+
+
+
+
+
+
+
+
+ {#if selectedResources.size === 1}
+ {@const allResources = getAllResourcesFlat()}
+ {@const selectedResource = allResources.find((r) => selectedResources.has(r.uri))}
+
+ {:else if selectedResources.size > 1}
+
+ {selectedResources.size} resources selected
+
+ {:else}
+
+ Select a resource to preview
+
+ {/if}
+
+
+
+
+ handleOpenChange(false)}>Cancel
+
+ {#if isAttaching}
+
+ {:else}
+
+ {/if}
+ Attach {selectedResources.size > 0 ? `(${selectedResources.size})` : 'Resource'}
+
+
+
+
diff --git a/tools/server/webui/src/lib/components/app/dialogs/index.ts b/tools/server/webui/src/lib/components/app/dialogs/index.ts
index c9b664326a..262d374f5b 100644
--- a/tools/server/webui/src/lib/components/app/dialogs/index.ts
+++ b/tools/server/webui/src/lib/components/app/dialogs/index.ts
@@ -443,3 +443,33 @@ export { default as DialogConversationSelection } from './DialogConversationSele
* ```
*/
export { default as DialogModelInformation } from './DialogModelInformation.svelte';
+
+/**
+ * **DialogMcpResources** - MCP resources browser dialog
+ *
+ * Dialog for browsing and attaching MCP resources to chat context.
+ * Displays resources from connected MCP servers in a tree structure
+ * with preview panel and multi-select support.
+ *
+ * **Architecture:**
+ * - Uses ShadCN Dialog with two-panel layout
+ * - Left panel: McpResourceBrowser with tree navigation
+ * - Right panel: McpResourcePreview for selected resource
+ * - Integrates with mcpStore for resource fetching and attachment
+ *
+ * **Features:**
+ * - Tree-based resource navigation by server and path
+ * - Single and multi-select with shift+click
+ * - Resource preview with content display
+ * - Quick attach button per resource
+ * - Batch attach for multiple selections
+ *
+ * @example
+ * ```svelte
+ *
+ * ```
+ */
+export { default as DialogMcpResources } from './DialogMcpResources.svelte';
diff --git a/tools/server/webui/src/lib/components/app/mcp/McpResourceBrowser.svelte b/tools/server/webui/src/lib/components/app/mcp/McpResourceBrowser.svelte
new file mode 100644
index 0000000000..ced60ba9a4
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/mcp/McpResourceBrowser.svelte
@@ -0,0 +1,402 @@
+
+
+{#snippet renderTreeNode(
+ node: ResourceTreeNode,
+ serverName: string,
+ depth: number,
+ parentPath: string
+)}
+ {@const isFolder = !node.resource && node.children.size > 0}
+ {@const folderId = `${serverName}:${parentPath}/${node.name}`}
+ {@const isFolderExpanded = expandedFolders.has(folderId)}
+
+ {#if isFolder}
+ {@const folderCount = countTreeResources(node)}
+ toggleFolder(folderId)}>
+
+ {#if isFolderExpanded}
+
+ {:else}
+
+ {/if}
+
+ {node.name}
+ ({folderCount})
+
+
+
+ {#each [...node.children.values()].sort((a, b) => {
+ // Folders first, then files
+ const aIsFolder = !a.resource && a.children.size > 0;
+ const bIsFolder = !b.resource && b.children.size > 0;
+ if (aIsFolder && !bIsFolder) return -1;
+ if (!aIsFolder && bIsFolder) return 1;
+ return a.name.localeCompare(b.name);
+ }) as child}
+ {@render renderTreeNode(child, serverName, depth + 1, `${parentPath}/${node.name}`)}
+ {/each}
+
+
+
+ {:else if node.resource}
+ {@const resource = node.resource}
+ {@const ResourceIcon = getResourceIcon(resource)}
+ {@const isSelected = isResourceSelected(resource)}
+ {@const displayName = resource.title || getDisplayName(node.name)}
+
+ {#if onToggle}
+
handleCheckboxChange(resource, checked === true)}
+ class="h-4 w-4"
+ />
+ {/if}
+ handleResourceClick(resource, e)}
+ >
+
+
+
+
+
+
+ {displayName}
+
+
+ {#if onAttach}
+ handleAttachClick(e, resource)}
+ >
+ Attach
+
+ {/if}
+
+
+ {/if}
+{/snippet}
+
+
+
+
Available resources
+
+
+ {#if isLoading}
+
+ {:else}
+
+ {/if}
+
+
+
+
+ {#if resources.size === 0}
+
+ {#if isLoading}
+ Loading resources...
+ {:else}
+ No resources available
+ {/if}
+
+ {:else}
+ {#each [...resources.entries()] as [serverName, serverRes]}
+ {@const isExpanded = expandedServers.has(serverName)}
+ {@const hasResources = serverRes.resources.length > 0}
+ {@const displayName = getServerDisplayName(serverName)}
+ {@const favicon = getServerFavicon(serverName)}
+ {@const resourceTree = buildResourceTree(serverRes.resources, serverName)}
+
toggleServer(serverName)}>
+
+ {#if isExpanded}
+
+ {:else}
+
+ {/if}
+ {#if favicon}
+
{
+ (e.currentTarget as HTMLImageElement).style.display = 'none';
+ }}
+ />
+ {/if}
+ {displayName}
+
+ ({serverRes.resources.length})
+
+ {#if serverRes.loading}
+
+ {/if}
+
+
+
+
+ {#if serverRes.error}
+
+ Error: {serverRes.error}
+
+ {:else if !hasResources}
+
No resources
+ {:else}
+ {#each [...resourceTree.children.values()].sort((a, b) => {
+ const aIsFolder = !a.resource && a.children.size > 0;
+ const bIsFolder = !b.resource && b.children.size > 0;
+
+ if (aIsFolder && !bIsFolder) return -1;
+ if (!aIsFolder && bIsFolder) return 1;
+
+ return a.name.localeCompare(b.name);
+ }) as child}
+ {@render renderTreeNode(child, serverName, 1, '')}
+ {/each}
+ {/if}
+
+
+
+ {/each}
+ {/if}
+
+
diff --git a/tools/server/webui/src/lib/components/app/mcp/McpResourcePicker.svelte b/tools/server/webui/src/lib/components/app/mcp/McpResourcePicker.svelte
new file mode 100644
index 0000000000..a822f18396
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/mcp/McpResourcePicker.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte b/tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte
new file mode 100644
index 0000000000..ba899147e5
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/mcp/McpResourcePreview.svelte
@@ -0,0 +1,194 @@
+
+
+
+ {#if !resource}
+
+
+ Select a resource to preview
+
+ {:else}
+
+
+
+
{resource.title || resource.name}
+
{resource.uri}
+ {#if resource.description}
+
{resource.description}
+ {/if}
+
+
+
+ {#if copied}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+ {#if isLoading}
+
+
+
+ {:else if error}
+
+ {:else if content}
+ {@const textContent = getTextContent()}
+ {@const blobContent = getBlobContent()}
+
+ {#if textContent}
+
{textContent}
+ {/if}
+
+ {#each blobContent as blob}
+ {#if isImageMimeType(blob.mimeType)}
+

+ {:else}
+
+
+ Binary content ({blob.mimeType || 'unknown type'})
+
+ {/if}
+ {/each}
+
+ {#if !textContent && blobContent.length === 0}
+
No content available
+ {/if}
+ {/if}
+
+
+
+ {#if resource.mimeType || resource.annotations}
+
+ {#if resource.mimeType}
+ {resource.mimeType}
+ {/if}
+ {#if resource.annotations?.priority !== undefined}
+
+ Priority: {resource.annotations.priority}
+
+ {/if}
+
+ Server: {resource.serverName}
+
+
+ {/if}
+ {/if}
+
diff --git a/tools/server/webui/src/lib/components/app/mcp/index.ts b/tools/server/webui/src/lib/components/app/mcp/index.ts
index 44e0ec69ce..ea353ca5c0 100644
--- a/tools/server/webui/src/lib/components/app/mcp/index.ts
+++ b/tools/server/webui/src/lib/components/app/mcp/index.ts
@@ -211,3 +211,50 @@ export { default as McpServerCardDeleteDialog } from './McpServerCard/McpServerC
* Displays guidance text from the MCP server for users.
*/
export { default as McpServerInfo } from './McpServerInfo.svelte';
+
+/**
+ * **McpResourceBrowser** - MCP resources tree browser
+ *
+ * Tree view component showing resources grouped by server.
+ * Supports resource selection and quick attach actions.
+ *
+ * **Features:**
+ * - Collapsible server sections
+ * - Resource icons based on MIME type
+ * - Resource selection highlighting
+ * - Quick attach button per resource
+ * - Refresh all resources action
+ * - Loading states per server
+ */
+export { default as McpResourceBrowser } from './McpResourceBrowser.svelte';
+
+/**
+ * **McpResourcePreview** - MCP resource content preview
+ *
+ * Preview panel showing resource content with metadata.
+ * Supports text and binary content display.
+ *
+ * **Features:**
+ * - Text content display with monospace formatting
+ * - Image preview for image MIME types
+ * - Copy to clipboard action
+ * - Download content action
+ * - Resource metadata display (MIME type, priority, server)
+ * - Loading and error states
+ */
+export { default as McpResourcePreview } from './McpResourcePreview.svelte';
+
+/**
+ * **McpResourcePicker** - MCP resource selection dialog
+ *
+ * Full dialog for browsing and attaching MCP resources.
+ * Combines browser and preview panels.
+ *
+ * **Features:**
+ * - Split panel layout (browser + preview)
+ * - Resource selection with preview
+ * - Quick attach from browser
+ * - Attach selected resource action
+ * - Auto-fetch resources on open
+ */
+export { default as McpResourcePicker } from './McpResourcePicker.svelte';