MC

mcp-apps-development

Build interactive UIs for conversational AI clients using the MCP Apps SDK.

Install

mkdir -p .claude/skills/mcp-apps-development && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10704" && unzip -o skill.zip -d .claude/skills/mcp-apps-development && rm skill.zip

Installs to .claude/skills/mcp-apps-development

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Build MCP Apps (ext-apps) that render interactive UI inside conversational AI clients. Use when creating visual tool outputs, interactive dashboards, form-based tools, or rich media experiences in MCP-compatible hosts like Claude Desktop, VS Code Copilot Chat, or other MCP clients that support the Apps specification.
318 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Build interactive UI
  • Render visual tool outputs
  • Create dashboards
  • Render rich media
  • Embed iframes

How it works

It uses the ext-apps SDK to render interactive UI components inside MCP-compatible clients.

Inputs & outputs

You give it
UI component request
You get back
Interactive MCP App

When to use mcp-apps-development

  • Create visual tool outputs
  • Build interactive dashboards
  • Render rich media in chat

About this skill

MCP Apps Development

Build MCP Apps that render interactive UI inside conversational AI clients using the @modelcontextprotocol/ext-apps SDK.

When to Use

  • Building MCP tools that display interactive UI (charts, forms, dashboards)
  • Creating visual resource viewers inside AI chat interfaces
  • Adding rich media output to existing MCP servers (video, maps, 3D, music)
  • Migrating OpenAI chat app plugins to the MCP Apps standard
  • Building fullscreen interactive experiences within MCP-compatible hosts

When NOT to Use

  • Building headless MCP servers (tools, resources, prompts only) -> use mcp-server-development skill
  • Creating standalone web applications outside AI clients
  • Building API integrations without a visual component

Decision Tree

Need interactive UI in an AI chat client?
+- Tool with visual output?
|  +- Register with registerAppTool()
|  +- Return _meta.ui.resourceUri linking to a resource
|  - Resource renders the UI via App class
+- Standalone visual resource?
|  +- Register with registerAppResource()
|  - Resource renders UI, host embeds as iframe
+- Migrating from OpenAI plugin?
|  +- See Migration section below
|  - Key: synchronous globals -> async App handlers
- No UI needed?
   - Use mcp-server-development skill instead

Architecture Overview

  ----------     PostMessage      ----------      MCP       ----------
  |  View  | <================> |   Host   | <==========> |  Server  |
  | (App)  |     (iframe)       |(AppBridge|   (Client)   | (MCP SDK)|
  | iframe |                    |  proxy)  |              |          |
  ----------                    ----------               ----------
       |                             |                        |
   App class               AppBridge class           registerAppTool()
   PostMessageTransport    proxies MCP requests      registerAppResource()
   React hooks             embeds iframe             _meta.ui.resourceUri

Three Abstractions:

LayerClassEntry PointRole
ViewAppext-appsRuns inside iframe, sends/receives messages
HostAppBridgeext-apps/app-bridgeEmbeds iframe, proxies MCP calls to client
Serverhelpersext-apps/serverregisterAppTool() / registerAppResource()

Quick Start: React MCP App

1. Scaffold from template

# Clone the ext-apps repo for templates
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/templates/basic-server-react
npm install

2. Server-side: Register tool + resource

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAppTool, registerAppResource } from "@anthropic-ai/sdk/mcp/ext-apps/server";

const server = new McpServer({ name: "my-app-server", version: "1.0.0" });

// Register a resource that renders UI
registerAppResource(server, {
  name: "dashboard",
  uri: "app://dashboard",
  title: "Dashboard",
  handler: async () => ({
    // Return bundled HTML/JS as a single-file resource
    blob: readFileSync("dist/index.html"),
    mimeType: "text/html",
  }),
});

// Register a tool that links to the resource
registerAppTool(server, {
  name: "show-dashboard",
  description: "Display the interactive dashboard",
  parameters: { query: { type: "string" } },
  handler: async ({ query }) => ({
    content: [{ type: "text", text: `Dashboard for: ${query}` }],
    _meta: {
      ui: { resourceUri: "app://dashboard" },
    },
  }),
});

