Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions docs/src/guide/client/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ The `@mcp-ui/client` package helps you render UI resources sent from an MCP-enab

## What's Included?

### Components
- **`<UIResourceRenderer />`**: The main component you'll use. It inspects the resource's `mimeType` and renders either `<HTMLResourceRenderer />` or `<RemoteDOMResourceRenderer />` internally.
- **`<HTMLResourceRenderer />`**: Internal component for HTML/URL resources
- **`<RemoteDOMResourceRenderer />`**: Internal component for remote DOM resources
- **`isUIResource()`**: Utility function to check if content is a UI resource (replaces manual `content.type === 'resource' && content.resource.uri?.startsWith('ui://')` checks)

### Utility Functions
- **`getResourceMetadata(resource)`**: Extracts the resource's `_meta` content (standard MCP metadata)
- **`getUIResourceMetadata(resource)`**: Extracts only the MCP-UI specific metadata keys (prefixed with `mcpui.dev/ui-`) from the resource's `_meta` content

## Purpose
- **Standardized UI**: mcp-ui's client guarantees full compatibility with the latest MCP UI standards.
- **Simplified Rendering**: Abstract away the complexities of handling different resource types.
Expand All @@ -25,6 +30,94 @@ To build just this package from the monorepo root:
pnpm build --filter @mcp-ui/client
```

## Utility Functions Reference

### `getResourceMetadata(resource)`

Extracts the standard MCP metadata from a resource's `_meta` property.

```typescript
import { getResourceMetadata } from '@mcp-ui/client';

const resource = {
uri: 'ui://example/demo',
mimeType: 'text/html',
text: '<div>Hello</div>',
_meta: {
title: 'Demo Component',
version: '1.0.0',
'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
author: 'Development Team'
}
};

const metadata = getResourceMetadata(resource);
console.log(metadata);
// Output: {
// title: 'Demo Component',
// version: '1.0.0',
// 'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
// 'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
// author: 'Development Team'
// }
```

### `getUIResourceMetadata(resource)`

Extracts only the MCP-UI specific metadata keys (those prefixed with `mcpui.dev/ui-`) from a resource's `_meta` property, with the prefixes removed for easier access.

```typescript
import { getUIResourceMetadata } from '@mcp-ui/client';

const resource = {
uri: 'ui://example/demo',
mimeType: 'text/html',
text: '<div>Hello</div>',
_meta: {
title: 'Demo Component',
version: '1.0.0',
'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
author: 'Development Team'
}
};

const uiMetadata = getUIResourceMetadata(resource);
console.log(uiMetadata);
// Output: {
// 'preferred-frame-size': ['800px', '600px'],
// 'initial-render-data': { theme: 'dark' },
// }
```

### Usage Examples

These utility functions are particularly useful when you need to access metadata programmatically:

```typescript
import { getUIResourceMetadata, UIResourceRenderer } from '@mcp-ui/client';

function SmartResourceRenderer({ resource }) {
const uiMetadata = getUIResourceMetadata(resource);

// Use metadata to make rendering decisions
const initialRenderData = uiMetadata['initial-render-data'];
const containerClass = initialRenderData.preferredContext === 'hero' ? 'hero-container' : 'default-container';

return (
<div className={containerClass}>
{preferredContext === 'hero' && (
<h2>Featured Component</h2>
)}
<UIResourceRenderer resource={resource} />
</div>
);
}
```

## See More

See the following pages for more details:

- [UIResourceRenderer Component](./resource-renderer.md) - **Main entry point**
Expand Down
61 changes: 61 additions & 0 deletions docs/src/guide/client/resource-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ For developers using frameworks other than React, a Web Component version is ava
- **External URLs** (`text/uri-list`): External applications and websites
- **Remote DOM Resources** (`application/vnd.mcp-ui.remote-dom`): Server-generated UI components using Shopify's remote-dom

## Metadata Integration

The `UIResourceRenderer` automatically detects and uses metadata from resources created with the server SDK's `createUIResource()` function. It looks for MCP-UI specific metadata keys prefixed with `mcpui.dev/ui-` in the resource's `_meta` property:

### Automatic Frame Sizing
- **`mcpui.dev/ui-preferred-frame-size`**: When present, this metadata is used as the initial size for iframe-based resources, overriding any default sizing behavior.

### Automatic Data Passing
- **`mcpui.dev/ui-initial-render-data`**: When present, this data is automatically merged with the `iframeRenderData` prop (if provided) and passed to the iframe using the `ui-lifecycle-iframe-render-data` mechanism.

## Props

```typescript
Expand Down Expand Up @@ -118,6 +128,57 @@ function App({ mcpResource }) {
}
```

## Metadata Usage Examples

When a resource is created on the server with metadata, the `UIResourceRenderer` automatically applies the configuration:

```tsx
// Server-side resource creation (for reference)
// const serverResource = createUIResource({
// uri: 'ui://chart/dashboard',
// content: { type: 'externalUrl', iframeUrl: 'https://charts.example.com' },
// encoding: 'text',
// uiMetadata: {
// 'preferred-frame-size': [ '800px', '600px' ],
// 'initial-render-data': { theme: 'dark', userId: '123' }
// }
// });

