generative-ui — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited generative-ui (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generative UI is the pattern in which a language model emits — as structured output constrained by a typed schema — a specification of a UI component or sub-tree that an application then renders for the user. The load-bearing distinction is that the model's output is neither a chat response, nor a tool call asking for execution, nor raw markup it freely authors: it is a typed instance of a component-vocabulary schema, plus the data to populate it. Three contracts must hold simultaneously for the pattern to work — the component schema (the typed vocabulary the model and application share), the generation constraint (the mechanism that forces the model's emission to be a valid schema instance — provider-native structured output or strict tool calling, not best-effort function calls), and the render pipeline (the application code that turns a validated spec into pixels). Rendering happens through one of two legitimate substrates that differ in where safety comes from: a typed-component-tree where safety is by restriction (the model can only name components in the published vocabulary; no HTML executes), or a sandboxed-iframe where safety is by isolation plus host mediation (a developer-authored HTML resource runs in a sandbox that cannot reach the parent DOM, fed the model's data over a postMessage/JSON-RPC bridge). The invariant across both is that the model never authors raw HTML injected into the host DOM — it authors a typed selection and data. The application owns rendering, the interaction layer, and visual design; the model owns the choice of which components to compose and with what data. A bidirectional interaction loop closes the cycle: a user action on a rendered component becomes a typed event the application encodes as the next turn the model reasons about, with destructive effects gated as separate, user-identity-authorized tool calls rather than auto-executed from a click.
The pattern where a language model emits or selects, as structured output constrained by a typed schema, a specification of a UI component or sub-tree that an application then renders for the user. Covers the three load-bearing contracts (component schema, generation constraint, render pipeline), the implementation variants (direct UI-spec generation, tool-result-to-component binding, hosted-widget bridge, RSC/component-streaming transport), component-palette design rules (semantic discriminated-union types, bounded props, versioning/migration, fallback representation, provider schema-compatibility testing), the two render substrates (typed-component-tree vs sandboxed-iframe HTML), streaming discipline (partial specs drive provisional UI only; final render waits for completed validation), the bidirectional interaction loop (postMessage/JSON-RPC) that closes the cycle from user action back into the model's next turn — including stable per-instance ids for multi-component disambiguation and per-control state-update rules, the UI lifecycle and human-in-the-loop states (idle/thinking/streaming/finalization; propose-edit, gated action, verification loop), the hosted-widget data-channel split (structuredContent/content visible to the model vs _meta component-only) and per-widget domain/CSP policy, the trust boundary that makes the rendering safe under adversarial input, the taxonomy by palette openness / generation mechanism / render substrate / interactivity / trust posture, the 2025–2026 protocol standardization (MCP Apps, OpenAI Apps SDK, MCP-UI, A2UI, AG-UI), and the distinction from adjacent patterns (chat with markdown, tool-call protocol mechanics, prebuilt-widget routing, RSC streaming / AG-UI as transport, AI-assisted design/codegen, model-emits-code which is something else).
The conventional model-to-user pipeline is: model emits text; user reads text. Generative UI inverts this: model emits a typed component specification; application renders the specification; user sees a UI surface authored by the application's design system but composed by the model.
This indirection is what makes the pattern simultaneously safe and expressive. Safe, because the application owns the rendering — the model can only ask the application to render components/resources the application has chosen to expose, with data the schema permits, and where HTML executes at all it executes inside a sandbox the model cannot escape. Expressive, because the model can compose components, choose presentations per response, and adapt to the question at hand without the developer pre-building every variant.
The discipline is in the three contracts. The schema must be small enough to be a coherent design vocabulary but expressive enough to cover the assistant's responses. The generation constraint must be reliable enough that the model's emission is always renderable. The render pipeline must enforce validation, design-system compliance, and security policies — by restriction (typed component tree) or by isolation (sandboxed iframe) — independently of what the model intended. When all three hold, generative UI lets a language model author the choice of what to show without ever injecting raw markup into the rendering surface.
The practical discipline is to keep three decisions separate. The model may decide what representation is appropriate for this turn. The application decides which component vocabulary exists and what each component means. The renderer decides whether a concrete emitted instance is safe, valid, accessible, and renderable. Collapsing those roles is where the pattern fails: if the model gets to invent components, it has escaped the palette; if schema validity is treated as authorization, unsafe URLs and overlong content still reach the UI; if a click is treated as execution permission, an injected or mistaken UI can trigger real side effects.
| Contract | Owner | Defines | Failure mode if absent |
|---|---|---|---|
| Component schema | Application developer | The typed vocabulary the model may emit and the application will render | Model emits unrenderable specs; design inconsistency |
| Generation constraint | Model + API | The mechanism that forces the model's output to be a valid schema instance | Specs fail validation; retry loops; production flake |
| Render pipeline | Application | The code that turns a validated spec into pixels with design-system and security policies enforced | Model output reaches the DOM unchecked; security and design break |
Generative UI ships in several shapes. They share the renderer discipline but differ in what the model emits and what binds it to a component — keep them distinct so a tool-result rendering is not mislabeled open-ended generation.
| Variant | What the model emits | What the app renders | Use when | Main risk |
|---|---|---|---|---|
| Direct UI-spec generation | A discriminated-union instance such as { "type": "chart", "props": … }, A2UI JSON messages, or another declarative UI spec over a known catalog | A component registry maps type/component to application components | The answer shape is open-ended and the model should choose among several presentations | Schema too broad; recursive trees or unsafe props become hard to govern |
| Tool-result component binding | A tool call plus a typed tool result; the UI maps tool name/result to a component | A fixed component per tool or result type | The UI is a rich rendering of external data fetched by a tool | Mislabeling this as open-ended generative UI; the model chose the tool, not the component tree |
| Hosted-widget bridge | A tool descriptor declares an output schema plus a UI resource; result structuredContent/content feeds model and component, private _meta hydrates only the component | A sandboxed/iframe widget or host-provided component | Building inside a host such as ChatGPT Apps / MCP Apps | Assuming host sandboxing removes the need for schema, CSP, origin, and action policy |
| RSC/component-streaming transport | A tool/function call, structured selection, or tool result the framework maps into RSC payloads or UIMessage parts | Framework-managed React components | Low-latency rich chat UI in React/Next.js | Treating transport as the concept; experimental RSC APIs may not be production defaults |
The first variant is the cleanest expression of this skill. The others are common industry implementations that use the same renderer discipline, but they should not blur the boundary: tool-call-flow owns the call/result protocol, rendering-models owns the delivery model, and this skill owns the model-to-renderer UI contract.
| Mechanism | How it works | Production stance | Watchouts |
|---|---|---|---|
| Provider-native structured output | The API constrains the final answer to a JSON Schema or schema-like response format (logits masked at decode so only schema-valid tokens emit) | Strong default for direct UI-spec generation when the provider supports the needed schema subset | Every provider supports only a subset; keep the palette shallow, explicit, and compatibility-tested |
| Strict function/tool calling | The model emits a tool/function call whose arguments are schema-constrained, often through grammar-constrained sampling (strict: true) | Strong default when a UI component is represented as a tool, or when a tool result drives the component | Non-strict, best-effort tool calling is not a reliable render contract — require strict/schema-enforced mode where available |
| Framework strategy layer | A framework picks provider-native structured output when possible and falls back to a tool-calling strategy (e.g. LangChain ProviderStrategy/ToolStrategy) | Useful portability layer across providers | The framework does not remove the renderer contract; validation, fallback, and safety policy remain application work |
| Streaming structured output | Partial JSON / UIMessage / tool-argument deltas arrive before the final object completes | Useful for skeletons, progress, and perceived latency | Partial output is not final authority; render provisional UI only and commit after the completed object/spec validates |
| Free-form JSON / JSON mode + post-validation | The model is asked to emit JSON and the app validates/retries | Prototype fallback only | Valid JSON is not schema adherence; retries add latency and production variance |
Production-grade generative UI uses provider-native structured output (OpenAI Structured Outputs, Gemini response schema, Anthropic strict tool use) or strict function/tool calling for the generation-constraint contract. "Function calling" by itself is too broad: a best-effort function call that can omit required fields or invent argument shapes is not a reliable render contract.
The component palette is the model-facing API of the UI system. Design it like a public contract, not like an internal React prop bag. A few rules separate a palette that survives production from one that produces constant validation failures and breaking changes:
type/kind discriminator with a per-variant prop shape is what lets both the generation constraint and the render pipeline's component-lookup step be exhaustive and type-checked. An open object with optional everything pushes validation to runtime and invites ambiguous specs.comparison_table, confirmation_card, date_range_picker, line_chart — not blue_card or two_column_div. The model reasons about meaning, not layout.minLength/maxLength and numeric bounds, allowed URL origins, allowed MIME types, finite nesting depth, and explicit nullability; prefer a shallow tree to deep recursion. Provider structured-output engines impose real limits — OpenAI Structured Outputs and Gemini response schemas cap total schema size, nesting depth, and property counts, and silently degrade or reject schemas that exceed them — so an unbounded palette can become un-emittable on a given model. Deeply nested schemas are also harder to generate, stream progressively, and migrate.plain_text summary, aria_label, table_data, or a generic card). "Unknown spec" must be a designed state, not a thrown exception reaching the user — and the fallback is also the accessibility/degradation path when the rich renderer is unavailable.$ref/anyOf support, format keywords). Add a test that submits the palette schema to every provider you ship on and asserts it is accepted and round-trips, so a schema edit one provider rejects is caught before release, not in production.A validated spec must be turned into pixels by some substrate. There are two legitimate ones, distinguished by where the safety comes from. Conflating them — or believing only one is "real" generative UI — is the most common 2026-era confusion.
| Property | Typed-component-tree | Sandboxed-iframe |
|---|---|---|
| What renders | The app's own registered components, selected by the spec's type discriminator | A developer-authored HTML/JS UI resource loaded into a sandboxed <iframe> |
| Safety comes from | Restriction — the model can only name components in the published vocabulary; no HTML is executed | Isolation + host mediation — the iframe sandbox blocks access to parent DOM, cookies, storage; HTML/JS runs but cannot escape, and the host enforces CSP/domain policy |
| Who authors the markup | Nobody emits markup; the app maps spec → component | The developer authors the HTML resource once; the model never writes it |
| What the model authors | A typed selection + data | A tool call that selects a UI resource + the data pushed into it |
| Interaction transport | App event-routing layer appends the action as a user-role turn (a tool_result or app-defined structured block — no standard provider ui_event content type exists) | postMessage / JSON-RPC bridge (ui/-prefixed methods) between iframe and host |
| Canonical implementations | A2UI (declarative JSON over a trusted client-held catalog), bespoke React/Vue component registries, Vercel AI SDK RSC streamUI (now paused — see vendor table) | MCP Apps (official MCP extension, 2026-01-26), OpenAI Apps SDK, MCP-UI |
| Best for | Tight design-system fidelity; small, well-known palettes; no third-party code | Rich/interactive apps (maps, dashboards, 3D, media viewers); third-party servers the host does not fully trust |
The shared invariant across both: the model never authors raw HTML injected into the host DOM. In the typed-component substrate it emits a typed spec; in the iframe substrate it emits a tool call that selects a pre-authored resource and supplies data. Unsandboxed model-emits-HTML-into-the-host-page is neither substrate — it is the dangerous anti-pattern both exist to avoid.
A2UI deserves first-class treatment in the typed-component-tree substrate. It is an open-source/open-standard public-preview example where the agent sends declarative JSON messages, the client renders native components from a trusted catalog, and data binding uses JSON-Pointer paths into application state. That makes A2UI a strong example of safety-by-vocabulary-restriction: expressive like UI, but still data interpreted by trusted renderer code rather than model-authored HTML/JSX/CSS/JS. Label its maturity clearly — A2UI is useful current evidence, but its public-preview versions and catalogs can still evolve.
AG-UI is not a third render substrate. It is an agent-to-frontend event transport that can carry message events, tool-lifecycle events (start → progress → finish), state snapshots/deltas, interrupts, and generative-UI specs such as A2UI — but it does not itself render. MCP discovery is likewise not a generation constraint. Keep generation constraint, render substrate, and event transport named separately.
A schema-validated spec is the input to the render pipeline, not permission to render. For the typed-component substrate the pipeline performs:
type discriminator to the application's registered component implementation. A spec asking for a component the application doesn't ship is a routing error, not permission to render arbitrary content.img-src/connect-src/frame policy, URL/link sanitization, length caps, MIME checks, recursion-depth limits, external-resource controls. The schema describes structure; the safety policy describes acceptable content.For the sandboxed-iframe substrate the pipeline is different in shape but identical in intent: fetch the declared ui:// resource (HTML+JS+CSS), render it in a sandboxed <iframe> whose permissions and content-security-policy the host controls (e.g. _meta.ui.csp, _meta.ui.permissions in MCP Apps), push the model's tool-result data into the iframe over the postMessage bridge, and proxy any tool calls the iframe requests back through the host's authorization. In both substrates the model's "intent" does not authorize anything; the pipeline (restriction) or the sandbox + host mediation (isolation) does.
If a provider or agent framework exposes reasoning, thinking, traces, or activity events, do not pipe raw chain-of-thought or private reasoning tokens into the generated UI as content. The UI may show sanitized progress labels, tool/activity traces, or collapsible status events when the product needs latency feedback, but the render contract remains the completed, validated UI spec or tool result.
A tool result that drives a hosted widget is not one undifferentiated blob — it has three audiences, and conflating them leaks data or starves the component. In the OpenAI Apps SDK / MCP Apps shape:
| Channel | Visible to | Use for |
|---|---|---|
structuredContent | Model and the component | The data the model should reason about in its next turn and the component needs to render (e.g. the list of search results behind a results card) |
content | Model and the component | The natural-language/text portion the model reads as the tool's textual result |
_meta | The component only — never enters the model's context | Render-only payload the model neither needs nor should pay tokens for, and data you do not want influencing the model (full result sets, pagination cursors, presentation hints) |
Two rules fall out of the split: (1) put in _meta anything the component needs but the model should not see or be steered by — it is the lever that keeps a large render payload out of the context window and keeps untrusted bulk data from reaching the model; (2) anything the model must reason about in the next turn has to live in structuredContent/content, because _meta is invisible to it. Hidden-from-the-model data can still be visible to the component and browser environment — protect it according to the component's threat model. The host also enforces a per-widget domain/CSP policy (declared resource origins, allowed connect-src/frame-src): a widget may only load and call out to origins the host has allowlisted, so a compromised or malicious resource cannot exfiltrate to an arbitrary endpoint. UI-to-host messages travel through a declared bridge (JSON-RPC over postMessage); every callable method needs schema validation, origin/trust labeling, logging, and user-consent policy where appropriate. The data-channel split and the domain policy together are why the iframe substrate can host third-party, not-fully-trusted UI without handing it the model's context or the open network.
A spec usually arrives incrementally — partial JSON, partial tool-call arguments, or streamed UI deltas (Anthropic fine-grained tool streaming, the AI SDK experimental_output / partial-object stream, Gemini partial structured responses). Partial output is a latency affordance, not a render authorization. The discipline:
The same rule holds in the iframe substrate: stream the model's data into the iframe as it arrives if the resource is built for it, but the host gates committed tool calls on completed, validated payloads, not on partial deltas.
For interactive generative UI, every interactive component needs five definitions:
| Definition | Example for a Confirm button | Why it matters |
|---|---|---|
| Instance identity | { component_id: "confirm-17" } | A response that renders several of the same component needs each event to identify which rendered instance the user acted on |
| Action shape | { kind: "confirm", component_id: "confirm-17", accepted: true } | The model receives a typed event in the next turn, not an ambiguous natural-language click |
| History encoding | A user-role turn carrying the action — e.g. a tool_result block keyed to the originating tool-use, or an application-defined structured content block. There is no standard provider ui_event content type; an { type: 'ui_event', … } shape is an application convention, not a wire format. The MCP Apps dialect instead carries it as a ui/-prefixed JSON-RPC message over postMessage | The model reads it the same way it reads any other turn |
| Side-effect policy | Confirming requires an additional tool call gated by user identity, never auto-executed from the click | The click is a user signal, not authorization for the destructive operation |
| State-update rule | The component declares whether it is optimistic, disabled-while-pending, or waits for model/tool confirmation | The UI remains coherent while the next model/tool turn runs |
Instance identity. A response that renders several interactive components (three confirm cards, a list of editable rows) needs each emitted event to carry a stable per-instance id (component_id) so the model can correlate the event to the right rendered instance and the data that produced it. Without it, multi-component responses produce ambiguous events the model cannot route.
A generative UI with interactive components but no interaction-loop definition is one-way: the user clicks and nothing happens. A generative UI that auto-executes destructive actions on click is a prompt-injection-defense failure waiting to happen. The middle path — click produces a structured event the model reasons about, the model emits a tool call that goes through normal authorization, idempotency, and audit controls — is the discipline.
Host-mediated capability delegation. In the protocol-standardized world (MCP Apps, OpenAI Apps SDK), the embedded UI does not act on the world directly. It requests an outcome ("schedule this meeting", "send this email") and the host routes it through capabilities the user has already connected, subject to user consent. This is the same discipline at the protocol layer: the rendered surface emits a request, the trusted host applies authorization, and side effects happen only through gated, user-consented paths — never because a button was clicked inside a model-influenced surface.
A generative-UI surface is not a single render — it moves through a lifecycle, and with reasoning models the latency between "user asked" and "UI is final" is long enough that each phase needs its own designed state. Event-transport protocols (AG-UI's run/message/tool-lifecycle/state events) exist precisely to carry these phase transitions to the frontend. The phases:
| Phase | What the surface shows | Contract rule |
|---|---|---|
| Idle / input | The affordance to ask, empty states, suggested starters, or the last settled result | Do not pre-render a component that implies model certainty before the model commits to a representation |
| Thinking / progress | A non-final working indicator — reasoning/tool-call progress, status text, partial plan | Show sanitized progress labels or tool/activity events; do not expose raw chain-of-thought or private reasoning tokens as user-facing UI content |
| Streaming / provisional | Skeletons and optimistic shells keyed off the discriminator (see Streaming Discipline) — visibly non-final, interaction not yet wired | Treat deltas as provisional; no final commit or side-effect affordance until completed validation passes |
| Revision / finalization | The validated, interactive render; earlier provisional UI is replaced, not appended | Encode user edits/approvals as typed events; state-changing work still goes through authorized tool/server actions |
On top of the phases sit three human-in-the-loop (HITL) patterns, each a different answer to "who commits?":
The unifying rule across phases and HITL patterns: a non-final state must be visibly non-final and must not be wired to side effects. Provisional UI that looks committed, or a proposed action that fires before the user approves, is the lifecycle-level form of the same failure the interaction loop guards against at the event level.
Current upstream state does not make this skill obsolete. It changes which implementations are the best examples:
ui:// resources, tool linkage, sandboxed iframes, JSON-RPC/postMessage), not just a vendor-local trick.streamUI remains a useful example but Vercel's own docs call RSC experimental and recommend AI SDK UI for production.The durable rule: prefer the strongest provider-native or strict-tool mechanism available, but keep the portable renderer contract independent of any one vendor — the pattern predates and outlives any single implementation.
| Implementation | Provided by | Substrate / mechanism | Status (2026) |
|---|---|---|---|
Structured Outputs (text.format / response_format) | OpenAI | Provider-native schema-constrained final output (grammar-constrained decoding against JSON Schema) | Current — strong for direct UI-spec generation; prefer over JSON mode |
Responses-API function tools with strict: true | OpenAI | Strict JSON-Schema function-call arguments | Current — strong for component-as-tool or tool-result binding |
OpenAI Apps SDK (window.openai, apps-sdk-ui) | OpenAI | Sandboxed-iframe components over MCP; host bridge for toolInput/toolOutput/setWidgetState/callTool/requestDisplayMode; inline-card/carousel/fullscreen display modes; transcript-visible data vs component-only _meta split | Current — still requires CSP, origin, and action policy; prefer the ui/* MCP-Apps bridge methods over legacy window.openai-only calls where both exist |
MCP Apps (@modelcontextprotocol/ext-apps) | Model Context Protocol | Sandboxed-iframe HTML resource (ui://) + postMessage/JSON-RPC bridge; CSP/domain policy | First official MCP extension, ratified 2026-01-26; launch blog names ChatGPT, Claude, Goose, and VS Code as clients (MCP-UI adopters such as Postman/Shopify are a broader, separate set — do not conflate) |
MCP-UI (@mcp-ui/client) | MCP-UI community / Shopify et al. | Three render modes: raw HTML, external URL, remote-DOM | Current — the rendering library behind many MCP Apps hosts; remote-DOM avoids iframe overhead but needs client-side component-library coordination |
| Anthropic strict tool use | Anthropic | Grammar-constrained schema adherence for tool inputs | Current — strong for schema-valid component/tool arguments |
| Anthropic fine-grained tool streaming | Anthropic | Streams tool-input deltas before buffering/validation | Current — latency feature; handle invalid/partial JSON and commit only after validation |
| Gemini response schema | Response constrained to a JSON Schema subset | Current — strong for direct UI-spec generation; still validate semantic/business correctness in app code | |
| A2UI | Google / A2UI project | Declarative JSON messages over a client-held component catalog, JSON-Pointer data binding, native renderer mapping | Current public-preview / open standard — first-class typed-component-tree (restriction) substrate; label maturity clearly |
| AG-UI protocol + A2UI | CopilotKit (adopted by Google, LangChain, AWS, Microsoft, Mastra, PydanticAI) | Event-based agent↔frontend transport (message/tool-lifecycle/state events); carries generative-UI specs including A2UI | Current — transport, distinct from the rendering substrate; visualize traces/tool events, not raw chain-of-thought |
| AI SDK UI generative user interfaces | Vercel | Tool calls/results rendered as typed React UI parts (UIMessage/tool parts) | Current — Vercel's production-oriented recommendation |
Vercel AI SDK RSC (streamUI) | Vercel | RSC streaming of components selected by function calling | Paused / experimental — not for production. Known failure modes: stream-completion flicker/remount, multiple-suspense-boundary crashes, createStreamableUI quadratic data transfer; migrate to AI SDK UI (useChat); the older render API is removed |
| LangChain structured + frontend structured output | LangChain | ProviderStrategy vs ToolStrategy; validated structured response mapped to UI with progressive rendering and fallbacks | Current — portability layer; renderer still owns validation, fallback, and safety |
| Generic JSON mode / OpenRouter-compatible JSON | Multiple | Free-form JSON with post-validation, falling back to retry | Current — prototype-grade; not a production render contract by itself |
Citing one vendor's API as the canonical pattern is a category error; the pattern predates and outlives any single implementation. The 2025–2026 center of gravity shifted toward MCP-based sandboxed-iframe apps (MCP Apps, OpenAI Apps SDK), open typed-component standards (A2UI), and event-transport protocols (AG-UI). Use whichever substrate + mechanism the chosen host and model support most reliably.
After applying this skill, verify:
ui:// resources — not an informal convention.structuredContent/content; render-only or bulk/untrusted data the model should not see (or pay tokens for) is in _meta (component-only).tool_result/app-defined block, or a ui/ postMessage; not a standard provider ui_event content type, which does not exist), a side-effect policy, and a state-update rule. Auto-executing destructive actions on rendered-component clicks is forbidden; capability delegation routes through the host with user consent.| Instead of this skill | Use | Why |
|---|---|---|
| Designing the page-level rendering model (CSR vs SSR vs RSC vs streaming) | rendering-models | rendering-models owns the page-rendering taxonomy; this skill operates inside any of those models |
| Designing the protocol cycle of a tool call | tool-call-flow | tool-call-flow owns the model↔runtime cycle; this skill owns the model→render-pipeline pattern (even when, as in MCP Apps, the UI resource is declared inside a tool) |
| Defending against attacker-controlled directives in model input | prompt-injection-defense | prompt-injection-defense owns the security property; this skill owns the rendering pattern that must preserve it |
| Designing the component library itself (tokens, primitives, composition) | design-system-architecture | design-system-architecture owns the library; this skill consumes it as the palette |
| Designing the JSON shape of an HTTP API endpoint | api-design | api-design owns the request/response surface; this skill owns the model-to-renderer schema |
| Evaluating model accuracy on a specific task | eval-driven-development | eval-driven-development owns measurement; this skill owns the pattern being evaluated |
ui:// resource, the host renders it in a sandboxed iframe, and the iframe communicates over a postMessage JSON-RPC dialect (ui/ methods). Canonical reference for the sandboxed-iframe substrate, its isolation-based security model, and CSP/domain policy.window.openai (toolInput/toolOutput/setWidgetState/callTool/requestDisplayMode); the structuredContent/content/_meta data-channel split. A production instance of the sandboxed-iframe substrate.@mcp-ui/client) behind many MCP Apps hosts; supports raw HTML, external URL, and remote-DOM render modes. See also Shopify Engineering: MCP UI — breaking the text wall.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.