# How UI Tool Metadata Enables Rich File Previews in Claude Desktop

> Discover how Claude Desktop leverages UI tool metadata to provide rich file previews for images, PDFs, and text. Enhance your workflow with dynamic content rendering.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: deep-dive
- Published: 2026-08-03

---

**Claude Desktop renders rich file previews by coupling UI tool metadata with type-specific payload data, allowing the interface to dynamically switch between image, PDF, and text renderers based on file content.**

The DesktopCommanderMCP server powers this experience through a two-layer metadata system: **tool-level metadata** tells Claude Desktop which UI widget to load, while **file-type metadata** embedded in each response determines how that widget renders the content. This architecture enables context-aware previews without hardcoding file handlers in the client.

## Understanding the Two-Layer Metadata Architecture

### Layer 1: Tool Metadata Defines the UI Widget

Tool metadata lives in [`src/ui/contracts.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/contracts.ts) and provides the bridge between MCP tools and their corresponding UI components.

The `FILE_PREVIEW_RESOURCE_URI` constant identifies the file-preview widget:

```typescript
// src/ui/contracts.ts
export const FILE_PREVIEW_RESOURCE_URI = "file-preview";

```

The `UiToolMeta` type and `buildUiToolMeta` helper construct the metadata object:

```typescript
// src/ui/contracts.ts
export type UiToolMeta = Record<string, unknown>;

export function buildUiToolMeta(
  resourceUri: string,
  showMcpUiPreviews: boolean,
  shouldShowUI: boolean
): UiToolMeta {
  return {
    _meta: {
      resourceUri,
      showMcpUiPreviews,
      shouldShowUI,
    },
  };
}

```

When registering the `read_file` tool in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), the server attaches this metadata at lines 418, 486, 607, and 867:

```typescript
// src/server.ts (excerpt)
{
  name: 'read_file',
  description: 'Read a file from the filesystem',
  // ... other tool configuration
  _meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true, showMcpUiPreviews),
}

```

This `_meta` field signals to Claude Desktop: "Load the `file-preview` resource when this tool returns content."

### Layer 2: File-Type Metadata Drives Rendering Decisions

While tool metadata selects the widget, **file-type metadata** determines what that widget displays. File handlers in `src/utils/files/*.ts` extract this metadata during processing.

**PDF handling** ([`src/utils/files/pdf.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/pdf.ts), lines 56-71):

```typescript
const pdfResult = await parsePdfToMarkdown(path, { offset: 0, length: 0 });

return {
  content: '',
  mimeType: 'application/pdf',
  metadata: {
    author: pdfResult.metadata.author,
    title: pdfResult.metadata.title,
    totalPages: pdfResult.metadata.totalPages,
    isPdf: true,
    pages: pdfResult.pages,  // rendered markdown per page
  },
};

```

**Image handling** adds `isImage: true`. **Plain text** handlers may include `lineCount`. Each handler contributes domain-specific fields that the UI consumes.

## How the File Preview UI Consumes Metadata

The file-preview widget ([`src/ui/file-preview/src/app.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/file-preview/src/app.ts)) receives a `RenderPayload` containing both content and metadata. It delegates rendering decisions to `renderPayloadBody` in [`src/ui/file-preview/src/file-type-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/file-preview/src/file-type-handlers.ts) (lines 1-30):

```typescript
// src/ui/file-preview/src/file-type-handlers.ts
export function renderPayloadBody(payload: RenderPayload) {
  const { metadata } = payload;

  if (metadata?.isImage) {
    return `<img src="${payload.content}" alt="Image preview"/>`;
  }

  if (metadata?.isPdf) {
    return renderPdfViewer(metadata);
  }

  return renderMarkdown(payload.content);
}

```

The PDF renderer uses the structured metadata to build a complete viewer:

```typescript
function renderPdfViewer(metadata: PdfMetadata) {
  return `
    <div class="pdf-header">
      <h2>${metadata.title || 'Untitled'}</h2>
      <span>By ${metadata.author || 'Unknown'} • ${metadata.totalPages} pages</span>
    </div>
    <div class="pdf-pages">
      ${metadata.pages.map((page, i) => `
        <div class="page" data-page="${i + 1}">${page}</div>
      `).join('')}
    </div>
  `;
}

```

## Complete Implementation Flow

1. **Tool registration** — Server attaches `buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true, showMcpUiPreviews)` to `read_file`

2. **File read** — `read_file` executes and routes to type-specific handler in `src/utils/files/`

3. **Metadata extraction** — Handler returns `{ content, mimeType, metadata }` with type flags (`isPdf`, `isImage`, etc.)

4. **UI routing** — Claude Desktop sees `_meta.resourceUri: "file-preview"` and loads that widget

5. **Conditional rendering** — [`file-type-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/file-type-handlers.ts) inspects metadata flags and renders appropriate component

This separation of concerns allows new file types to add support without modifying the UI codebase—only the server-side handler and its returned metadata shape need changes.

## Summary

- **UI tool metadata** in [`src/ui/contracts.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/contracts.ts) and [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) binds MCP tools to specific UI widgets via `_meta` fields
- **`buildUiToolMeta`** constructs metadata objects that specify which resource URI to load and whether to show the UI
- **File-type handlers** in `src/utils/files/*.ts` return domain-specific metadata (`isPdf`, `isImage`, `author`, `totalPages`) alongside content
- **[`file-type-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/file-type-handlers.ts)** routes to appropriate renderers by inspecting metadata flags, enabling extensible preview types without UI changes
- **Claude Desktop** combines these layers to show rich, context-aware file previews without embedded file logic

## Frequently Asked Questions

### What is UI tool metadata in DesktopCommanderMCP?

UI tool metadata is a structured object attached to MCP tool definitions that tells Claude Desktop which UI component to render when that tool produces output. According to the DesktopCommanderMCP source code, this metadata is created by `buildUiToolMeta` in [`src/ui/contracts.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/contracts.ts) and attached via the `_meta` field during tool registration in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts).

### How does the file preview UI know which renderer to use?

The file preview UI examines the `metadata` field of the payload returned by the server. In [`src/ui/file-preview/src/file-type-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/file-preview/src/file-type-handlers.ts), the `renderPayloadBody` function checks for boolean flags like `isImage` or `isPdf`, then branches to the appropriate renderer. This design keeps rendering logic decoupled from file detection logic.

### Can new file types be supported without modifying the UI code?

Yes. Adding support for a new file type requires only: (1) creating a handler in `src/utils/files/` that returns appropriate metadata flags and content, and (2) adding a corresponding branch in [`src/ui/file-preview/src/file-type-handlers.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/ui/file-preview/src/file-type-handlers.ts). The UI widget itself remains generic and data-driven.

### Where is the PDF metadata like author and title extracted?

PDF metadata extraction occurs in [`src/utils/files/pdf.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/utils/files/pdf.ts) (lines 56-71). The handler parses the PDF, extracts document properties including `author`, `title`, and `totalPages`, and returns these in the metadata object alongside rendered page content. The UI then consumes these fields to populate the PDF viewer header.