Why not just build a web app?
You could build a standalone web app and send users a link. However, MCP Apps offer these key advantages that a separate page can’t match: Context preservation. The app lives inside the conversation. Users don’t switch tabs, lose their place, or wonder which chat thread had that dashboard. The UI is right there, alongside the discussion that led to it. Bidirectional data flow. Your app can call any tool on the MCP server, and the host can push fresh results to your app. A standalone web app would need its own API, authentication, and state management. MCP Apps get this via existing MCP patterns. Integration with the host’s capabilities. The app can delegate actions to the host, which can then invoke the capabilities and tools the user has already connected (subject to user consent). Instead of every app implementing and maintaining direct integrations (e.g., email providers), the app can request an outcome (like “schedule this meeting”), and the host routes it through the user’s existing connected capabilities. Security guarantees. MCP Apps run in a sandboxed iframe controlled by the host. They can’t access the parent page, steal cookies, or escape their container. This means hosts can safely render third-party apps without trusting the server author completely. If your use case doesn’t benefit from these properties, a regular web app might be simpler. But if you want tight integration with the LLM-based conversation, MCP Apps are a much better tool.How MCP Apps work
Traditional MCP tools return text, images, resources or structured data that the host displays as part of the conversation. MCP Apps extend this pattern by allowing tools to declare a reference to an interactive UI in their tool description that the host renders in place. The core pattern combines two MCP primitives: a tool that declares a UI resource in its description, plus a UI resource that renders data as an interactive HTML interface. When a large language model (LLM) decides to call a tool that supports MCP Apps, here’s what happens:-
UI preloading: The tool description includes a
_meta.ui.resourceUrifield pointing to aui://resource. The host can preload this resource before the tool is even called, enabling features like streaming tool inputs to the app. -
Resource fetch: The host fetches the UI resource from the server. This
resource contains an HTML page, often bundled with its JavaScript and CSS for
simplicity. Apps can also load external scripts and resources from origins
specified in
_meta.ui.csp. -
Sandboxed rendering: Web hosts typically render the HTML inside a
sandboxed iframe
within the conversation. The sandbox restricts the app’s access to the parent
page, ensuring security. The resource’s
_meta.uiobject can includepermissionsto request additional capabilities (e.g., microphone, camera) andcspto control what external origins the app can load resources from. -
Bidirectional communication: The app and host communicate through a
JSON-RPC protocol that forms its own dialect of MCP. Some requests and
notifications are shared with the core MCP protocol (e.g.,
tools/call), some are similar (e.g.,ui/initialize), and most are new with aui/method name prefix. The app can request tool calls, send messages, update the model’s context, and receive data from the host.
When to use MCP Apps
MCP Apps are a good fit when your use case involves: Exploring complex data. A user asks “show me sales by region.” A text response might list numbers, but an MCP App can render an interactive map where users click regions to drill down, hover for details, and toggle between metrics, all without additional prompts. Configuring with many options. Setting up a deployment involves dozens of interdependent choices. Rather than a back-and-forth conversation (“Which region?” “What instance size?” “Enable autoscaling?”), an MCP App presents a form where users see all options at once, with validation and defaults. Viewing rich media. When a user asks to review a PDF, see a 3D model, or preview generated images, text descriptions fall short. An MCP App embeds the actual viewer (pan, zoom, rotate) directly in the conversation. Real-time monitoring. A dashboard showing live metrics, logs, or system status needs continuous updates. An MCP App maintains a persistent connection, updating the display as data changes without requiring the user to ask “what’s the status now?” Multi-step workflows. Approving expense reports, reviewing code changes, or triaging issues involves examining items one by one. An MCP App provides navigation controls, action buttons, and state that persists across interactions.Getting started
You’ll need Node.js 18 or higher. Familiarity with MCP tools and resources is recommended since MCP Apps combine both primitives. Experience with the MCP TypeScript SDK will help you better understand the server-side patterns. The fastest way to create an MCP App is using an AI coding agent with the MCP Apps skill. If you prefer to set up a project manually, skip to Manual setup.Using an AI coding agent
AI coding agents with Skills support can scaffold a complete MCP App project for you. Skills are folders of instructions and resources that your agent loads when relevant. They teach the AI how to perform specialized tasks like creating MCP Apps. Thecreate-mcp-app skill includes architecture guidance, best practices, and
working examples that the agent uses to generate your project.
1
Install the skill
If you are using Claude Code, you can install the skill directly with:You can also use the Vercel Skills CLI to install skills across different AI coding agents:Alternatively, you can install the skill manually by cloning the ext-apps repository:And then copying the skill to the appropriate location for your agent:
For example, with Claude Code you can install the skill globally (available in all projects):Or install it for a single project only by copying to To verify the skill is installed, ask your agent “What skills do you have access to?” — you should see
| Agent | Skills directory (macOS/Linux) | Skills directory (Windows) |
|---|---|---|
| Claude Code | ~/.claude/skills/ | %USERPROFILE%\.claude\skills\ |
| VS Code and GitHub Copilot | ~/.copilot/skills/ | %USERPROFILE%\.copilot\skills\ |
| Gemini CLI | ~/.gemini/skills/ | %USERPROFILE%\.gemini\skills\ |
| Cline | ~/.cline/skills/ | %USERPROFILE%\.cline\skills\ |
| Goose | ~/.config/goose/skills/ | %USERPROFILE%\.config\goose\skills\ |
| Codex | ~/.codex/skills/ | %USERPROFILE%\.codex\skills\ |
This list is not comprehensive. Other agents may support skills in different locations; check your agent’s documentation.
.claude/skills/ in your project directory:create-mcp-app as one of the available skills.2
Create your app
Ask your AI coding agent to build it:The agent will recognize the 
create-mcp-app skill is relevant, load its instructions, then scaffold a complete project with server, UI, and configuration files.
3
Run your app
4
Test your app
Follow the instructions in Testing your app below. For the color picker example, start a new chat and ask Claude to provide you a color picker.

