Back to list
olaservo

mcp-apps-ts

by olaservo

Agent skills for building, testing, and learning about MCP

0🍴 0📅 Jan 25, 2026

SKILL.md


name: mcp-apps-ts description: Build interactive HTML UIs for MCP servers using the MCP Apps extension (SEP-1865). Covers server-side tool registration with UI resources, client-side App lifecycle, and host integration via AppBridge. Use when creating visual interfaces for MCP tools.

TypeScript MCP Apps Builder

Build interactive HTML UIs for MCP tools using the MCP Apps extension (SEP-1865).

This is an experimental extension. MCP Apps enables servers to deliver interactive HTML UIs that run in sandboxed iframes, allowing rich visual interfaces while maintaining security.

Tip: Stay up to date! MCP Apps is under active development. Before starting, check the ext-apps repository for:

How It Works

  1. Browse the snippet catalog below or in snippets/
  2. Identify your role: server developer, app developer, or host integrator
  3. Copy the snippets you need into your project
  4. Customize the copied code for your use case

Quick Start Decision Trees

What Role Are You Building?

Want a complete, runnable starter project?
  -> Copy a SCAFFOLD (new!)
      - scaffold-vanilla-server: Full server + vanilla JS UI
        Copy the directory, run npm install && npm run dev
        See snippets/scaffold/vanilla-server/README.md

Building an MCP server that provides tools with UIs?
  -> Start with SERVER snippets
      - tool-with-ui: Register tool with associated HTML UI
      - tool-with-structured: Return structured content
      - resource-with-csp: Add Content Security Policy
      - server-with-private-tools: Hide tools from model (NEW!)

Building the HTML UI that displays to users?
  -> Start with APP snippets
      - app-vanilla-basic: Simple App class setup (vanilla JS)
      - app-vanilla-full: Full lifecycle handlers (vanilla JS)
      - app-react-basic: React hooks integration
      - app-react-with-styles: React with host styling (NEW!)
      - tool-calling: Call back to server tools
      - app-with-display-mode: Request fullscreen/pip (NEW!)
      - app-with-model-context: Update model context (NEW!)

Building a host/client that embeds MCP app UIs?
  -> Start with HOST snippets
      - host-full-integration: Complete end-to-end flow (single server)
      - host-multi-server: Connect to multiple servers with UI routing
      - sandbox-proxy: Required for security
      - app-bridge-basic: Just the AppBridge setup
      - app-bridge-handlers: Full handlers setup

Which App Framework Should I Use?

Minimal dependencies, simple UI?
  -> Use Vanilla JS snippets
      - app-vanilla-basic for getting started
      - app-vanilla-full for full control

React-based UI with state management?
  -> Use React snippets
      - app-react-basic for hooks integration
      - app-react-with-styles for host styling

Phase 1: Research

1.1 Understand MCP Apps Architecture

MCP Apps uses a two-part registration pattern: Tool + UI Resource.

ComponentPackageRole
Server@modelcontextprotocol/sdk + ext-apps/serverRegister tools with ui:// resources
App@modelcontextprotocol/ext-appsHTML UI running in sandbox iframe
Host@modelcontextprotocol/ext-apps/app-bridgeEmbeds and manages app iframes

1.2 Key Concepts

  • ui:// URI Scheme: Tools reference UI resources via ui://tool-name/app.html URIs
  • RESOURCE_URI_META_KEY: Constant for linking tools to UIs in _meta
  • RESOURCE_MIME_TYPE: text/html;profile=mcp-app identifies MCP App resources
  • PostMessageTransport: Communication between app iframe and host
  • Double-iframe Sandboxing: Outer iframe isolates, inner iframe runs app with allow-scripts
  • Host Context: Theme, locale, styles, and safe area information from the host
  • Display Modes: Inline, fullscreen, or picture-in-picture display options

1.3 Browse Available Snippets

