2026-09-23·by Sijie Wang#standmeet#architecture#design

ui-cards

ui:// MCP-Apps cards

Parent: capabilities

Per-tool interactive cards in the chat, delivered as MCP-Apps ui:// resources, rendered in a sandboxed iframe talking to the host via postMessage. All per-tool cards are migrated — ask_visitor, corpus_search/list, summarize, the calendar tools, and even calendar_book (via the booker plugin's own ui:// card); the backend serves them through the tool's _meta.ui_resource.

The handshake

(The wire also carries mcp-ui:tool / mcp-ui:tool-result — a card calling back into a tool — and mcp-ui:state-set / mcp-ui:state-ack for card state; app/src/components/page/McpAppCard.tsx + its hook.)

Class view — the frontend dispatch

Precision: CardKind is only 'dump' | 'none' — the ui:// path is not a kind: when the tool result carries _meta.ui_resource, uiHtmlForToolMcpAppCard renders it regardless, and cardKindFor only decides the fallback for the rest.

The one remaining exception: the generic dump

NON_SANDBOX_CARDS (app/src/components/page/ToolCallCards.tsx) has shrunk to a single entry: the generic dump card used for skill_* / ext_* tool results (a debug-style text rendering). Everything else is a sandboxed ui:// card.

Proposed (🚧 — still unbuilt as of 2026-09-07: mcpplugin.Manifest has no renderer field)

Replace even that residue with a manifest-declared renderer field (renderer: ui | dump) — consistent with the Origin/manifest philosophy of mcp-capability-plugins: each capability declares its rendering, no hardcoded list at all.

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

ui-cards