Manual setup
If you’re not using an AI coding agent, or prefer to understand the setup process, follow these steps.1
Create the project structure
A typical MCP App project separates the server code from the UI code:The server registers the tool and serves the UI resource. The UI files get bundled into a single HTML file that the server returns when the host requests the resource.
my-mcp-app
package.json
tsconfig.json
vite.config.ts
server.ts
mcp-app.html
src
mcp-app.ts
2
Install dependencies
ext-apps package provides helpers for both the server side (registering tools and resources) and the client side (the App class for UI-to-host communication). Vite with the vite-plugin-singlefile plugin bundles your UI into a single HTML file that can be served as a resource.3
Configure the project
- package.json
- tsconfig.json
- vite.config.ts
The
"type": "module" setting enables ES module syntax. The build script uses the INPUT environment variable to tell Vite which HTML file to bundle. The serve script runs your server using tsx for TypeScript execution.4
Build the project
With the project structure and configuration in place, continue to Building an MCP App below to implement the server and UI.
Building an MCP App
Let’s build a simple app that displays the current server time. This example demonstrates the full pattern: registering a tool with UI metadata, serving the bundled HTML as a resource, and building a UI that communicates with the server.Server implementation
The server needs to do two things: register a tool that includes the_meta.ui.resourceUri field, and register a resource handler that serves the
bundled HTML. Here’s the complete server file:
resourceUri: Theui://scheme tells hosts this is an MCP App resource. The path structure is arbitrary.registerAppTool: Registers a tool with the_meta.ui.resourceUrifield. When the host calls this tool, the UI is fetched and rendered, and the tool result is passed to it upon arrival.registerAppResource: Serves the bundled HTML when the host requests the UI resource.- Express server: Exposes the MCP server over HTTP on port 3001.
UI implementation
The UI consists of an HTML page and a TypeScript module that uses theApp
class to communicate with the host. Here’s the HTML:
app.connect(): Establishes communication with the host. Call this once when your app initializes.app.ontoolresult: A callback that fires when the host pushes a tool result to your app (e.g., when the tool is first called and the UI renders).app.callServerTool(): Lets your app proactively call tools on the server. Keep in mind that each call involves a round-trip to the server, so design your UI to handle latency gracefully.
App class provides additional methods for logging, opening URLs, and
updating the model’s context with structured data from your app. See the full
API documentation.
Testing your app
To test your MCP App, build the UI and start your local server:http://localhost:3001/mcp. However, to see your app render, you need an MCP
host that supports MCP Apps. You have several options.
Testing with Claude
Claude (web) and Claude Desktop support MCP Apps. For local development, you’ll need to expose your server to the internet. You can run an MCP server locally and use tools likecloudflared
to tunnel traffic through.
In a separate terminal, run:
https://random-name.trycloudflare.com) and add it
as a custom connector
in Claude - click on your profile, go to Settings, Connectors, and
finally Add custom connector.
Custom connectors are available on paid Claude plans (Pro, Max, or Team).