SnippetDescriptionBest For
scaffold-vanilla-serverComplete starter projectQuickest start - copy & run
tool-with-uiTool with UI resource registrationBasic server setup
tool-with-structuredTool returning structuredContentRich data responses
resource-with-cspUI resource with CSP metadataSecurity-conscious apps
server-with-private-toolsTools hidden from modelUI-only actions (NEW!)
app-vanilla-basicBasic App class (vanilla JS)Quick prototypes
app-vanilla-fullFull lifecycle handlersProduction apps
app-react-basicReact hooks integrationReact projects
app-react-with-stylesReact with host stylingThemed React apps (NEW!)
tool-callingCall MCP tools from appInteractive UIs
app-with-display-modeRequest fullscreen/pipImmersive UIs (NEW!)
app-with-model-contextUpdate model contextState persistence (NEW!)
app-bridge-basicBasic host embeddingSimple integration
app-bridge-handlersFull AppBridge handlersCustom hosts
host-full-integrationComplete host flowEnd-to-end hosting
host-multi-serverMulti-server hostMultiple server routing
sandbox-proxySandbox proxy HTMLHost security

Phase 2: Implement

2.1 Server-Side: Register Tool with UI

npm install @modelcontextprotocol/sdk @modelcontextprotocol/ext-apps zod

Copy the tool-with-ui snippet and customize:

  1. Define your tool's input schema
  2. Create the UI resource HTML content
  3. Link tool to UI via ui:// URI and _meta.ui.resourceUri

For tools that should only be callable by the UI (not the model), use server-with-private-tools:

_meta: { ui: { resourceUri: "ui://...", visibility: ["app"] } }

See also: For MCP server basics (transports, tool registration patterns), refer to the mcp-server-ts skill.

2.2 App-Side: Build the UI

Vanilla JS:

npm install @modelcontextprotocol/ext-apps

React:

npm install @modelcontextprotocol/ext-apps react react-dom

Copy the appropriate app snippet and implement:

  1. Initialize App with name and version
  2. Register handlers BEFORE calling connect()
  3. Handle ontoolresult for tool execution results
  4. Handle ontoolcancelled for cancellation
  5. Handle onhostcontextchanged for theme/style changes
  6. Call tools via app.callServerTool()

2.3 Host Context & Styling

Apps can access host context for theme, styles, and safe areas:

const context = app.getHostContext();
// {
//   theme: "light" | "dark",
//   locale: "en-US",
//   styles: { variables: { ... }, css: { fonts: "..." } },
//   safeAreaInsets: { top, right, bottom, left },
//   availableDisplayModes: ["inline", "fullscreen", "pip"]
// }

React: Use useHostStyleVariables() and useHostFonts() hooks to automatically apply host styles.

2.4 Display Modes

Apps can request different display modes:

// Check if fullscreen is available
if (context?.availableDisplayModes?.includes("fullscreen")) {
  await app.requestDisplayMode({ mode: "fullscreen" });
}

2.5 Model Context Updates

Apps can update the model's context with state information:

await app.updateModelContext({
  content: [{ type: "text", text: "User selected 3 items" }],
  structuredContent: { items: 3, total: 150.00 }
});

2.6 Host-Side: Embed Apps (Optional)

npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk

Copy the host-full-integration snippet for the complete flow, or start with app-bridge-basic for just the AppBridge setup:

  1. Connect MCP client to server (see mcp-client-ts skill)
  2. Create AppBridge with the connected client
  3. Set up sandbox proxy iframe (use sandbox-proxy snippet)
  4. Register handlers before connecting
  5. Load UI resource and initialize app

Critical: The App initiates ui/initialize, the Host responds! If building without AppBridge SDK, you must handle the request/response correctly. See "Common Pitfalls" in the Architecture reference.

See also: For MCP client basics (connecting to servers, calling tools), refer to the mcp-client-ts skill.


Phase 3: Test

3.1 Build All Components

