Introduction
Welcome to the MCP Apps SDK documentation!
The @mcp-ui/* packages provide tools for building MCP Apps - interactive UI components for Model Context Protocol (MCP) tools. This SDK implements the MCP Apps standard, enabling rich HTML interfaces within AI applications.
You can use GitMCP to give your IDE access to mcp-ui's latest documentation!
Background
MCP-UI pioneered the concept of interactive UI over the Model Context Protocol. Before MCP Apps existed as a standard, this project demonstrated how MCP tools could return rich, interactive HTML interfaces instead of plain text, enabling UI components within AI applications.
The patterns and ideas explored in MCP-UI directly influenced the development of the MCP Apps specification, which standardized UI delivery over MCP. Today, the @mcp-ui/* packages implement this standard while maintaining the project's original vision: making it simple to build beautiful, interactive experiences for AI tools.
What are MCP Apps?
MCP Apps is a standard for attaching interactive UIs to MCP tools. When a tool has an associated UI, hosts can render it alongside the tool's results, enabling rich user experiences like forms, charts, and interactive widgets.
The Core Pattern
The MCP Apps pattern uses three key concepts:
- Tool with
_meta.ui.resourceUri- Links a tool to its UI resource - Resource Handler - Serves the UI content when the host requests it
- AppRenderer - Client component that fetches and renders the UI
// 1. Create UI content
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
content: { type: 'rawHtml', htmlString: '<h1>Widget</h1>' },
encoding: 'text',
});
// 2. Register resource handler
registerAppResource(server, 'widget_ui', widgetUI.resource.uri, {}, async () => ({
contents: [widgetUI.resource]
}));
// 3. Register tool with _meta linking
registerAppTool(server, 'show_widget', {
description: 'Show interactive widget',
inputSchema: { query: z.string() },
_meta: { ui: { resourceUri: widgetUI.resource.uri } } // This links tool → UI
}, async ({ query }) => {
return { content: [{ type: 'text', text: `Result: ${query}` }] };
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
When a host calls show_widget, it sees the _meta.ui.resourceUri and fetches the UI from that resource URI to render alongside the tool result.
SDK Packages
The @mcp-ui/* packages provide everything needed to build and render MCP Apps:
Server SDK (@mcp-ui/server)
createUIResource: Creates UI resource objects with HTML content or fetched external URLs- Works with
registerAppToolandregisterAppResourcefrom@modelcontextprotocol/ext-apps/server
Client SDK (@mcp-ui/client)
AppRenderer: High-level component for rendering tool UIs (fetches resources, handles lifecycle)AppFrame: Lower-level component for when you have pre-fetched HTML
Additional Language SDKs
mcp_ui_server(Ruby): Helper methods for creating UI resourcesmcp-ui-server(Python): Helper methods for creating UI resources
How It Works
┌─────────────────────────────────────────────────────────────────┐
│ MCP Host │
│ 1. Calls tool │
│ 2. Sees _meta.ui.resourceUri in tool definition │
│ 3. Fetches resource via resources/read │
│ 4. Renders UI in sandboxed iframe (AppRenderer) │
└─────────────────────────────────────────────────────────────────┘
│ ▲
▼ │
┌─────────────────────────────────────────────────────────────────┐
│ MCP Server │
│ - registerAppTool with _meta.ui.resourceUri │
│ - registerAppResource to serve UI content │
│ - createUIResource to build UI payloads │
└─────────────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
Example Flow
Server (MCP Tool):
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
const dashboardUI = await createUIResource({
uri: 'ui://my-tool/dashboard',
content: { type: 'rawHtml', htmlString: '<h1>Dashboard</h1>' },
encoding: 'text'
});
registerAppResource(server, 'dashboard_ui', dashboardUI.resource.uri, {}, async () => ({
contents: [dashboardUI.resource]
}));
registerAppTool(server, 'show_dashboard', {
description: 'Show dashboard',
inputSchema: {},
_meta: { ui: { resourceUri: dashboardUI.resource.uri } }
}, async () => {
return { content: [{ type: 'text', text: 'Dashboard loaded' }] };
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'mcp_ui_server'
resource = McpUiServer.create_ui_resource(
uri: 'ui://my-tool/dashboard',
content: { type: :raw_html, htmlString: '<h1>Dashboard</h1>' },
encoding: :text
)
# Return in MCP response
{ content: [resource] }2
3
4
5
6
7
8
9
10
Client (Frontend App):
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: new URL('http://localhost:8765/sandbox_proxy.html') }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
}}
onMessage={async (params) => {
console.log('Message from UI:', params);
return { isError: false };
}}
/>
);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Key Benefits
- Standardized: Implements the MCP Apps specification for consistent behavior across hosts
- Secure: Sandboxed iframe execution prevents malicious code from affecting the host
- Interactive: Two-way communication between UI and host via JSON-RPC
- Flexible: Supports HTML content with the MCP Apps standard MIME type
Wire Format: UIResource
The underlying data format for UI content is the UIResource object:
interface UIResource {
type: 'resource';
resource: {
uri: string; // ui://component/id
mimeType: 'text/html;profile=mcp-app'; // MCP Apps standard
text?: string; // Inline HTML content
blob?: string; // Base64-encoded content
};
}2
3
4
5
6
7
8
9
The MIME type text/html;profile=mcp-app is the MCP Apps standard for UI resources.
Key Field Details:
uri: Unique identifier usingui://scheme (e.g.,ui://my-tool/widget-01)mimeType:text/html;profile=mcp-app— MCP Apps-compliant HTMLtextorblob: The actual content, either as plain text or Base64 encoded
Next Steps
- Getting Started - Set up your development environment
- Server Walkthroughs - Step-by-step guides
- Client SDK - Learn to render tool UIs with AppRenderer
- TypeScript Server SDK - Create tools with UI
- Ruby Server SDK - Ruby implementation
- Protocol Details - Understand the underlying protocol