Testing with the basic-host
Theext-apps repository includes a test host for development. Clone the repo and
install dependencies:
npm start from ext-apps/examples/basic-host/ will start the basic-host
test interface. To connect it to a specific server (e.g., one you’re developing),
pass the SERVERS environment variable inline:
http://localhost:8080. You’ll see a simple interface where you can
select a tool and call it. When you call your tool, the host fetches the UI
resource and renders it in a sandboxed iframe. You can then interact with your
app and verify that tool calls work correctly.

Security model
MCP Apps run in a sandboxed iframe, which provides strong isolation from the host application. The sandbox prevents your app from accessing the parent window’s DOM, reading the host’s cookies or local storage, navigating the parent page, or executing scripts in the parent context. All communication between your app and the host goes through the postMessage API, which theApp class shown above abstracts for you. The host controls which
capabilities your app can access. For example, a host might restrict which tools
an app can call or disable the sendOpenLink capability.
The sandbox is designed to prevent apps from escaping to access the host or user data.
Framework support
MCP Apps use their own dialect of MCP, built on JSON-RPC like the core protocol. Some messages are shared with regular MCP (e.g.,tools/call), while others are
specific to apps (e.g., ui/initialize). The transport is
postMessage
instead of stdio or HTTP. Since it’s all standard web primitives, you can use any
framework or none at all.
The App class from @modelcontextprotocol/ext-apps is a convenience wrapper,
not a requirement. You can implement the
postMessage protocol
directly if you prefer to avoid dependencies or need tighter control.
The examples directory
includes starter templates for React, Vue, Svelte, Preact, Solid, and vanilla
JavaScript. These demonstrate recommended patterns for each framework’s system,
but they’re examples rather than requirements. You can choose whatever works
best for your use case.
Client support
MCP Apps is an extension to the core MCP specification. Host support varies by client.
-
Use a framework: The
@mcp-ui/clientpackage provides React components for rendering and interacting with MCP Apps views in your host application. See the MCP-UI documentation for usage details. - Build on AppBridge: The SDK includes an App Bridge module that handles rendering apps in sandboxed iframes, message passing, tool call proxying, and security policy enforcement. The basic-host example shows how to integrate it.
Examples
The ext-apps repository includes ready-to-run examples demonstrating different use cases:- 3D and visualization: map-server (CesiumJS globe), threejs-server (Three.js scenes), shadertoy-server (shader effects)
- Data exploration: cohort-heatmap-server, customer-segmentation-server, wiki-explorer-server
- Business applications: scenario-modeler-server, budget-allocator-server
- Media: pdf-server, video-resource-server, sheet-music-server, say-server (text-to-speech)
- Utilities: qr-server, system-monitor-server, transcript-server (speech-to-text)
- Starter templates: React, Vue, Svelte, Preact, Solid, vanilla JavaScript
Learn more
API Documentation
Full SDK reference and API details
GitHub Repository
Source code, examples, and issue tracker
Specification
Technical specification for implementers