Skip to content

MCP Apps

MCP Apps is the official MCP extension for interactive UI. Moka implements the host side of the 2026-01-26 specification.

A tool declares a UI resource in its metadata:

{
"name": "roll_dice",
"description": "Roll dice. Shows an interactive dice roller.",
"inputSchema": { "type": "object", "properties": { "sides": { "type": "number" }, "count": { "type": "number" } } },
"_meta": { "ui": { "resourceUri": "ui://moka-demo/dice" } }
}

The server serves that resource with the MCP Apps mime type:

{
"uri": "ui://moka-demo/dice",
"mimeType": "text/html;profile=mcp-app",
"text": "<!doctype html><html>…</html>",
"_meta": { "ui": { "prefersBorder": true, "csp": { "connectDomains": ["https://api.example.com"] } } }
}
  1. When the model (or you, in the Tool runner) calls the tool, Moka reads the ui:// resource via resources/read.
  2. It renders the HTML in a sandboxed iframe below the tool call, with a Content-Security-Policy built from _meta.ui.csp.
  3. The app sends ui/initialize; Moka replies with host info, capabilities and host context: theme, CSS variables, locale, time zone and container size.
  4. After ui/notifications/initialized, Moka sends ui/notifications/tool-input (the arguments) and ui/notifications/tool-result (the full MCP result, including structuredContent).
  5. The app can now call back into the host.
Method (app → host)What Moka does
ui/initializeReturns protocol version, hostInfo, hostCapabilities, hostContext
ui/notifications/initializedSends tool input and result
tools/callCalls a tool on the same MCP server and returns the result
resources/readReads a resource from the same server
ui/messagePosts a user message into the chat (shown as From app: …) and runs the agent
ui/update-model-contextAttaches the content to the model’s next turn
ui/open-linkOpens the URL in a new tab
ui/request-display-modeSwitches between inline and fullscreen
ui/notifications/size-changedResizes the iframe (60–1200 px)
pingResponds {}
Notification (host → app)When
ui/notifications/tool-inputAfter initialization
ui/notifications/tool-resultAfter initialization, or as soon as the tool finishes
ui/notifications/host-context-changedWhen the user switches light/dark theme

Tools that declare _meta.ui.visibility: ["app"] are hidden from the model but callable by their app.

hostContext.styles.variables uses the standard names, so apps can match Moka automatically:

Variable
--color-background-primary / --color-background-secondarySurfaces
--color-text-primary / --color-text-secondaryText
--color-border-primaryBorders
--color-accent / --color-accent-foregroundBrand accent
--font-sans, --border-radius-mdTypography and shape
:root {
--bg: var(--color-background-primary, #fff);
--fg: var(--color-text-primary, #111);
}
  • The iframe is sandboxed with allow-scripts allow-forms allow-popups and without allow-same-origin, so the app runs in an opaque origin: it cannot read Moka’s token, cookies or storage, or call Moka’s API directly.
  • A CSP is injected: no network access unless the resource declares connectDomains; scripts, styles, images and fonts only inline or from declared resourceDomains.
  • Apps can only call tools on their own server.
  • Every message in both directions is logged to the inspector as ui.rpc.