3. Client-side: React App

import { useApp, useHostStyles, useAutoResize } from "@anthropic-ai/sdk/mcp/ext-apps/react";

function Dashboard() {
  const app = useApp();
  useHostStyles();    // Inherit host CSS variables
  useAutoResize();    // Auto-resize iframe to content

  const [data, setData] = useState(null);

  useEffect(() => {
    // Read resources or call tools via the app instance
    app.readResource("data://metrics").then(setData);
  }, [app]);

  return (
    <div style={{ fontFamily: "var(--host-font-family)" }}>
      <h1>Dashboard</h1>
      {data && <Chart data={data} />}
    </div>
  );
}

4. Build as single-file bundle

// vite.config.ts
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";

export default defineConfig({
  plugins: [viteSingleFile()],
  build: { outDir: "dist" },
});

Core Rules

1. Tool + Resource Pattern

Every visual tool MUST follow the tool-resource linking pattern:

  • Tool: Handles logic, returns _meta.ui.resourceUri pointing to a resource
  • Resource: Serves the UI (HTML bundle) that renders in the iframe
  • Link: The _meta.ui.resourceUri in the tool response tells the host which resource to display
Tool call -> returns _meta.ui.resourceUri -> Host loads resource -> renders in iframe

2. Handler Registration Order

Register ALL handlers (tools, resources, event listeners) BEFORE calling app.connect() or server.connect():

// CORRECT: register first, connect last
app.onToolCall("search", handler);
app.onResourceRead("data://items", handler);
await app.connect(transport);

// WRONG: connecting before registering handlers
await app.connect(transport);        // handlers will be missed
app.onToolCall("search", handler);   // too late

3. Tool Visibility Model

MCP Apps have two visibility scopes for tools:

ScopeVisible ToUse For
Model-visibleAI model + appTools the AI calls (search, analyze)
App-onlyApp iframe onlyUI utility tools (sort, filter, paginate)

Register app-only tools with visibility metadata to prevent the AI from calling UI-internal tools.

4. Host Styling

MUST use CSS custom properties from the host for visual consistency:

body {
  font-family: var(--host-font-family, system-ui, sans-serif);
  color: var(--host-color, #1a1a1a);
  background: var(--host-background, #ffffff);
}

Handle safe area insets for various host layouts:

.content {
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

5. Single-File Bundling

MCP App resources MUST be served as self-contained single-file HTML bundles. Use vite-plugin-singlefile to inline all CSS, JS, and assets:

npm install -D vite-plugin-singlefile

The host loads the resource as an iframe srcdoc -- external script/style references will not resolve.

6. Streaming Partial Input

For tools called with streaming, handle partial input via ontoolinputpartial:

app.onToolCall("search", {
  handler: async ({ query }) => ({ results: search(query) }),
  ontoolinputpartial: (partial) => {
    // Update UI as partial tool input streams in
    updateSearchPreview(partial.query);
  },
});

7. Visibility-Based Resource Management

Use IntersectionObserver to detect when the app iframe scrolls out of view and pause expensive operations:

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      resumeAnimation();
    } else {
      pauseAnimation();
    }
  });
});

8. Fullscreen Mode

Apps can request fullscreen rendering from the host:

app.requestFullscreen();   // Expand to full host viewport
app.exitFullscreen();      // Return to inline iframe

Use sparingly -- only when content genuinely needs the full viewport (3D scenes, maps, editors).

Framework Templates

FrameworkTemplateUse When
Reactbasic-server-reactComponent-heavy UIs, state management
Vuebasic-server-vueTwo-way bindings, Vue ecosystem
Sveltebasic-server-svelteMinimal bundle size, reactive
Preactbasic-server-preactReact API with smaller footprint
Solidbasic-server-solidFine-grained reactivity, performance
Vanilla JSbasic-server-vanillajsNo framework overhead, simple UIs

All templates include Vite build config, single-file bundling, and a basic server with registerAppTool / registerAppResource wired up.

Migration from OpenAI Plugins

Key conceptual changes:

