`experimental_MCPAppRenderer`
experimental_MCPAppRenderer
experimental_MCPAppRenderer 는 AI SDK tool UI 파트에 대해 MCP App을 렌더링해요. tool 파트에서 MCP App 메타데이터를 감지하고, 앱 리소스를 로드하고, sandbox 프록시 iframe에서 앱을 렌더링하며, iframe과 호스트 애플리케이션 사이에 MCP Apps JSON-RPC 메시지를 브리징해요.
MCP App 메타데이터가 없는 tool 파트의 경우 컴포넌트는 fallback 을 렌더링해요.
출처: 문서
본문
Import
<Snippet
text={import { experimental_MCPAppRenderer as MCPAppRenderer } from "@ai-sdk/react"}
prompt={false}
/>
예제 (Example)
'use client';
import {
experimental_MCPAppRenderer as MCPAppRenderer,
type MCPAppBridgeHandlers,
type MCPAppMetadata,
type MCPAppResource,
type MCPAppSandboxConfig,
} from '@ai-sdk/react';
import { isToolUIPart } from 'ai';
const sandbox = {
url: '/mcp-app-sandbox',
className: 'h-80 w-full rounded-lg border',
style: { border: 0 },
} satisfies MCPAppSandboxConfig;
async function loadResource(app: MCPAppMetadata): Promise<MCPAppResource> {
const response = await fetch('/api/mcp-app-host/read-resource', {
method: 'POST',
body: JSON.stringify({ uri: app.resourceUri }),
});
if (!response.ok) {
throw new Error('Failed to load MCP App resource');
}
return response.json();
}
const handlers: MCPAppBridgeHandlers = {
callTool: params =>
fetch('/api/mcp-app-host/call-tool', {
method: 'POST',
body: JSON.stringify(params),
}).then(response => response.json()),
openLink: ({ url }) => {
window.open(url, '_blank', 'noopener,noreferrer');
return {};
},
};
export function MessagePart({ part }: { part: unknown }) {
if (!isToolUIPart(part)) {
return null;
}
return (
<MCPAppRenderer
part={part}
loadResource={loadResource}
handlers={handlers}
sandbox={sandbox}
fallback={null}
/>
);
}
Props
<PropertiesTable
content={[
{
name: 'part',
type: 'ToolUIPartpart.toolMetadata.mcp.app.',
},
{
name: 'sandbox',
type: 'MCPAppSandboxConfig',
description:
'Configuration for the outer sandbox proxy iframe used to host the app.',
},
{
name: 'resource',
type: 'MCPAppResource',
isOptional: true,
description:
'A preloaded MCP App resource. Provide either resource or loadResource.',
},
{
name: 'loadResource',
type: '(app: MCPAppMetadata) => Promisetools/call, resources/read, ui/open-link, and display mode changes.',
},
{
name: 'hostInfo',
type: '{ name: string; version: string }',
isOptional: true,
description:
'Host identity returned to the app during the ui/initialize handshake.',
},
{
name: 'hostContext',
type: 'MCPAppHostContext',
isOptional: true,
description:
'Host context sent to the app, such as theme, display mode, and available display modes.',
},
{
name: 'fallback',
type: 'ReactNode',
isOptional: true,
description:
'Rendered while the resource is loading, when loading fails, or when the tool part is not an MCP App.',
},
]}
/>
Sandbox 구성 (Sandbox Config)
<PropertiesTable
content={[
{
name: 'url',
type: 'string | URL',
description:
'The URL of the sandbox proxy iframe. The proxy receives app HTML from the host and creates the inner app iframe.',
},
{
name: 'title',
type: 'string',
isOptional: true,
description: 'Accessible iframe title.',
},
{
name: 'className',
type: 'string',
isOptional: true,
description: 'Class name applied to the outer iframe.',
},
{
name: 'style',
type: 'CSSProperties',
isOptional: true,
description: 'Inline styles applied to the outer iframe.',
},
{
name: 'targetOrigin',
type: 'string',
isOptional: true,
description:
'Target origin used for postMessage. Defaults to *; set this to your sandbox origin in production.',
},
{
name: 'outerSandbox',
type: 'string',
isOptional: true,
description:
'Sandbox attribute for the outer proxy iframe. Defaults to allow-scripts allow-same-origin allow-forms.',
},
{
name: 'innerSandbox',
type: 'string',
isOptional: true,
description:
'Sandbox attribute sent to the proxy for the inner app iframe. Defaults to allow-scripts allow-forms.',
},
]}
/>
브리지 핸들러 (Bridge Handlers)
experimental_MCPAppRenderer 는 이 핸들러를 사용해 iframe 요청에 응답해요. 프로덕션에서 서버 기반 핸들러는 MCP 서버를 호출하기 전에 인가와 MCP Apps tool 가시성을 검증해야 해요.
<PropertiesTable
content={[
{
name: 'allowedTools',
type: 'string[]',
isOptional: true,
description:
'Optional client-side allowlist checked before forwarding tools/call requests.',
},
{
name: 'callTool',
type: '(params: MCPAppToolCallParams) => Promisetools/call requests.',
},
{
name: 'readResource',
type: '(params: { uri: string }) => Promiseresources/read requests.',
},
{
name: 'listResources',
type: '(params?: unknown) => Promiseresources/list requests.',
},
{
name: 'openLink',
type: '(params: { url: string }) => Promiseui/open-link requests.',
},
{
name: 'sendMessage',
type: '(params: unknown) => Promiseui/message requests.',
},
{
name: 'updateModelContext',
type: '(params: unknown) => Promiseui/update-model-context requests.',
},
{
name: 'requestDisplayMode',
type: "(params: { mode: 'inline' | 'fullscreen' | 'pip' }) => Promise<{ mode: MCPAppDisplayMode }> | { mode: MCPAppDisplayMode }",
isOptional: true,
description: 'Handles app-initiated ui/request-display-mode requests.',
},
{
name: 'onSizeChange',
type: '(params: { width?: number; height?: number }) => void',
isOptional: true,
description: 'Called when the app sends a size change notification.',
},
{
name: 'onInitialized',
type: '() => void',
isOptional: true,
description: 'Called after the app sends ui/notifications/initialized.',
},
{
name: 'onRequestTeardown',
type: '(params: unknown) => void',
isOptional: true,
description: 'Called when the app requests teardown.',
},
{
name: 'onLog',
type: '(params: unknown) => void',
isOptional: true,
description: 'Called when the app sends a log notification.',
},
{
name: 'onError',
type: '(error: Error) => void',
isOptional: true,
description:
'Called when a supported iframe request fails while being handled.',
},
]}
/>
함께 보기 (See Also)
<ExampleLinks examples={[ { title: 'MCP Apps guide', link: '/docs/ai-sdk-core/mcp-apps', }, { title: 'MCP Apps helpers', link: '/docs/reference/ai-sdk-core/mcp-apps', }, ]} />