mcp-apps-interactive-ui

mcp-apps-interactive-ui

A Pizza Builder MCP server that demonstrates interactive in-chat UI widgets for selecting pizza options with live price updates and order submission.

Category
Visit Server

README

mcp-apps-interactive-ui

A runnable example of building interactive in-chat UI with MCP Apps, with docs on the parts that aren't obvious from the spec.

MCP Apps (the io.modelcontextprotocol/ui extension, SEP-1865) lets an MCP server hand the host a sandboxed HTML widget instead of plain text. The model calls one tool and the user gets a real interface: option pickers, live totals, buttons that talk back to the model. This repo is a minimal working example of that pattern.

The Pizza Builder example

Static screenshot, not a live embed. To run the real thing: build the repo and open dist/builder.html in a browser, or connect the server to a host (see Quickstart).

The example app is a Pizza Builder. Pick size, crust, and toppings, watch the price update live, and hit Place order to hand the choice back to the model. The domain is intentionally simple so the MCP Apps mechanics stay in focus.

Requirements

Node.js 20.11 or newer (the code uses import.meta.dirname, and @modelcontextprotocol/ext-apps requires Node 20+).

What it demonstrates

In a small, commented codebase (~700 lines of TypeScript):

Capability How Where
Render an interactive widget from a tool call _meta.ui.resourceUri links a tool to a ui:// resource src/server.ts, docs/02
Keep the model's context small launcher returns a {orderId} ref; the widget fetches the rest via an app-only tool docs/06
Let the user pick options without spending tokens visibility: ["app"] tools the host keeps out of the model's list src/server.ts
Hand a result back to the model updateModelContext (stage) then sendMessage (trigger) docs/05
Show external images _meta.ui.csp.resourceDomains docs/04
Download a file app.downloadFile src/widget/widget.ts
Go fullscreen app.requestDisplayMode (gated on availableDisplayModes) src/widget/widget.ts
Match the host's theme and fonts applyHostStyleVariables / applyHostFonts docs/03
See what the host actually granted you getHostCapabilities() and a capability probe docs/07

Plus 10 gotchas: URI caching, the silent updateModelContext, the caution banner, the session-locked tool catalog, and more.

Quickstart

git clone https://github.com/iamneilroberts/mcp-apps-interactive-ui
cd mcp-apps-interactive-ui
npm install
npm run build

See the widget immediately, no host required. The build produces a single self-contained dist/builder.html that falls back to mock data when opened directly:

open dist/builder.html      # macOS
xdg-open dist/builder.html  # Linux

Run it as a real MCP server:

npm start            # Streamable HTTP at http://127.0.0.1:3001/mcp
npm run start:stdio  # stdio, for Claude Desktop / MCP Inspector

Connect it to a host:

  • Claude Desktop: add to your MCP config, then ask Claude "build me a pizza":
    {
      "mcpServers": {
        "pizza": { "command": "node", "args": ["/abs/path/to/mcp-apps-interactive-ui/dist/index.js", "--stdio"] }
      }
    }
    
  • MCP Inspector: npx @modelcontextprotocol/inspector node dist/index.js --stdio

How it fits together

flowchart TB
    M["Model (Claude)"]
    H["Host (claude.ai / Desktop)"]
    W["Widget (sandboxed iframe)"]
    S[("Your MCP server: build_pizza, pizza_state, pizza_pick")]

    M <-->|"tool call / result + UI resource"| H
    H <-->|"postMessage / JSON-RPC bridge"| W
    H -->|"app.callServerTool() (proxied by host)"| S

The model launches the widget once. After that the widget talks to your server directly through the host (app.callServerTool), so picking options costs zero model tokens. The widget hands control back to the model only when the user is done. Full walkthrough in docs/01.

Repo layout

src/
  server.ts          the MCP server: 1 launcher tool + 2 app-only tools + 1 UI resource
  data.ts            the toy domain (menu, orders, pricing)
  index.ts           transport wiring (Streamable HTTP + stdio)
  widget/
    widget.ts        the App: render, pick, place-order, download, fullscreen, theme
    widget.html      the shell (CSS + JS get inlined here at build time)
    styles.css
esbuild.mjs          bundles the widget into one self-contained HTML the server serves
test/                unit tests for the pricing/selection logic (node --test via tsx)
docs/                01-08, the deep dives
media/               screenshots

Run the tests with npm test. CI (typecheck, build, test) runs on every push via GitHub Actions.

Docs

  1. Architecture & lifecycle: the handshake, the bridge, the two-part registration.
  2. Declaring UI resources: ui://, the MIME type, _meta.ui.resourceUri, tool visibility.
  3. The host API: every app.* method, and the capabilities-vs-context distinction.
  4. CSP & imagery: why your image is blocked and how to allow the domains you need.
  5. Two-way comms: updateModelContext vs sendMessage, the caution banner, no progress tokens.
  6. The token economy: the ref-and-fetch pattern that keeps a large payload out of the model's context.
  7. Probing host capabilities: how to find out what a host grants, with real Claude Desktop results.
  8. Gotchas: 10 things to know before shipping.

The capability tables in docs/07 come from running a probe inside a live host. Claude Desktop results are confirmed as of 2026-06-13; some claude.ai web cells are marked pending where they hadn't been captured in-host. Host behavior changes over time, so treat every host-specific claim as "as of that date" and re-probe before relying on a specific cell.

License

MIT.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured