MCPNEW

McpAppFrame

MCP Apps (SEP-1865) host renderer: sandboxed iframe + JSON-RPC bridge.

Available inreactangularvue

Preview

Live previewreal component

Install

TerminalBash
npx mcp-elements add mcp-app-frame

Usage

mcp-app-frame-example.tsxTSX
import { McpAppFrame } from '@mcp-elements/react'
import { APP_RESOURCE_MIME_TYPE } from '@mcp-elements/core'

// 1. Your MCP client reads the tool's declared ui:// resource
const { contents } = await client.readResource({ uri: toolMeta.ui.resourceUri })
const resource = contents[0] // mimeType === APP_RESOURCE_MIME_TYPE

// 2. Render it — the frame answers ui/initialize, injects the spec CSP,
//    proxies tools/call back to your client, and streams tool input/results
export function Example({ toolArgs, result }) {
  return (
    <McpAppFrame
      html={resource.text}
      resourceMeta={resource._meta?.ui}
      hostContext={{ theme: 'dark' }}
      callTool={(name, args) => client.callTool({ name, arguments: args })}
      toolInput={toolArgs}
      toolResult={result}
      openLink={(url) => window.open(url, '_blank', 'noopener')}
    />
  )
}

Props

PropTypeDefaultDescription
htmlstringRaw HTML of the ui:// resource (from resources/read). Rendered via srcdoc with the spec CSP injected
srcstringURL to load instead of raw HTML (dedicated-origin or dev setups)
resourceMetaUiResourceMetaThe resource's _meta.ui — drives CSP injection, iframe permissions, and border preference
callTool(name, args) => Promise<CallToolResult>Proxies tools/call requests from the app to your MCP client
readResource(uri: string) => Promise<unknown>Proxies resources/read requests from the app to your MCP client
openLink(url: string) => void | Promise<void>Handles ui/open-link requests from the app
toolInputRecord<string, unknown>Complete tool arguments; delivered as ui/notifications/tool-input after the handshake
toolResultCallToolResultTool result; delivered as ui/notifications/tool-result after the handshake
hostContextHostContextUI context shared during ui/initialize (theme, containerDimensions, locale...)
onInitialized(appCapabilities?) => voidCalled when the ui/initialize handshake completes
autoResizebooleantrueGrow the iframe to match ui/notifications/size-changed from the app
heightnumber480Initial/fallback height in pixels
sandboxstring'allow-scripts allow-forms'iframe sandbox flags (keep allow-same-origin out for srcdoc content)
classNamestringAdditional CSS classes