Render MCP tool results as interactive inline apps in ChatKit.
_meta.ui.resourceUri, and ChatKit renders that resource as an inline iframe in the conversation. The iframe communicates with the Xpert backend through the standard MCP Apps JSON-RPC bridge.
Use MCP Apps when a model-visible tool needs to produce a rich, interactive result such as a chart, dashboard, map, form, media browser, or drilldown analysis. For static structured cards, keep using ChatKit Widgets. For persistent workbench pages or integration configuration screens, keep using Xpert extension views.
What Xpert Supports
The Xpert MCP Apps host supports:- tool metadata discovery through
_meta.ui.resourceUri - inline
McpAppChatKit message components ui://MCP resources with MIME typetext/html;profile=mcp-app- JSON-RPC bridge methods including
ui/initialize,ui/notifications/tool-input,ui/notifications/tool-result,tools/call,resources/read,ui/open-link,ui/message,ui/update-model-context,ui/request-display-mode, andui/notifications/size-changed - app-only tools through
_meta.ui.visibility = ['app'] - resource CSP, browser permissions,
domain, andprefersBorderfrom MCP App resource metadata - short-lived app instances with history refresh recovery from safe message metadata
- plugin-managed MCP servers installed from Xpert plugins
window.openai APIs. MCP Apps can still be authored so they remain portable across hosts by relying on the standard bridge first.
Architecture
The chat history stores only safe metadata such asappInstanceId, resourceUri, toolName, toolsetId, and serverName, plus size-limited snapshots of the initial tool input/result. The raw HTML is not stored in the conversation. On page refresh, the backend can reconnect to the toolset and rebuild an expired app instance from the stored metadata.
History Replay And Initial Tool Results
An MCP App usually needs the triggering tool call’stool-input and tool-result during initialization. Xpert + ChatKit resolves that data in this order:
- Use the live app instance first: while the in-memory app instance is still valid, the resource response returns the complete initial
toolResultkept on the live instance. - Revive when the live instance is gone: after a history refresh, a closed Agent Toolset, or a backend restart, the MCP Apps Host reconnects to the Toolset, reads the
ui://resource again, and creates a fresh app instance. - Fall back to small snapshots in chat history: if the message contains a size-limited initial
toolResult, ChatKit replays it throughui/notifications/tool-result; if no replayable result is available, ChatKit sends onlyui/notifications/tool-inputand does not invent an empty result.
CallToolResult payloads whose serialized size is at most 128KB are persisted inline. Oversized results store only toolResultSize and toolResultTruncated: true. The backend threshold can be configured with:
Runtime Metadata Contract
ChatKit does not inspect arbitrary MCP server implementation details. It relies on the Xpert backend to preserve the MCP App metadata discovered from the selected Toolset. At runtime, an MCP tool becomes app-capable when Xpert sees_meta.ui.resourceUri and the resource URI uses the ui:// scheme. Tool _meta.ui should only carry resourceUri and visibility. Resource security and rendering metadata such as CSP, browser permissions, domain, and prefersBorder belongs on the MCP App resource _meta.ui. Xpert reads resources/read content item metadata first and falls back to resources/list metadata when the read result does not include it.
Visibility controls who can call the tool:
A common pattern is to expose one model-visible tool that opens the app, plus one or more app-only tools used by the iframe.
Xpert filters non-model-visible tools out of the LLM tool list. The MCP Apps host also rejects iframe calls to tools that are not app-visible or are disabled in the Xpert Toolset.
For plugin-side metadata and tool registration examples, see MCP Tools and MCP Apps.
Resource Requirements
The app resource must return HTML with the MCP App profile MIME typetext/html;profile=mcp-app. Registering the resource is the MCP server’s responsibility; validating and sandboxing it is the host’s responsibility.
The resource can also declare display metadata in _meta.ui: title, description, and icon. title and description may be strings or Xpert-style I18nObject values. icon uses the shared IconDefinition shape. ChatKit stores only this safe descriptor in the message history, localizes the text with the current ChatKit language, and renders the icon/title/description in the MCP App message header.
Security defaults are intentionally strict:
- the initial App HTML must come from a
ui://resource - raw HTML is fetched at render time and is not persisted in chat history
- CSP defaults to deny-by-default, with explicit domains from resource
_meta.ui.csp - camera, microphone, geolocation, and clipboard-write are denied unless resource
_meta.ui.permissionsexplicitly requests them - iframe
resources/readcalls are limited to the same MCP server and reject browser/script schemes such ashttp://,https://,javascript://,data://, andblob:// - resource
domaindoes not create a dedicated origin in v1; it is treated as unsupported host metadata - iframe tool calls always go through the Xpert backend and its tenant, organization, workspace, toolset, and tool-enabled checks
Theme Variables
Before ChatKit writes an MCP App HTML document into the iframe, it injects host theme variables into the app<head>. Variables use the generic --mcp-app-* prefix so other MCP Apps hosts can reuse the same contract:
--mcp-app-* variables instead of depending on ChatKit internal classes or private tokens. The current host provides:
Recommended app styling:
--mcp-app-color-chart-* values are host-provided chart color hints, not a guarantee that they fit every business chart. If the host theme uses neutral or muted chart tokens, the MCP App can define its own semantic data palette such as --sales-chart-revenue, --sales-chart-margin, or --risk-chart-high, while still using --mcp-app-* for background, text, borders, fonts, and radius.
ui/initialize still returns hostContext.theme as the light / dark string. The same variable map is also returned as hostContext.themeCssVariables so apps can initialize chart themes or canvas colors.
ChatKit also passes the current UI language through hostContext.locale, hostContext.language, and hostContext.direction. Before the iframe document runs, ChatKit sets the app HTML lang and dir attributes to the same values. MCP Apps should use these fields to localize labels, number/date formatting, chart titles, and validation messages inside the app resource.
Bridge Methods
Inside the iframe, use JSON-RPC messages throughpostMessage. The app should initialize itself before requesting host capabilities. The initialize request includes the app identity, app capabilities, and protocol version:
McpUiInitializeResult shape:
CallToolResult payload:
Authoring and Packaging
The recommended way to ship an MCP App in Xpert is as a plugin-managed MCP server. The plugin owns the MCP server entrypoint, tool metadata,ui:// resource, app-only tools, and install policy. ChatKit only hosts the resulting app instance.
For the plugin-side implementation flow, manifest schema, package layout, and local testing checklist, see MCP Tools and MCP Apps.
Relationship to Other Xpert UI Surfaces
MCP Apps are one of several UI extension points in Xpert:
Do not put arbitrary HTML into the widget renderer. Do not use middleware as the resource host. MCP Apps should use the MCP resource and bridge flow, while extension views should keep using Xpert view manifests and platform data/action providers.
Enablement and Operations
In production, enable MCP Apps explicitly:appInstanceToken for each app instance and ChatKit includes it on resource and RPC requests. In production, revive, tools/call, and resources/read requests are rejected if the signed token is missing, expired, or does not match the tenant, workspace, Toolset, server, tool, and resource URI.
For local development, non-production environments enable MCP Apps by default and tolerate legacy messages without appInstanceToken. For plugin build, install, controlled stdio runtime, and runtime-copy checks, see MCP Tools and MCP Apps.