BotDojo LogoInteractive Agent SDKBeta
About BotDojoBotDojo

What Are MCP Apps?

MCP Apps are interactive UI components that render inside chat conversationsβ€”think of them as "widgets in chat" that can display rich interfaces, handle user interactions, and communicate with your agent.

Your Website
mystore.com/admin/products/editSTORE ADMINDashboardOrdersProductsCustomersAnalyticsSettingsEdit ProductSKU: BONSAI-PINE-002Product NameAncient Pine BonsaiPrice$449.99CategoryBonsai TreeDescription✨Enhance with AIPine bonsai with aged bark. 15 years old.AgentΓ—Ask anything...
Product page with basic description

Why MCP Apps?

🎨

Rich Interfaces

Show data visualizations, interactive forms, product cards, document editorsβ€”anything HTML can do.

πŸ”„

Two-Way Communication

Apps can send messages, call tools, and persist state. Agents can stream updates and results back.

πŸ”’

Secure & Sandboxed

Runs in isolated iframe with CSP enforcement, protecting your app from malicious content.

Built on Open Standards

MCP Apps are defined by SEP-1865, part of the Model Context Protocol (MCP) specification. This ensures compatibility across different MCP-enabled hosts and clients.

How to Define MCP Apps

1. Add UI Metadata to Your Tool

Tools can reference an MCP App by adding _meta.ui metadata. This tells the host which UI resource to render when the tool is called.

πŸ“„Example: Tool with UI Metadatatypescript
1{
2 name: 'show_product_card',
3 description: 'Display interactive product card',
4 inputSchema: {
5 type: 'object',
6 properties: {
7 productId: { type: 'string' },
8 name: { type: 'string' },
9 price: { type: 'number' }
10 }
11 },
12 _meta: { // From MCP spec (SEP-1865)
13 ui: {
14 resourceUri: 'ui://my-app/product-card', // Which UI to render
15 preferredFrameSize: { width: 600, height: 400 },
16 csp: {
17 connectDomains: ['https://api.example.com'], // Allowed API calls
18 resourceDomains: ['https://cdn.example.com'] // Allowed images/fonts
19 }
20 }
21 },
22 execute: async (args) => {
23 return [{ type: 'text', text: 'Product card displayed' }];
24 }
25}

2. Provide the UI Resource

Add a resource with matching URI that returns HTML content. The MIME type must be text/html;profile=mcp-app.

πŸ“„Example: UI Resourcetypescript
1resources: [
2 {
3 uri: 'ui://my-app/product-card', // Must match tool's resourceUri
4 name: 'Product Card Widget',
5 mimeType: 'text/html;profile=mcp-app', // Required for MCP Apps
6 getContent: async () => {
7 // In this playground, we use Next.js to bundle React components:
8 const { fetchMcpAppHtml } = await import('@/utils/fetchMcpApp');
9 const html = await fetchMcpAppHtml('product-card-app');
10
11 return {
12 uri: 'ui://my-app/product-card',
13 mimeType: 'text/html;profile=mcp-app',
14 text: html // Your HTML as a string
15 };
16 }
17 }
18]

πŸ’‘ Tip: You can deliver MCP Apps as inline HTML (bundled string) or remote HTTPS URL. See Inline vs Remote guide for details.

Building MCP Apps with useMcpApp Hook

The useMcpApp hook from mcp-app-view (open source) handles the MCP protocol communication, making it easy to build interactive widgets.

πŸ“„Example: Product Card Widgettypescript
1import { useMcpApp } from '@botdojo/chat-sdk/mcp-app-view/react';
2
3function ProductCardWidget() {
4 const containerRef = useRef<HTMLDivElement>(null);
5
6 const {
7 isInitialized, // true after handshake with host
8 tool, // { arguments, status, result }
9 hostContext, // { state, theme, viewport }
10 sendMessage, // Send messages to chat
11 callTool, // Call other tools locally
12 openLink, // Request to open URLs
13 } = useMcpApp({
14 containerRef,
15 autoReportSize: true, // Auto-report iframe height changes
16 });
17
18 if (!isInitialized) {
19 return <div>Loading...</div>;
20 }
21
22 const { productId, name, price } = tool.arguments || {};
23
24 return (
25 <div ref={containerRef} className="p-6">
26 <h2>{name}</h2>
27 <p>Price: ${price}</p>
28 <button onClick={() => sendMessage([
29 { type: 'text', text: `User clicked on product ${productId}` }
30 ])}>
31 Add to Cart
32 </button>
33 </div>
34 );
35}

Secure Hosting with MCP App Proxy

For security, MCP Apps must run in a sandboxed iframe with a different origin than your host app. BotDojo provides an open-source proxy server that implements the double-iframe architecture required by SEP-1865.

🌐

Public Proxy Instance

https://mcp-app-proxy.botdojo.com/

How the Proxy Works

Host (Your App)
origin: https://app.example.com
Outer Iframe (Proxy)
origin: https://mcp-app-proxy.com
Inner Iframe (Your MCP App)
srcdoc: <HTML with CSP>
sandbox: allow-scripts allow-same-origin
🎨 Your widget runs here

The different origin prevents MCP Apps from accessing your host app's cookies, localStorage, or making authenticated requests. The proxy forwards all JSON-RPC messages transparently while enforcing Content Security Policy.

βœ… Implementation Checklist

  1. Define tool with UI metadata: Add _meta.ui.resourceUri to your tool
  2. Provide UI resource: Add resource with MIME type text/html;profile=mcp-app
  3. Build your widget: Use useMcpApp hook from mcp-app-view
  4. Handle callbacks: Implement onToolCall, onUiMessage, onOpenLink in your host
  5. Set cache key: Provide cacheKey prop for optimal performance