OpenAI PluginMCP App
window.chatgpt.* (synchronous globals)App class (async handlers)
Plugin manifest + API specregisterAppTool() + registerAppResource()
Separate frontend/backendSingle server + bundled UI resource
CORS configuration requiredPostMessage transport (no CORS)

Migration checklist:

  1. Investigate CSP requirements (Content Security Policy)
  2. Replace synchronous global calls with async App handlers
  3. Bundle UI as single-file HTML (no external script/link tags)
  4. Register tools with registerAppTool() and resources with registerAppResource()
  5. Replace CORS config with PostMessage-based transport
  6. Test with basic-host from the ext-apps repo

Anti-Patterns

  • External script tags: Embedding <script src="..."> in app HTML -- use single-file bundling instead
  • Direct DOM globals: Using window.parent or postMessage directly -- use the App class transport
  • Late handler registration: Calling connect() before registering handlers -- register first, connect last
  • Ignoring host styles: Hardcoding colors/fonts instead of using var(--host-*) CSS properties
  • Always-on fullscreen: Requesting fullscreen on load -- only use when content needs it
  • Unbounded rendering: Running animations/timers when iframe is not visible -- use IntersectionObserver
  • Manual version pinning: Editing package.json versions by hand -- use npm install <package> instead
  • God-tools: One tool handling all UI interactions -- separate into model-visible and app-only tools

Testing

Test MCP Apps using the `basic-hos


Content truncated.

When not to use it

  • Headless MCP servers
  • Standalone web apps

Prerequisites

@modelcontextprotocol/ext-apps

Limitations

  • Requires MCP client support
  • Requires single-file bundling

How it compares

It enables visual, interactive experiences within chat interfaces, unlike standard headless MCP tools.

Compared to similar skills

mcp-apps-development side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
mcp-apps-development (this skill)05moReviewAdvanced
zustand1132moNo flagsIntermediate
accessibility-compliance452moNo flagsIntermediate
react-modernization212moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jnPiyush

View all by jnPiyush

ux-ui-design

jnPiyush

Design user experiences with wireframing, prototyping, user flows, accessibility, and production-ready HTML prototypes. Use when creating wireframes, building interactive prototypes, designing user flows, implementing accessibility standards, or producing HTML/CSS design deliverables.

00

copilot-studio-agents

jnPiyush

Design Microsoft Copilot Studio agents (formerly Power Virtual Agents) -- topics, trigger phrases, generative answers, knowledge sources, connector and MCP actions, authentication, channels, and agent flows -- so an agent can author the conversational logic that ships as a Bot component inside a Pow

00

verification-before-completion

jnPiyush

Block false completion claims. Force the agent to identify the claim, run the exact verification command, read the actual output, compare against the claim, and only then report. Use whenever an agent is about to say "done", "fixed", "tests pass", "deployed", "loop complete", or close an issue.

00

configuration

jnPiyush

Implement configuration management patterns including environment variables, secrets, feature flags, and validation strategies. Use when setting up app configuration, managing environment-specific settings, implementing feature flags, storing secrets securely, or validating configuration at startup.

00

docx

jnPiyush

Read, write, and transform Microsoft Word .docx files. Use when extracting text or tables from Word documents, generating reports from templates, applying styles, inserting images, building tables, or converting Markdown/HTML to Word.

00

error-handling

jnPiyush

Implement robust error handling with exceptions, retry logic, circuit breakers, and graceful degradation. Use when designing error handling strategies, implementing retry policies, adding circuit breakers, configuring timeouts, or building health check endpoints.

00

You might also like

zustand

lobehub

Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.

113434

accessibility-compliance

wshobson

Implement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.

45132

react-modernization

wshobson

Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.

21134

react

lobehub

React component development guide. Use when working with React components (.tsx files), creating UI, using @lobehub/ui components, implementing routing, or building frontend features. Triggers on React component creation, modification, layout implementation, or navigation tasks.

3480

frontend-testing

langgenius

Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities. Triggers on testing, spec files, coverage, Vitest, RTL, unit tests, integration tests, or write/review test requests.

1152

feature-flags

facebook

Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React.

642

Search skills

Search the agent skills registry