// Client-side rendering - metadata is automatically applied
function Dashboard({ mcpResource }) {
const handleUIAction = async (result: UIActionResult) => {
// Handle UI actions
return { status: 'handled' };
};

return (
<UIResourceRenderer
resource={mcpResource.resource} // Contains metadata from server
htmlProps={{
// Additional render data can be merged with metadata
iframeRenderData: {
sessionId: 'abc123',
permissions: ['read', 'write']
}
}}
onUIAction={handleUIAction}
/>
);
}

// The UIResourceRenderer will:
// 1. Use ['800px', '600px'] as the initial iframe size
// 2. Merge server metadata with prop data:
// { theme: 'dark', userId: '123', sessionId: 'abc123', permissions: ['read', 'write'] }
// 3. Pass the combined data to the iframe via ui-lifecycle-iframe-render-data
```

### Metadata Precedence

When both server metadata and component props provide similar data:
- **Frame sizing**: Server `preferred-frame-size` is used as the initial size, but can be overridden by component styling
- **Render data**: Server `initial-render-data` is merged with the `iframeRenderData` prop, with prop values taking precedence for duplicate keys

## Utility Functions

### `isUIResource()`
Expand Down
19 changes: 18 additions & 1 deletion docs/src/guide/server/typescript/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,30 @@ For a complete example, see the [`typescript-server-demo`](https://github.com/id
## Key Exports

- **`createUIResource(options: CreateUIResourceOptions): UIResource`**:
The primary function for creating UI snippets. It takes an options object to define the URI, content (direct HTML or external URL), and encoding method (text or blob).
The primary function for creating UI snippets. It takes an options object to define the URI, content (direct HTML or external URL), encoding method (text or blob), and metadata configuration.

## Purpose

- **Ease of Use**: Simplifies the creation of valid `UIResource` objects.
- **Validation**: Includes basic validation (e.g., URI prefixes matching content type).
- **Encoding**: Handles Base64 encoding when `encoding: 'blob'` is specified.
- **Metadata Support**: Provides flexible metadata configuration for enhanced client-side rendering and resource management.

## Metadata Features

The `createUIResource()` function supports three types of metadata configuration to enhance resource functionality:

### `metadata`
Standard MCP resource metadata that becomes the `_meta` property on the resource. This follows the MCP specification for resource metadata.

### `uiMetadata`
MCP-UI specific configuration options. These keys are automatically prefixed with `mcpui.dev/ui-` in the resource metadata:

- **`preferred-frame-size`**: Define the resource's preferred initial frame size (e.g., `{ width: 800, height: 600 }`)
- **`initial-render-data`**: Provide data that should be passed to the iframe when rendering

### `resourceProps`
Additional properties that are spread directly onto the resource definition, allowing you to add any MCP specification-supported properties like `annotations`.

## Building

Expand Down
112 changes: 110 additions & 2 deletions docs/src/guide/server/typescript/usage-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,120 @@ console.log('Resource 5:', JSON.stringify(resource5, null, 2));
// of a toolResult in an MCP interaction.
```