# Build UI (using Vite with vite-plugin-singlefile)
npm run build

# Start MCP server
npm run serve

3.2 Test with Reference Host

# Clone ext-apps repo for test host
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/examples/basic-host
npm install && npm start
# Open http://localhost:8080

3.3 Quality Checklist

  • Tool correctly registers with ui:// resource
  • RESOURCE_MIME_TYPE is text/html;profile=mcp-app
  • App initializes without errors
  • Handlers registered BEFORE connect()
  • ontoolresult receives tool execution results
  • ontoolcancelled handles cancellation gracefully
  • onhostcontextchanged responds to theme changes
  • callServerTool() successfully calls server
  • CSP is properly declared (if using)

3.4 Common Gotchas

1. Always describe your UI in content: The model doesn't see the visual UI - only the tool result content. Always include a text description of what was rendered:

return {
  content: [{ type: "text", text: "Displayed weather widget showing 72°F, sunny conditions" }],
  structuredContent: { temp: 72, condition: "sunny" }
};

2. Data flow to model context:

  • content and structuredContent → sent to model context
  • _meta → NOT sent to model (only for host/UI metadata)
  • Don't put large base64 data in structuredContent - use content with resource references

3. Check for UI support before registering (coming soon): Once PR #313 merges, use hasUiSupport() to conditionally register UI tools:

server.oninitialized = ({ clientCapabilities }) => {
  if (hasUiSupport(clientCapabilities)) {
    registerAppTool(server, "weather", { /* ... */ }, handler);
  }
};

Available Snippets Catalog

Scaffold (Complete Starter Projects)

NameDescription
scaffold-vanilla-serverComplete MCP App server with vanilla JS UI - copy entire directory and run

Quick Start with Scaffold:

# Copy the scaffold to your project
cp -r snippets/scaffold/vanilla-server ./my-mcp-app

# Install and run
cd my-mcp-app
npm install
npm run dev

Server runs at http://localhost:3102/mcp. Test with the basic-host example.

Server (MCP Server with UI)

NameDescription
tool-with-uiRegister MCP tool with UI resource using registerAppTool and registerAppResource
tool-with-structuredTool returning structuredContent for typed responses
resource-with-cspUI resource with Content Security Policy metadata
server-with-private-toolsTools with visibility: ["app"] hidden from model

App (HTML UI in iframe)

NameDescription
app-vanilla-basicBasic App class with ontoolresult handler (vanilla JS)
app-vanilla-fullFull lifecycle: all handlers including ontoolcancelled, onhostcontextchanged
app-react-basicReact component with useApp hook
app-react-with-stylesReact with useHostStyleVariables and useHostFonts
tool-callingExamples of calling MCP tools from app UI
app-with-display-modeRequest fullscreen/pip display modes
app-with-model-contextUpdate model context with app state

Host (Embedding Apps)

NameDescription
app-bridge-basicBasic AppBridge setup with PostMessageTransport
app-bridge-handlersFull handlers: onmessage, onopenlink, onloggingmessage, onsizechange
host-full-integrationComplete flow: MCP client + tool call + UI detection + AppBridge
host-multi-serverMulti-server host with tool aggregation and UI routing
sandbox-proxySandbox proxy HTML for double-iframe security

Reference Files

For deeper guidance, load these reference documents:

Important: Read the "Common Pitfalls" section in the Architecture doc before implementing a host. Key issues include srcdoc iframe origins, protocol direction, message sequencing, and handler overwrite bugs.


SDK Packages

PackageImport PathPurpose
Main SDK@modelcontextprotocol/ext-appsApp class, types, style utilities
Server helpers@modelcontextprotocol/ext-apps/serverregisterAppTool, registerAppResource
React@modelcontextprotocol/ext-apps/reactuseApp, useHostStyleVariables, useHostFonts, useHostStyles
App Bridge@modelcontextprotocol/ext-apps/app-bridgeAppBridge for hosts

External Resources

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon