AU

assistant-ui/mcp-app-studio-starter

Developer toolsOfficial
10 stars 6 forks Quality 48 Trend 8

Starter template for MCP App Studio

Overview

Starter template for building interactive apps for AI assistants with MCP App Studio. This template is automatically downloaded when you run npx mcp-app-studio. You don't need to clone this repo directly. - — as an MCP Apps host (standard ui/* bridge) - — as an MCP Apps host - — compatible with any MCP-supporting AI assistant Open http://localhost:3002 — you're in the workbench. This project also works with pnpm/yarn/bun (use the equivalent install + run commands). If you switch package managers (e.g. pnpm → npm), delete node_modules/ first to avoid confusing the installer. The MCP server (when server/ exists) runs at http://localhost:3001/mcp by default. If 3001 is already in use, it will select the next available port and write it to server/.mcp-port. The workbench simulates an MCP Apps host in an iframe. It also installs a window.openai shim so you can exercise during development (optional, non-standard).

README

MCP App Studio Starter

Starter template for building interactive apps for AI assistants with MCP App Studio.

Note: This template is automatically downloaded when you run npx mcp-app-studio. You don’t need to clone this repo directly.

Supported Platforms

Build once, deploy anywhere:

  • ChatGPT — as an MCP Apps host (standard ui/* bridge)
  • Claude Desktop — as an MCP Apps host
  • Any MCP Apps host — compatible with any MCP-supporting AI assistant

Quick Start

# npm (default)
npm install
npm run dev

Open http://localhost:3002 — you’re in the workbench.

This project also works with pnpm/yarn/bun (use the equivalent install + run commands).

If you switch package managers (e.g. pnpm → npm), delete node_modules/ first to avoid confusing the installer.

The MCP server (when server/ exists) runs at http://localhost:3001/mcp by default. If 3001 is already in use, it will select the next available port and write it to server/.mcp-port.

The workbench simulates an MCP Apps host in an iframe. It also installs a window.openai shim so you can exercise ChatGPT-only extensions during development (optional, non-standard).

Agent Workflow

Use .agent/skills/mcp-app-development/SKILL.md as the default coding-agent workflow for this repo. It defines the 80/20 capability-slice loop:

  • Build UI and real MCP tool(s) in one chunk
  • Use TDD (red -> green) for parity checks
  • Never treat mock-only behavior as done

Commands

Command Description
npm run dev Start workbench (Next.js + MCP server)
npm run build Production build
npm run export Generate widget bundle for deployment

Project Structure

app/                        Next.js pages
components/
├── examples/               Example widgets (POI Map)
├── workbench/              Workbench UI components
└── ui/                     Shared UI components
lib/
├── sdk/                    SDK exports for production
├── workbench/              React hooks + dev environment
└── export/                 Production bundler
server/                     MCP server (if included)

Building Your Widget

1. Create a component

// components/my-widget/index.tsx
import {
  useToolInput,
  useCallTool,
  useTheme,
  useCapabilities,
  useUpdateModelContext,
  useWidgetState,
} from "@/lib/sdk";

export function MyWidget() {
  const input = useToolInput();
  const callTool = useCallTool();
  const theme = useTheme();
  const capabilities = useCapabilities();
  const updateModelContext = useUpdateModelContext();
  const [widgetState, setWidgetState] = useWidgetState();

  const handleSearch = async () => {
    const result = await callTool("search", { query: input.query });
    console.log(result.structuredContent);
  };

  return (
    
  );
}

2. Register in the workbench

Add your component to lib/workbench/component-registry.tsx.

3. Add mock data

Configure mock tool responses in lib/workbench/mock-config/.

React Hooks Reference

Full documentation: lib/workbench/README.md

These hooks work identically across MCP hosts (including ChatGPT):

Hook Description
useToolInput() Get input arguments from tool call
useTheme() Get current theme (“light” or “dark”)
useCallTool() Call backend tools
useDisplayMode() Get/set display mode
useSendMessage() Send messages to conversation

Platform Detection (when needed)

Hook Description
useCapabilities() Get full capability object
useFeature(name) Check if specific feature is available

Host-Dependent / Extensions (advanced)

These hooks only work on specific platforms. Check availability first:

Hook Platform Description
useWidgetState() ChatGPT extensions Optional OpenAI/ChatGPT host-managed state
useUpdateModelContext() Host-dependent Update model-visible context dynamically
useToolInputPartial() Host-dependent Streaming input during generation
useLog() Host-dependent Structured logging to host
openModal() helper ChatGPT extensions (fallback-safe) Use host modal when available, fallback locally

useWidgetState() is not a standard MCP Apps persistence primitive. For portable MCP Apps, use app-managed persistence such as localStorage or server-backed tools.

Tool result metadata (_meta) is available via readToolResponseMetadata() when the host exposes window.openai.toolResponseMetadata.

Platform-Specific Features

MCP App Studio is MCP-first: prefer the MCP Apps bridge (ui/*) and feature-detect optional ChatGPT extensions (window.openai) when needed.

Feature MCP Apps standard ChatGPT extensions (optional)
Tool input Yes (alias: window.openai.toolInput)
Tool result Yes (alias: window.openai.toolOutput)
Tool result metadata (_meta) Yes Yes (alias: window.openai.toolResponseMetadata)
Call tool Yes (alias: window.openai.callTool)
Send message Host-dependent (alias: window.openai.sendFollowUpMessage)
Update model context Host-dependent (extension: window.openai.setWidgetState)
Host-managed modal No Yes (window.openai.requestModal)
Widget state persistence No Yes (OpenAI/ChatGPT host-managed state)
File upload/download No Yes
Open in app link No Yes (window.openai.setOpenInAppUrl)
Instant checkout No Yes (window.openai.requestCheckout) (private beta)

Use useCapabilities() or useFeature() to conditionally enable features.

  • Prefer local/in-widget modals for cross-host compatibility.
  • Use window.openai.requestModal() only when you specifically need a ChatGPT-hosted modal template.
  • Always feature-detect and provide a fallback:
if (typeof window !== "undefined" && window.openai?.requestModal) {
  await window.openai.requestModal({ title: "Details", params: { id } });
} else {
  // Fallback: local modal state or route navigation
}

Checkout Guidance (ChatGPT beta extension)

  • window.openai.requestCheckout(...) is currently a ChatGPT private beta extension.
  • Treat checkout as optional: feature-detect and provide a fallback (for example, external checkout).
  • If you expose a companion deep link in ChatGPT menus, use window.openai.setOpenInAppUrl({ href }).
import { requestCheckout, setOpenInAppUrl } from "@/lib/sdk";

setOpenInAppUrl("https://your-app.com/orders/123");

const outcome = await requestCheckout(
  { id: "checkout_123", payment_mode: "test" },
  () => window.open("https://your-app.com/checkout/123", "_blank"),
);

if (outcome.mode === "fallback") {
  // Non-ChatGPT host or checkout beta unavailable.
}

Exporting for Production

npm run export

Defaults for --entry and --export-name are read from mcp-app-studio.config.json (written by the CLI when you scaffold a project). You can override them via flags.

Generates:

export/
├── widget/
│   └── index.html      Self-contained widget
├── manifest.json       App manifest
└── README.md           Deployment instructions

The exported widget uses the mcp-app-studio SDK which automatically detects the host platform and uses the appropriate bridge.

MCP Metadata Defaults (Export)

  • ui.* metadata is canonical in exported server code.
  • Legacy metadata keys from older ChatGPT Apps integrations are not supported in exported MCP metadata.
  • Visibility defaults to host default (["model","app"]) when no visibility keys are set.
  • Widget resources are emitted with MCP Apps MIME type text/html;profile=mcp-app.

Widget resource CSP must use MCP-standard keys:

ui: {
  csp: {
    connectDomains: ["https://api.example.com"],
    resourceDomains: ["https://cdn.example.com"],
    frameDomains: ["https://www.youtube.com"],
    baseUriDomains: ["https://cdn.example.com"],
  },
}

Deploying

Widget

Deploy export/widget/ to any static host:

# Vercel
cd export/widget && vercel deploy

# Netlify
netlify deploy --dir=export/widget

# Or any static host (S3, Cloudflare Pages, etc.)

MCP Server

If you have a server/ directory:

cd server
npm run build
# Deploy to Vercel, Railway, Fly.io, etc.

Register with Platform

For ChatGPT:

  1. Update manifest.json with your deployed widget URL
  2. Go to ChatGPT Apps dashboard
  3. Create a new app and connect your MCP server
  4. Test in a new ChatGPT conversation

For Claude Desktop:

  1. Configure your MCP server in Claude Desktop settings
  2. The widget will render when tools with UI are invoked

Configuration

SDK Guide (Optional)

The workbench includes an AI-powered SDK guide. To enable:

cp .env.example .env.local
# then set:
# OPENAI_API_KEY="your-key"

MCP Server CORS

For production, restrict CORS to your widget domain:

cp server/.env.example server/.env
# then set:
# CORS_ORIGIN=https://your-widget-domain.com

Dark Mode

Exported widgets inherit host theme and token variables. Follow the framework-agnostic contract in lib/workbench/THEMING_CONTRACT.md.

At minimum, support data-theme / .dark and semantic tokens:

.my-element {
  background: var(--background);
  color: var(--foreground);
  border-color: var(--border);
}

Learn More

View the full README on GitHub

Install

This server does not publish a one-line install command.

Open the repository installation guide New to MCP? Start with the mcpskill.org guide →