MCP App Studio#
Build interactive apps for MCP Apps hosts (ChatGPT, Claude Desktop, etc.).
ChatGPT is an MCP Apps host. This SDK is MCP-first: it uses the standard
ui/* bridge everywhere, and treats window.openai as optional ChatGPT-only
extensions layered on top.
What You Get#
- Local workbench — Preview widgets without deploying
- Universal SDK — Single API works across MCP Apps hosts
- Optional ChatGPT extensions — Feature-detected
window.openaihelpers - One-command export — Generate production bundle + MCP server
Quick Start#
npx mcp-app-studio my-app
cd my-app
npm install
npm run dev
Open http://localhost:3002 — you're in the workbench.
Universal SDK#
The SDK provides React hooks that work identically across platforms:
import {
UniversalProvider,
usePlatform,
useToolInput,
useCallTool,
useTheme,
useFeature,
hasChatGPTExtensions,
} from "mcp-app-studio";
function MyWidget() {
const platform = usePlatform(); // "mcp" | "unknown"
const input = useToolInput<{ query: string }>();
const callTool = useCallTool();
const theme = useTheme();
// Optional ChatGPT extensions (window.openai)
const hasWidgetState = useFeature("widgetState");
const canUseOpenAIExtensions = hasChatGPTExtensions();
return (
<div className={theme === 'dark' ? 'bg-gray-900' : 'bg-white'}>
{/* Your widget */}
</div>
);
}
// Wrap your app
function App() {
return (
<UniversalProvider>
<MyWidget />
</UniversalProvider>
);
}
Migrating from 0.5.x#
Platform detection#
detectPlatform() now reports host family ("mcp" or "unknown"). It no
longer returns "chatgpt" directly.
// Before (0.5.x)
import { detectPlatform } from "mcp-app-studio";
if (detectPlatform() === "chatgpt") {
// ChatGPT-specific behavior
}
// After (MCP-first)
import { hasChatGPTExtensions, useFeature } from "mcp-app-studio";
if (hasChatGPTExtensions()) {
// ChatGPT extension layer is available (window.openai)
}
const hasWidgetState = useFeature("widgetState");
Provider imports#
Use UniversalProvider from the main package export. The
mcp-app-studio/chatgpt entrypoint is removed.
// Before (0.5.x)
import { ChatGPTProvider } from "mcp-app-studio/chatgpt";
// After (MCP-first)
import { UniversalProvider } from "mcp-app-studio";
Platform Capabilities#
| Feature | MCP Apps bridge | ChatGPT extensions (window.openai) |
|---|---|---|
callTool | ✅ | ✅ (callTool) |
openLink | ✅ | ✅ (openExternal) |
sendMessage | ✅ | ✅ (sendFollowUpMessage) |
modelContext (ui/update-model-context) | ✅ | — |
widgetState (persistence) | — | ✅ |
fileUpload / fileDownload | — | ✅ |
requestModal | — | ✅ |
partialToolInput (streaming) | Host-dependent | — |
Workflow#
1. DEVELOP npm run dev Edit widgets, test with mock tools
2. EXPORT npm run export Generate widget bundle + manifest
3. DEPLOY Your choice Vercel, Netlify, any static host
4. REGISTER Platform dashboard Connect your app
Generated Project#
my-app/
├── app/ Next.js app
├── components/
│ └── examples/ Example widgets
├── lib/
│ ├── workbench/ Dev environment + React hooks
│ └── export/ Production bundler
└── server/ MCP server (if selected)
Export Output#
npm run export
Generates:
export/
├── widget/
│ └── index.html Self-contained widget (deploy to static host)
├── manifest.json App manifest
└── README.md Deployment instructions
Debugging#
Enable debug mode to troubleshoot platform detection:
import { enableDebugMode, detectPlatformDetailed } from "mcp-app-studio";
// In browser console or before app init
enableDebugMode();
// Get detailed detection info
const result = detectPlatformDetailed();
console.log('Platform:', result.platform);
console.log('Detected by:', result.detectedBy);
console.log('Checks:', result.checks);
Tool metadata (_meta.ui.resourceUri)#
For tools that render UI, OpenAI recommends using _meta.ui.resourceUri (with
legacy support for _meta["openai/outputTemplate"]). See the starter template
and MCP server generator for a working example.
MCP Server#
If you selected "Include MCP server" during setup:
cd server
npm install
npm run dev # http://localhost:3001/mcp
npm run inspect # Test with MCP Inspector
Learn More#
License#
MIT