Complete guide to building interactive apps that AI agents can control. Scaffold bidirectional MCP-UI and WebMCP integration with create-webmcp-app CLI tool.
Building a bidirectional MCP-UI app means the AI agent can invoke your code, which then provides UI to the user, which can then invoke the AI again. This creates interactive workflows where the interface evolves based on what the agent decides to do.npx create-webmcp-app scaffolds a bidirectional system with three components:
MCP Server (Cloudflare Worker) - Exposes tools to AI agents
Embedded Web App (React or Vanilla JS) - Runs in an iframe, registers its own tools
Communication Layer - IframeParentTransport ↔ IframeChildTransport
Your embedded app registers tools that AI can call, creating bidirectional interaction.
HTML/CSS/JavaScript with no build step. Uses CDN for dependencies.
Copy
# Select "vanilla" when promptedcd your-projectpnpm dev
Runs at: http://localhost:8889
React + TypeScript + Vite with hot module replacement.
Copy
# Select "react" when promptedcd your-projectpnpm dev
Runs at: http://localhost:8888
How it works: See MCP-UI Architecture for detailed diagrams and communication flow between the AI agent, MCP server, host application, and your embedded app.
import { createUIResource } from '@mcp-ui/server';import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';import { McpAgent } from 'agents/mcp';export class TemplateMCP extends McpAgent<Cloudflare.Env> { server = new McpServer({ name: 'webmcp-template', version: '1.0.0', }); async init() { /** * This tool tells the AI "here's an interactive web app you can use" * Returns a UIResource that the host application will render */ this.server.tool( 'showTemplateApp', `Display the template web application with WebMCP integration.After calling this tool, the app will appear and register the following WebMCP tools:- template_get_message: Get the current message from the app- template_update_message: Update the message displayed in the app- template_reset: Reset the message to default`, {}, async () => { // Point to your embedded app const iframeUrl = `${this.env.APP_URL}/`; // Create UI resource with iframe URL const uiResource = createUIResource({ uri: 'ui://template-app', content: { type: 'externalUrl', // Tell host to load this in iframe iframeUrl: iframeUrl, }, encoding: 'blob', }); return { content: [ { type: 'text', text: `# Template App StartedThe template app is now displayed in the side panel.**Available tools** (registered via WebMCP):- \`template_get_message\` - View the current message- \`template_update_message\` - Change the message- \`template_reset\` - Reset to defaultTry calling these tools to interact with the app!`, }, uiResource, ], }; } ); }}
The tools mentioned in the description are registered by the iframe, not by this server.
Full template with styling and additional tools available via npx create-webmcp-app (select “vanilla”). Tool handlers and UI buttons should share the same state management logic.
Update your MCP client to point to the production URL (e.g., https://your-app.workers.dev/mcp). See the mcp-ui-webmcp repository for detailed deployment instructions.