### Test Host Setup Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Commands to clone the ext-apps repository, install dependencies, and start the basic-host example. ```bash git clone https://github.com/modelcontextprotocol/ext-apps.git cd ext-apps npm install cd examples/basic-host npm start ``` -------------------------------- ### Install dependencies Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Install the dependencies you'll need. ```bash npm init -y npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk express cors npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env ``` -------------------------------- ### Running Examples with basic-host Source: https://apps.extensions.modelcontextprotocol.io/api/index.html Steps to clone the repository, install dependencies, and start the basic-host for running examples. ```bash git clone https://github.com/modelcontextprotocol/ext-apps.git cd ext-apps npm install npm start ``` -------------------------------- ### Create project directory Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Start by creating a project directory. ```bash mkdir my-mcp-app && cd my-mcp-app ``` -------------------------------- ### Start Server Command Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Command to start the MCP server in watch mode. ```bash npm start ``` -------------------------------- ### Configure package.json scripts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Configure your package.json scripts for building and starting the app. ```bash npm pkg set type=module npm pkg set scripts.build="tsc --noEmit && tsc -p tsconfig.server.json && cross-env INPUT=mcp-app.html vite build" npm pkg set scripts.start='concurrently --raw "cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch" "tsx watch main.ts"' ``` -------------------------------- ### Configure package.json scripts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html Sets up npm scripts for building and starting the MCP app, including build commands for TypeScript compilation and Vite bundling, and a start command for development with hot-reloading. ```bash npm pkg set type=module npm pkg set scripts.build="tsc --noEmit && tsc -p tsconfig.server.json && cross-env INPUT=mcp-app.html vite build" npm pkg set scripts.start='concurrently "cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch" "tsx watch main.ts"' ``` -------------------------------- ### Server Listening Message Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html The expected output message when the MCP server starts successfully. ```bash MCP server listening on http://localhost:3001/mcp ``` -------------------------------- ### HTML for the View Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Creates the HTML structure for the 'Get Time' application, including a paragraph to display the server time and a button to trigger the time retrieval. ```html Get Time App

Server Time: Loading...

