Claude Skill

Build an MCP App (Interactive UI Widgets)

This skill should be used when the user wants to build an "MCP app", add "interactive UI" or "widgets" to an MCP server, "render components in chat", build "MCP UI resources", make a tool that shows a "form", "picker", "dashboard" or "confirmation dialog" inline in the conversation, or mentions "apps SDK" in the context of MCP. Use AFTER the build-mcp-server skill has settled the deployment model, or when the user already knows they want UI widgets.

Editor's Note

This skill should be used when the user wants to build an "MCP app", add "interactive UI" or "widgets" to an MCP server, "render components in chat", build "MCP UI resources", make a tool that shows a "form", "picker", "dashboard" or "confirmation dialog"... Covers claude host specifics, when a widget beats plain text, widgets vs elicitation — route correctly.

Page Outline

Claude host specificsWhen a widget beats plain textWidgets vs Elicitation — route correctlyArchitecture: two deployment shapes

Source Content

Normalized top-level metadata comes from the directory layer. The body below is the upstream source content for this item.

Build an MCP App (Interactive UI Widgets)

An MCP app is a standard MCP server that **also serves UI resources** — interactive components rendered inline in the chat surface. Build once, runs in Claude *and* ChatGPT and any other host that implements the apps surface.

The UI layer is **additive**. Under the hood it's still tools, resources, and the same wire protocol. If you haven't built a plain MCP server before, the `build-mcp-server` skill covers the base layer. This skill adds widgets on top.

> **Testing in Claude:** Add the server as a custom connector in claude.ai (via a Cloudflare tunnel for local dev) — this exercises the real iframe sandbox and `hostContext`. See https://claude.com/docs/connectors/building/testing.

Claude host specifics

  • `_meta.ui.prefersBorder: false` on a `ui://` resource removes the outer card border (mobile).
  • `hostContext.safeAreaInsets: {top, right, bottom, left}` (px) — honor these for notches and the composer overlay.
  • `_meta.ui.csp.{connectDomains, resourceDomains, baseUriDomains}` — declare external origins per resource; default is block-all. `frameDomains` is currently restricted in Claude.
  • Directory submission for MCP Apps requires 3–5 PNG screenshots, ≥1000px wide, cropped to the app response only (no prompt in the image). See https://claude.com/docs/connectors/building/submission#asset-specifications.

---

When a widget beats plain text

Don't add UI for its own sake — most tools are fine returning text or JSON. Add a widget when one of these is true:

| Signal | Widget type | |---|---| | Tool needs structured input Claude can't reliably infer | Form | | User must pick from a list Claude can't rank (files, contacts, records) | Picker / table | | Destructive or billable action needs explicit confirmation | Confirm dialog | | Output is spatial or visual (charts, maps, diffs, previews) | Display widget | | Long-running job the user wants to watch | Progress / live status |

If none apply, skip the widget. Text is faster to build and faster for the user.

---

Widgets vs Elicitation — route correctly

Before building a widget, check if **elicitation** covers it. Elicitation is spec-native, zero UI code, works in any compliant host.

| Need | Elicitation | Widget | |---|---|---| | Confirm yes/no | ✅ | overkill | | Pick from short enum | ✅ | overkill | | Fill a flat form (name, email, date) | ✅ | overkill | | Pick from a large/searchable list | ❌ (no scroll/search) | ✅ | | Visual preview before choosing | ❌ | ✅ | | Chart / map / diff view | ❌ | ✅ | | Live-updating progress | ❌ | ✅ |

If elicitation covers it, use it. See `../build-mcp-server/references/elicitation.md`.

---

Architecture: two deployment shapes

Remote MCP app (most common)

Hosted streamable-HTTP server. Widget templates are served as **resources**; tool results reference them. The host fetches the resource, renders it in an iframe sandbox, and brokers messages between the widget and Claude.

┌──────────┐  tools/call   ┌────────────┐
│  Claude  │─────────────> │ MCP server │
│   host   │<── result ────│  (remote)  │
│          │  + widget ref │            │
│          │               │            │
│          │ resources/read│            │
│          │─────────────> │  widget    │
│ ┌──────┐ │<── template ──│  HTML/JS   │
│ │iframe│ │               └────────────┘
│ │widget│ │
│ └──────┘ │
└──────────┘

MCPB-packaged MCP app (local + UI)

Same widget mechanism, but the server runs local

<!-- truncated -->

Related Items

Deploy agents, MCP servers, and backends fast logo

Railway - Deploy agents and MCP servers fast

Try Railway