← Field log

A2UI vs MCP Apps: two standards for agent UI, and why you'll need both

There are now two ways for an AI agent to show you a UI. They solve the same problem completely differently.

MCP Apps (Anthropic/MCP) — the server ships HTML. The host renders it in a sandboxed iframe. Full power: React, D3, WebGL, anything. The server controls the pixels. A2UI (Google) — the server ships declarative JSON describing components. The client renders them natively using its own design system. No iframes. The client controls the pixels.

Both are open standards. Both are shipping in production. They make fundamentally different tradeoffs.

MCP Apps: full control

// Server ships actual HTML
server.registerResource("ui://myapp/chart", {
  mimeType: "text/html;profile=mcp-app",
  content: `<html>
    <canvas id="chart"></canvas>
    <script>
      const app = new App();
      app.ontoolresult = (data) => drawChart(data);
      document.getElementById("reset").onclick = () =>
        app.callServerTool("reset_data", {});
    </script>
    <button id="reset">Reset</button>
  </html>`,
});

The iframe is sandboxed — can't access cookies, can't touch the parent page. But inside the sandbox, anything goes. A Figma viewer. A 3D model. A code editor. A map with real-time markers.

Actions work through app.callServerTool() — the iframe calls back to the MCP server via the host. Button click → JSON-RPC → server executes → result pipes back into the iframe.

Strengths: unlimited expressiveness, server owns the rendering, works for complex visualizations. Weaknesses: every widget looks different (no shared design system), heavy for simple cases (shipping a full HTML page to show a number), requires webview on mobile.

A2UI: declarative components

[
  {
    "id": "deal-card",
    "component": "Card",
    "children": ["title", "value", "stage-btn"]
  },
  {
    "id": "title",
    "component": "Text",
    "content": { "literalString": "Acme Corp" }
  },
  {
    "id": "value",
    "component": "Text",
    "content": { "literalString": "$240,000" }
  },
  {
    "id": "stage-btn",
    "component": "Button",
    "child": "btn-label",
    "action": { "event": { "name": "advance_stage" } }
  },
  {
    "id": "btn-label",
    "component": "Text",
    "content": { "literalString": "Advance Stage" }
  }
]

The client app maintains a catalog of trusted components — Card, Button, Text, Table, Chart. The agent can only use components from that catalog. The client renders them natively: SwiftUI on iOS, Flutter on Android, React on web.

Actions are events. A button press fires { "event": { "name": "advance_stage" } } to the host, which routes it wherever it needs to go.

Strengths: consistent look and feel (client's design system), lightweight (JSON not HTML), native rendering (no webview), LLM-friendly (flat list, easy to generate incrementally), secure by construction (can't render arbitrary HTML). Weaknesses: limited to the component catalog, can't do custom visualizations, client must implement every component type.

When to use which

This isn't a competition. They're for different things.

Use casePick
Dashboard with chartsMCP Apps
Simple status card with actionsA2UI
3D model viewerMCP Apps
Form with input fieldsA2UI
Code editor / diff viewerMCP Apps
Table with sortingEither — A2UI is lighter, MCP Apps is more flexible
Mobile-first agent UIA2UI (native rendering, no webview)
Data-heavy interactive toolMCP Apps

The pattern: if the component catalog covers it, use A2UI. If you need full control, use MCP Apps. Most agent UIs will be simple enough for A2UI — a card showing a number, a table with rows, a button that triggers an action. The complex cases (Figma viewer, notebook, map) need MCP Apps.

The gap nobody is filling

Right now you pick one. If you build an MCP App, it works in Claude and ChatGPT but looks foreign on a native mobile app. If you build A2UI, it looks great natively but won't render in clients that only support MCP Apps.

The missing piece is an abstraction that compiles to both. Define your widget once using high-level primitives — Card, Metric, Table, Button — and the build step outputs:

  • MCP App HTML bundle for Claude/ChatGPT/VS Code
  • A2UI JSON for native clients
  • Native SwiftUI for iOS
  • React component for web

Same widget definition, every surface. The simple cases use declarative components (A2UI path). The complex cases break out of the abstraction into full HTML (MCP Apps path).

Nobody ships this today. We're building it.


MCP Apps spec · A2UI spec · A2UI repo