``` -------------------------------- ### Building and running examples Source: https://apps.extensions.modelcontextprotocol.io/api/media/CONTRIBUTING.md Command to build and run examples. ```bash npm run examples:start ``` -------------------------------- ### Clone repository and install dependencies Source: https://apps.extensions.modelcontextprotocol.io/api/documents/Testing_MCP_Apps.html Steps to clone the MCP Apps repository and install necessary dependencies for the basic-host example. ```bash git clone https://github.com/modelcontextprotocol/ext-apps.git cd ext-apps npm install cd examples/basic-host ``` -------------------------------- ### main.ts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html This file serves as the entry point for the application, providing functions to start the MCP server using either Streamable HTTP transport or stdio transport. It includes logic to select the transport based on command-line arguments. ```typescript import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; /** * Starts an MCP server with Streamable HTTP transport in stateless mode. * * @param createServer - Factory function that creates a new McpServer instance per request. */ export async function startStreamableHTTPServer( createServer: () => McpServer, ): Promise { const port = parseInt(process.env.PORT ?? "3001", 10); const app = createMcpExpressApp({ host: "0.0.0.0" }); app.use(cors()); app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => { transport.close().catch(() => {}); server.close().catch(() => {}); }); try { await server.connect(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("MCP error:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null, }); } } }); const httpServer = app.listen(port, (err) => { if (err) { console.error("Failed to start server:", err); process.exit(1); } console.log(`MCP server listening on http://localhost:${port}/mcp`); }); const shutdown = () => { console.log("\nShutting down..."); httpServer.close(() => process.exit(0)); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); } /** * Starts an MCP server with stdio transport. * * @param createServer - Factory function that creates a new McpServer instance. */ export async function startStdioServer( createServer: () => McpServer, ): Promise { await createServer().connect(new StdioServerTransport()); } async function main() { if (process.argv.includes("--stdio")) { await startStdioServer(createServer); } else { await startStreamableHTTPServer(createServer); } } main().catch((e) => { console.error(e); process.exit(1); }); ``` -------------------------------- ### MCP Apps Server Setup Source: https://apps.extensions.modelcontextprotocol.io/api/documents/migrate-openai-app.html Example of setting up an MCP Apps server, registering a tool, and registering a UI resource. ```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; import { z } from "zod"; function createServer() { const server = new McpServer({ name: "shop", version: "1.0.0" }); // Register tool with MCP Apps metadata registerAppTool( server, "shopping-cart", { title: "Shopping Cart", description: "Display the user's shopping cart", inputSchema: { userId: z.string() }, annotations: { readOnlyHint: true }, _meta: { ui: { resourceUri: "ui://view/cart.html" } }, }, async (args) => { const cart = await getCart(args.userId); return { content: [{ type: "text", text: JSON.stringify(cart) }], structuredContent: { cart }, }; }, ); // Register UI resource registerAppResource( server, "Cart View", "ui://view/cart.html", { description: "Shopping cart UI" }, async () => ({ contents: [ { uri: "ui://view/cart.html", mimeType: RESOURCE_MIME_TYPE, text: getCartHtml(), _meta: { ui: { csp: { resourceDomains: ["https://cdn.example.com"], connectDomains: ["https://api.example.com"], frameDomains: ["https://embed.example.com"], }, }, }, }, ], }), ); return server; } ``` -------------------------------- ### main.ts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html This file serves as the entry point for the application and provides functions to start the MCP server using either Streamable HTTP transport or stdio transport. It configures Express.js for the HTTP server, including CORS handling and request routing for MCP requests. It also includes graceful shutdown handling for server termination. ```typescript import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; /** * Starts an MCP server with Streamable HTTP transport in stateless mode. * * @param createServer - Factory function that creates a new McpServer instance per request. */ export async function startStreamableHTTPServer( createServer: () => McpServer, ): Promise { const port = parseInt(process.env.PORT ?? "3001", 10); const app = createMcpExpressApp({ host: "0.0.0.0" }); app.use(cors()); app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => { transport.close().catch(() => {}); server.close().catch(() => {}); }); try { await server.connect(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("MCP error:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null, }); } } }); const httpServer = app.listen(port, (err) => { if (err) { console.error("Failed to start server:", err); process.exit(1); } console.log(`MCP server listening on http://localhost:${port}/mcp`); }); const shutdown = () => { console.log("\nShutting down..."); httpServer.close(() => process.exit(0)); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); } /** * Starts an MCP server with stdio transport. * * @param createServer - Factory function that creates a new McpServer instance. */ export async function startStdioServer( createServer: () => McpServer, ): Promise { await createServer().connect(new StdioServerTransport()); } async function main() { if (process.argv.includes("--stdio")) { await startStdioServer(createServer); } else { await startStreamableHTTPServer(createServer); } } main().catch((e) => { console.error(e); process.exit(1); }); ``` -------------------------------- ### Starting development environment with hot reloading Source: https://apps.extensions.modelcontextprotocol.io/api/media/CONTRIBUTING.md Command to start the development environment with hot reloading for examples. ```bash npm run examples:dev ``` -------------------------------- ### Manual setup for custom scenarios Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html This example shows how to manually set up automatic size change notifications when autoResize is initially false. ```typescript const app = new App( { name: "MyApp", version: "1.0.0" }, {}, { autoResize: false }, ); await app.connect(transport); // Later, enable auto-resize manually const cleanup = app.setupSizeChangedNotifications(); // Clean up when done cleanup(); ``` -------------------------------- ### Manual Installation Source: https://apps.extensions.modelcontextprotocol.io/api/documents/agent-skills.html Cloning the repository for manual installation. ```git git clone https://github.com/modelcontextprotocol/ext-apps.git ``` -------------------------------- ### Installing dependencies Source: https://apps.extensions.modelcontextprotocol.io/api/media/CONTRIBUTING.md Command to install project dependencies. ```bash npm install ``` -------------------------------- ### Install beta version Source: https://apps.extensions.modelcontextprotocol.io/api/media/CONTRIBUTING.md Command to install a beta version of the package. ```bash npm install @modelcontextprotocol/ext-apps@beta ``` -------------------------------- ### Start basic-host with multiple servers Source: https://apps.extensions.modelcontextprotocol.io/api/documents/Testing_MCP_Apps.html Command to start the basic-host, connecting to multiple local MCP servers. ```bash SERVERS='["http://localhost:3001/mcp", "http://localhost:3002/mcp"]' npm start ``` -------------------------------- ### Example: View ready Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html This example shows how to set up a callback for `oninitialized` to log a message and send tool input to the View. ```javascript bridge.oninitialized = () => { console.log("View ready"); bridge.sendToolInput({ arguments: toolArgs }); }; ``` -------------------------------- ### Example: Basic usage Source: https://apps.extensions.modelcontextprotocol.io/api/functions/server-helpers.registerAppResource.html This example demonstrates the basic usage of the registerAppResource function. ```typescript registerAppResource( server, "Weather View", "ui://weather/view.html", { description: "Interactive weather display", }, async () => ({ contents: [ { uri: "ui://weather/view.html", mimeType: RESOURCE_MIME_TYPE, text: await fs.readFile("dist/view.html", "utf-8"), }, ], }), ); ``` -------------------------------- ### Start basic-host with a single server Source: https://apps.extensions.modelcontextprotocol.io/api/documents/Testing_MCP_Apps.html Command to start the basic-host, pointing it to a single local MCP server. ```bash SERVERS='["http://localhost:3001/mcp"]' npm start ``` -------------------------------- ### Clone and Install Repository Source: https://apps.extensions.modelcontextprotocol.io/api/media/CONTRIBUTING.md Steps to clone the repository and install dependencies for local development. ```bash git clone https://github.com/modelcontextprotocol/ext-apps.git cd ext-apps npm install ``` -------------------------------- ### Example: With CSP configuration for network access Source: https://apps.extensions.modelcontextprotocol.io/api/functions/server-helpers.registerAppResource.html This example shows how to configure CSP for network access when registering an app resource. ```typescript registerAppResource( server, "Music Player", "ui://music/player.html", { description: "Audio player with external soundfonts", }, async () => ({ contents: [ { uri: "ui://music/player.html", mimeType: RESOURCE_MIME_TYPE, text: musicPlayerHtml, _meta: { ui: { csp: { resourceDomains: ["https://cdn.example.com"], // For scripts/styles/images connectDomains: ["https://api.example.com"], // For fetch/WebSocket }, }, }, }, ], }), ); ``` -------------------------------- ### Client-Side Migration Example - Before (OpenAI) Source: https://apps.extensions.modelcontextprotocol.io/api/documents/migrate-openai-app.html Example code demonstrating how to use the OpenAI Apps SDK for various functionalities. ```javascript // OpenAI Apps SDK applyTheme(window.openai.theme); console.log("Tool args:", window.openai.toolInput); console.log("Tool result:", window.openai.toolOutput); // Call a tool const result = await window.openai.callTool("get_weather", { city: "Tokyo" }); // Send a message await window.openai.sendFollowUpMessage({ prompt: "Weather updated!" }); // Report height window.openai.notifyIntrinsicHeight(400); // Open link await window.openai.openExternal({ href: "https://example.com" }); ``` -------------------------------- ### Example: Apply style variables from host context Source: https://apps.extensions.modelcontextprotocol.io/api/functions/app.applyHostStyleVariables.html This example shows how to use CSS variables in your styles and apply them when the host context changes or after connecting. ```javascript // Use CSS variables in your styles document.body.style.background = "var(--color-background-primary)"; // Apply when host context changes app.onhostcontextchanged = (ctx) => { if (ctx.styles?.variables) { applyHostStyleVariables(ctx.styles.variables); } }; // Apply initial styles after connecting app.connect().then(() => { const ctx = app.getHostContext(); if (ctx?.styles?.variables) { applyHostStyleVariables(ctx.styles.variables); } }); ``` -------------------------------- ### Install via Vercel Skills CLI Source: https://apps.extensions.modelcontextprotocol.io/api/documents/agent-skills.html Installation command using the Vercel Skills CLI. ```bash npx skills add modelcontextprotocol/ext-apps ``` -------------------------------- ### Client-Side Migration Example - After (MCP Apps) Source: https://apps.extensions.modelcontextprotocol.io/api/documents/migrate-openai-app.html Example code demonstrating how to use the MCP Apps SDK, including registering handlers before connecting and using MCP-specific APIs. ```javascript import { App } from "@modelcontextprotocol/ext-apps"; const app = new App({ name: "MyApp", version: "1.0.0" }); // Register handlers BEFORE connect (events may occur immediately after connect) app.ontoolinput = (params) => { console.log("Tool args:", params.arguments); }; app.ontoolresult = (params) => { console.log("Tool result:", params.structuredContent); }; app.onhostcontextchanged = (ctx) => { if (ctx.theme) applyTheme(ctx.theme); }; // Connect (auto-detects OpenAI vs MCP) await app.connect(); // Access context applyTheme(app.getHostContext()?.theme); // Call a tool const result = await app.callServerTool({ name: "get_weather", arguments: { city: "Tokyo" }, }); // Send a message await app.sendMessage({ role: "user", content: [{ type: "text", text: "Weather updated!" }], }); // Open link (note: url not href) await app.openLink({ url: "https://example.com" }); ``` -------------------------------- ### Example: Forward to your LLM provider Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html This example shows how to set up a callback for `createSamplingMessage` to forward the message to an LLM provider. ```javascript bridge.oncreatesamplingmessage = async (params, extra) => { // Apply rate limiting, user approval, cost controls here return await myLlmProvider.complete(params, { signal: extra.signal }); }; ``` -------------------------------- ### App constructor example Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html This example demonstrates how to create a new MCP App instance with basic information, capabilities, and options. ```typescript const app = new App( { name: "MyApp", version: "1.0.0" }, { tools: { listChanged: true } }, // capabilities { autoResize: true }, // options ); ``` -------------------------------- ### Example: Simple completion Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html Creates a message and logs the content. This is a basic example of using the `createSamplingMessage` function. ```javascript const result = await app.createSamplingMessage({ messages: [ { role: "user", content: { type: "text", text: "Summarize this in one line." }, }, ], maxTokens: 100, }); console.log(result.content); ``` -------------------------------- ### Example: Agentic loop with tools Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html Demonstrates an agentic loop with tools, checking host capabilities and handling tool use. This example shows how to define tools and process results when the `stopReason` is 'toolUse'. ```javascript if (!app.getHostCapabilities()?.sampling?.tools) return; const result = await app.createSamplingMessage({ messages, maxTokens: 1024, tools: [ { name: "get_weather", description: "Get the current weather", inputSchema: { type: "object", properties: { city: { type: "string" } }, }, }, ], }); if (result.stopReason === "toolUse") { // result.content may be an array containing tool_use blocks } ``` -------------------------------- ### Example: Download a JSON file (embedded text resource) Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html This example demonstrates how to use the `downloadFile` method to download an embedded JSON file. It shows how to construct the `resource` object with `uri`, `mimeType`, and `text` properties, and how to handle potential download denials. ```javascript const data = JSON.stringify({ items: selectedItems }, null, 2); const { isError } = await app.downloadFile({ contents: [{ type: "resource", resource: { uri: "file:///export.json", mimeType: "application/json", text: data, }, }], }); if (isError) { console.warn("Download denied or cancelled"); } ``` -------------------------------- ### Compile Project Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Command to verify that the project compiles successfully. No output indicates success. ```bash npx tsc --noEmit ``` -------------------------------- ### Project Directory Structure Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html This shows the expected directory structure after setting up the project. ```bash my-mcp-app/ ├── main.ts ├── package.json ├── server.ts ├── tsconfig.json ├── tsconfig.server.json └── vite.config.ts ``` -------------------------------- ### Build Command Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Command to build the bundled View for the MCP application. ```bash npm run build ``` -------------------------------- ### Fetch updated weather data Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app.App.html Example of calling a server tool to get weather data. ```javascript try { const result = await app.callServerTool({ name: "get_weather", arguments: { location: "Tokyo" }, }); if (result.isError) { console.error("Tool returned error:", result.content); } else { console.log(result.content); } } catch (error) { console.error("Tool call failed:", error); } ``` -------------------------------- ### Build Output Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Indicates the output file generated after running the build command. ```bash $ ls dist/ mcp-app.html ``` -------------------------------- ### Vite Configuration Source: https://apps.extensions.modelcontextprotocol.io/api/media/SKILL-3.md Configure Vite to use `vite-plugin-singlefile` for bundling the MCP App into a single HTML file. This example assumes a basic Vite setup; add framework-specific plugins as needed. ```typescript import { defineConfig } from "vite"; import { viteSingleFile } from "vite-plugin-singlefile"; export default defineConfig({ plugins: [viteSingleFile()], build: { outDir: "dist", rollupOptions: { input: "mcp-app.html", }, }, }); ``` -------------------------------- ### TypeScript for the View Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Connects the HTML View to the MCP host, handles user interactions by calling server tools, and displays the received server time. ```typescript import { App } from "@modelcontextprotocol/ext-apps"; // Get element references const serverTimeEl = document.getElementById("server-time")!; const getTimeBtn = document.getElementById("get-time-btn")!; // Create app instance const app = new App({ name: "Get Time App", version: "1.0.0" }); // Handle tool results from the server. Set before `app.connect()` to avoid // missing the initial tool result. app.ontoolresult = (result) => { const time = result.content?.find((c) => c.type === "text")?.text; serverTimeEl.textContent = time ?? "[ERROR]"; }; // Wire up button click getTimeBtn.addEventListener("click", async () => { // `app.callServerTool()` lets the UI request fresh data from the server const result = await app.callServerTool({ name: "get-time", arguments: {} }); const time = result.content?.find((c) => c.type === "text")?.text; serverTimeEl.textContent = time ?? "[ERROR]"; }); // Connect to host app.connect(); ``` -------------------------------- ### Directory Structure Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Shows the expected file structure for the 'my-mcp-app' directory after creating the HTML and TypeScript files. ```bash my-mcp-app/ ├── main.ts ├── mcp-app.html ├── package.json ├── server.ts ├── src/ │ └── mcp-app.ts ├── tsconfig.json ├── tsconfig.server.json └── vite.config.ts ``` -------------------------------- ### server.ts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html This file registers a tool and its associated UI resource with the MCP server. It demonstrates the two-part registration process where a tool is linked to a resource via a URI, enabling interactive UIs when the tool is called. ```typescript import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import fs from "node:fs/promises"; import path from "node:path"; const DIST_DIR = path.join(import.meta.dirname, "dist"); /** * Creates a new MCP server instance with tools and resources registered. */ export function createServer(): McpServer { const server = new McpServer({ name: "Quickstart MCP App Server", version: "1.0.0", }); // Two-part registration: tool + resource, tied together by the resource URI. const resourceUri = "ui://get-time/mcp-app.html"; // Register a tool with UI metadata. When the host calls this tool, it reads // `_meta.ui.resourceUri` to know which resource to fetch and render as an // interactive UI. registerAppTool( server, "get-time", { title: "Get Time", description: "Returns the current server time.", inputSchema: {}, _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async () => { const time = new Date().toISOString(); return { content: [{ type: "text", text: time }] }; }, ); // Register the resource, which returns the bundled HTML/JavaScript for the UI. registerAppResource( server, resourceUri, resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () => { const html = await fs.readFile(path.join(DIST_DIR, "mcp-app.html"), "utf-8"); return { contents: [ { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }, ], }; }, ); return server; } ``` -------------------------------- ### Testing with basic-host Source: https://apps.extensions.modelcontextprotocol.io/api/media/SKILL-1.md Commands to build and run the server, and then run the basic-host example for testing. ```bash # Terminal 1: Build and run your server npm run build && npm run serve # Terminal 2: Run basic-host (from cloned repo) cd /tmp/mcp-ext-apps/examples/basic-host npm install SERVERS='["http://localhost:3001/mcp"]' npm run start # Open http://localhost:8080 ``` -------------------------------- ### sendToolCancelled Example Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html Example of how to send a tool cancellation notification with a reason. ```javascript // User clicked "Cancel" button bridge.sendToolCancelled({ reason: "User cancelled the operation" }); ``` -------------------------------- ### Create tsconfig.server.json Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Create tsconfig.server.json for compiling server-side code. ```json { "compilerOptions": { "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", "moduleResolution": "NodeNext", "declaration": true, "emitDeclarationOnly": true, "outDir": "./dist", "rootDir": ".", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["server.ts", "main.ts"] } ``` -------------------------------- ### sendResourceListChanged Example Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html Example of how to use sendResourceListChanged in an MCP client notification handler. ```javascript // In your MCP client notification handler: mcpClient.setNotificationHandler(ResourceListChangedNotificationSchema, () => { bridge.sendResourceListChanged(); }); ``` -------------------------------- ### Example Source: https://apps.extensions.modelcontextprotocol.io/api/modules/server-helpers.html Register a tool that displays a view and the HTML resource the tool references. ```javascript // Register a tool that displays a view registerAppTool( server, "weather", { description: "Get weather forecast", _meta: { ui: { resourceUri: "ui://weather/view.html" } }, }, toolCallback, ); // Register the HTML resource the tool references registerAppResource( server, "Weather View", "ui://weather/view.html", {}, readCallback, ); ``` -------------------------------- ### sendPromptListChanged Example Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html Example of how to use sendPromptListChanged in an MCP client notification handler. ```javascript // In your MCP client notification handler: mcpClient.setNotificationHandler(PromptListChangedNotificationSchema, () => { bridge.sendPromptListChanged(); }); ``` -------------------------------- ### Create vite.config.ts Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Create vite.config.ts to bundle the UI into a single HTML file. ```typescript import { createLogger, defineConfig } from "vite"; import { viteSingleFile } from "vite-plugin-singlefile"; const INPUT = process.env.INPUT; if (!INPUT) { throw new Error("INPUT environment variable is not set"); } const isDevelopment = process.env.NODE_ENV === "development"; const prefixedLogger = createLogger(); for (const level of ["info", "warn", "error"] as const) { const fn = prefixedLogger[level]; prefixedLogger[level] = (msg, opts) => fn(msg.replace(/^/mg, "[vite] "), opts); } export default defineConfig({ customLogger: prefixedLogger, plugins: [viteSingleFile()], build: { sourcemap: isDevelopment ? "inline" : undefined, cssMinify: !isDevelopment, minify: !isDevelopment, rollupOptions: { input: INPUT, }, outDir: "dist", emptyOutDir: false, }, }); ``` -------------------------------- ### Domain Example 2 Source: https://apps.extensions.modelcontextprotocol.io/api/interfaces/app.McpUiResourceMeta.html Example of a URL-derived subdomain for the domain field. ```string "www-example-com.oaiusercontent.com" ``` -------------------------------- ### Domain Example 1 Source: https://apps.extensions.modelcontextprotocol.io/api/interfaces/app.McpUiResourceMeta.html Example of a hash-based subdomain for the domain field. ```string "a904794854a047f6.claudemcpcontent.com" ``` -------------------------------- ### Create tsconfig.json Source: https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html Create the tsconfig.json file for general TypeScript compilation. ```json { "compilerOptions": { "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "verbatimModuleSyntax": true, "noEmit": true, "strict": true, "skipLibCheck": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src", "server.ts", "main.ts"] } ``` -------------------------------- ### useHostStyles Example Source: https://apps.extensions.modelcontextprotocol.io/api/functions/_modelcontextprotocol_ext-apps_react.useHostStyles.html Applies all host styles - pass initial context to apply styles from connect() immediately ```javascript function MyApp() { const { app } = useApp({ appInfo: { name: "MyApp", version: "1.0.0" }, capabilities: {}, }); // Apply all host styles - pass initial context to apply styles from connect() immediately useHostStyles(app, app?.getHostContext()); return (
Hello!
); } ``` -------------------------------- ### Install via Claude Code Plugin Source: https://apps.extensions.modelcontextprotocol.io/api/documents/agent-skills.html Installation command for Claude Code plugin. ```bash /plugin marketplace add modelcontextprotocol/ext-apps /plugin install mcp-apps@modelcontextprotocol-ext-apps ``` -------------------------------- ### sendToolResult example Source: https://apps.extensions.modelcontextprotocol.io/api/classes/app-bridge.AppBridge.html Example of sending a tool execution result using the AppBridge client. ```typescript const result = await mcpClient.request( { method: "tools/call", params: { name: "get_weather", arguments: args } }, CallToolResultSchema, ); bridge.sendToolResult(result); ``` -------------------------------- ### Complete Hybrid Example with Theming and Styling Source: https://apps.extensions.modelcontextprotocol.io/api/media/SKILL-3.md This comprehensive example integrates the hybrid app pattern with MCP-specific features like theming, style variables, fonts, and safe area insets. It initializes the MCP app by registering handlers before connecting and applies host styles. For standalone apps, it initializes using URL parameters. The core rendering logic remains unified. ```typescript import { App, PostMessageTransport, applyDocumentTheme, applyHostStyleVariables, applyHostFonts } from "@modelcontextprotocol/ext-apps"; const isMcpApp = window.location.origin === "null"; async function initMcpApp(): Promise> { const app = new App({ name: "My App", version: "1.0.0" }); // Register ALL handlers BEFORE connect() const params = await new Promise>((resolve) => { app.ontoolinput = (input) => resolve(input.arguments ?? {}); }); app.onhostcontextchanged = (ctx) => { if (ctx.theme) applyDocumentTheme(ctx.theme); if (ctx.styles?.variables) applyHostStyleVariables(ctx.styles.variables); if (ctx.styles?.css?.fonts) applyHostFonts(ctx.styles.css.fonts); if (ctx.safeAreaInsets) { const { top, right, bottom, left } = ctx.safeAreaInsets; document.body.style.padding = `${top}px ${right}px ${bottom}px ${left}px`; } }; app.onteardown = async () => { return {}; }; await app.connect(new PostMessageTransport()); return params; } async function initStandaloneApp(): Promise> { return Object.fromEntries(new URL(location.href).searchParams); } async function main() { const params = isMcpApp ? await initMcpApp() : await initStandaloneApp(); renderApp(params); // Same rendering logic — no fork needed } main().catch(console.error); ``` -------------------------------- ### Example Usage Source: https://apps.extensions.modelcontextprotocol.io/api/variables/app.OPEN_LINK_METHOD.html This example demonstrates how to use the OPEN_LINK_METHOD constant to check message methods. ```typescript import { SANDBOX_PROXY_READY_METHOD } from '@modelcontextprotocol/ext-apps'; if (event.data.method === SANDBOX_PROXY_READY_METHOD) { // Handle sandbox proxy ready notification } ```