## Metadata Configuration Examples

The `createUIResource` function supports three types of metadata configuration to enhance your UI resources:

```typescript
import { createUIResource } from '@mcp-ui/server';

// Example 7: Using standard metadata
const resourceWithMetadata = createUIResource({
uri: 'ui://analytics/dashboard',
content: { type: 'rawHtml', htmlString: '<div id="dashboard">Loading...</div>' },
encoding: 'text',
metadata: {
title: 'Analytics Dashboard',
description: 'Real-time analytics and metrics',
created: '2024-01-15T10:00:00Z',
author: 'Analytics Team',
preferredRenderContext: 'sidebar'
}
});
console.log('Resource with metadata:', JSON.stringify(resourceWithMetadata, null, 2));
/* Output includes:
{
"type": "resource",
"resource": {
"uri": "ui://analytics/dashboard",
"mimeType": "text/html",
"text": "<div id=\"dashboard\">Loading...</div>",
"_meta": {
"title": "Analytics Dashboard",
"description": "Real-time analytics and metrics",
"created": "2024-01-15T10:00:00Z",
"author": "Analytics Team",
"preferredRenderContext": "sidebar"
}
}
}
*/

// Example 8: Using uiMetadata for client-side configuration
const resourceWithUIMetadata = createUIResource({
uri: 'ui://chart/interactive',
content: { type: 'externalUrl', iframeUrl: 'https://charts.example.com/widget' },
encoding: 'text',
uiMetadata: {
'preferred-frame-size': ['800px', '600px'],
'initial-render-data': {
theme: 'dark',
chartType: 'bar',
dataSet: 'quarterly-sales'
},
}
});
console.log('Resource with UI metadata:', JSON.stringify(resourceWithUIMetadata, null, 2));
/* Output includes:
{
"type": "resource",
"resource": {
"uri": "ui://chart/interactive",
"mimeType": "text/uri-list",
"text": "https://charts.example.com/widget",
"_meta": {
"mcpui.dev/ui-preferred-frame-size": ["800px", "600px"],
"mcpui.dev/ui-initial-render-data": {
"theme": "dark",
"chartType": "bar",
"dataSet": "quarterly-sales"
},
}
}
}
*/

// Example 9: Using resourceProps for additional MCP properties
const resourceWithProps = createUIResource({
uri: 'ui://form/user-profile',
content: { type: 'rawHtml', htmlString: '<form id="profile">...</form>' },
encoding: 'text',
resourceProps: {
annotations: {
audience: ['developers', 'admins'],
priority: 'high'
}
}
});
console.log('Resource with additional props:', JSON.stringify(resourceWithProps, null, 2));
/* Output includes:
{
"type": "resource",
"resource": {
"uri": "ui://form/user-profile",
"mimeType": "text/html",
"text": "<form id=\"profile\">...</form>",
"annotations": {
"audience": ["developers", "admins"],
"priority": "high"
}
}
}
*/
```

### Metadata Best Practices

- **Use `metadata` for standard MCP resource information** like titles, descriptions, timestamps, and authorship
- **Use `uiMetadata` for client rendering hints** like preferred sizes, initial data, and context preferences
- **Use `resourceProps` for MCP specification properties** like annotations, descriptions at the resource level, and other standard fields

## Advanced URI List Example

You can provide multiple URLs in the `text/uri-list` format for fallback purposes. However, **MCP-UI requires a single URL** and will only use the first valid URL found:

```typescript
// Example 6: Multiple URLs with fallbacks (MCP-UI uses only the first)
// Example 10: Multiple URLs with fallbacks (MCP-UI uses only the first)
const multiUrlContent = `# Primary dashboard
https://dashboard.example.com/main

Expand All @@ -154,7 +262,7 @@ https://backup.dashboard.example.com/main
# Emergency fallback (will be logged but not used)
https://emergency.dashboard.example.com/main`;

const resource6 = createUIResource({
const resource = createUIResource({
uri: 'ui://dashboard-with-fallbacks/session-123',
content: { type: 'externalUrl', iframeUrl: multiUrlContent },
encoding: 'text',
Expand Down
Loading