/docsfor

Usage with MCP Apps

How the assistant-ui MCP Apps renderer mounts each widget in a Safe Content Frame.

MCP Apps lets a Model Context Protocol server ship an HTML widget alongside a tool, which the chat host renders inline when the tool is called. assistant-ui's McpAppRenderer mounts that widget in an iframe built with SafeContentFrame and talks to it over a JSON-RPC postMessage bridge. This page covers the parts of that renderer that come from Safe Content Frame; the MCP Apps guide covers setup, the backend route, and the full bridge protocol.

Each widget on its own domain

The renderer serves each widget from its own hashed cross-origin domain, so the widget cannot reach the host page through same-origin references. The hash uses a random salt per render, or a salt derived from the content when enableBrowserCaching is set. See How it works for how the domain is chosen.

Message checks on the bridge

The bridge accepts a message only when both checks pass:

  • event.source === frame.iframe.contentWindow, so the message came from this widget's window.
  • event.origin === frame.origin, the domain SafeContentFrame issued for this render.

Messages from any other window or origin are dropped silently.

Sandbox options

The iframe always carries the allow-same-origin allow-scripts sandbox flags, and the sandbox array adds to them. Set the frame options through the sandbox field on McpAppRendererOptions:

McpAppRenderer({
  // ...
  sandbox: {
    sandbox: ["allow-forms", "allow-popups"],
    enableBrowserCaching: true,
    className: "my-mcp-app",
  },
});

The frame settings in sandbox (className and style aside) are read once when the frame mounts. Changing them takes effect when the app's resource URI changes and the frame is rebuilt.

Next

  • MCP Apps guide: install, client and route setup, AI SDK and AG-UI integration, and the bridge protocol.
  • API reference: every SafeContentFrame option.