Migrating from OpenAI Apps SDK to MCP Apps SDK
This guide helps you migrate from the OpenAI Apps SDK (window.openai.*) to the MCP Apps SDK (@modelcontextprotocol/ext-apps).
Quick Start Comparison
| OpenAI Apps SDK |
MCP Apps SDK |
Implicit global (window.openai) |
Explicit instance (new App(...)) |
| Properties pre-populated on load |
Async connection + notifications |
| Sync property access |
Getters + event handlers |
Setup & Connection
| OpenAI |
MCP Apps |
Notes |
window.openai (auto-available) |
const app = new App({name, version}, {}) |
MCP requires explicit instantiation |
| (implicit) |
await app.connect() |
MCP requires async connection; auto-detects OpenAI env |
| — |
await app.connect(new OpenAITransport()) |
Force OpenAI mode (not yet available, see PR #172) |
| — |
await app.connect(new PostMessageTransport(...)) |
Force MCP mode explicitly |
Host Context Properties
| OpenAI |
MCP Apps |
Notes |
window.openai.theme |
app.getHostContext()?.theme |
"light" | "dark" |
window.openai.locale |
app.getHostContext()?.locale |
BCP 47 language tag (e.g., "en-US") |
window.openai.displayMode |
app.getHostContext()?.displayMode |
"inline" | "pip" | "fullscreen" |
window.openai.maxHeight |
app.getHostContext()?.viewport?.maxHeight |
Max container height in px |
window.openai.safeArea |
app.getHostContext()?.safeAreaInsets |
{ top, right, bottom, left } |
window.openai.userAgent |
app.getHostContext()?.userAgent |
Host user agent string |
| — |
app.getHostContext()?.availableDisplayModes |
MCP adds: which modes host supports |
| — |
app.getHostContext()?.toolInfo |
MCP adds: tool metadata during call |
Tool Data (Input/Output)
| OpenAI |
MCP Apps |
Notes |
window.openai.toolInput |
app.ontoolinput = (params) => { params.arguments } |
Tool arguments; MCP uses callback |
window.openai.toolOutput |
app.ontoolresult = (params) => { params.content } |
Tool result; MCP uses callback |
window.openai.toolResponseMetadata |
app.ontoolresult → params._meta |
Widget-only metadata from server |
| — |
app.ontoolinputpartial = (params) => {...} |
MCP adds: streaming partial args |
| — |
app.ontoolcancelled = (params) => {...} |
MCP adds: cancellation notification |
Calling Tools
| OpenAI |
MCP Apps |
Notes |
await window.openai.callTool(name, args) |
await app.callServerTool({ name, arguments: args }) |
Call another MCP server tool |
Sending Messages
| OpenAI |
MCP Apps |
Notes |
await window.openai.sendFollowUpMessage({ prompt }) |
await app.sendMessage({ role: "user", content: [{ type: "text", text: prompt }] }) |
MCP uses structured content array |
External Links
| OpenAI |
MCP Apps |
Notes |
await window.openai.openExternal({ href }) |
await app.openLink({ url: href }) |
Different param name: href → url |
Display Mode
| OpenAI |
MCP Apps |
Notes |
await window.openai.requestDisplayMode({ mode }) |
await app.requestDisplayMode({ mode }) |
Same API |
| — |
Check app.getHostContext()?.availableDisplayModes first |
MCP lets you check what's available |
Size Reporting
| OpenAI |
MCP Apps |
Notes |
window.openai.notifyIntrinsicHeight(height) |
app.sendSizeChanged({ width, height }) |
MCP includes width |
| Manual only |
Auto via { autoResize: true } (default) |
MCP auto-reports via ResizeObserver |
State Persistence
| OpenAI |
MCP Apps |
Notes |
window.openai.widgetState |
— |
Not directly available in MCP |
window.openai.setWidgetState(state) |
— |
Use alternative mechanisms (localStorage, server-side state, etc.) |
File Operations (Not Yet in MCP Apps)
| OpenAI |
MCP Apps |
Notes |
await window.openai.uploadFile(file) |
— |
Not yet implemented |
await window.openai.getFileDownloadUrl({ fileId }) |
— |
Not yet implemented |
Other (Not Yet in MCP Apps)
| OpenAI |
MCP Apps |
Notes |
await window.openai.requestModal(options) |
— |
Not yet implemented |
window.openai.requestClose() |
— |
Not yet implemented |
window.openai.view |
— |
Not yet mapped |
Event Handling
| OpenAI |
MCP Apps |
Notes |
Read window.openai.* on load |
app.ontoolinput = (params) => {...} |
Register before connect() |
Read window.openai.* on load |
app.ontoolresult = (params) => {...} |
Register before connect() |
| Poll or re-read properties |
app.onhostcontextchanged = (ctx) => {...} |
MCP pushes context changes |
| — |
app.onteardown = async () => {...} |
MCP adds: cleanup before unmount |
Logging
| OpenAI |
MCP Apps |
Notes |
console.log(...) |
app.sendLog({ level: "info", data: "..." }) |
MCP provides structured logging |
Host Info
| OpenAI |
MCP Apps |
Notes |
| — |
app.getHostVersion() |
Returns { name, version } of host |
| — |
app.getHostCapabilities() |
Check serverTools, openLinks, logging, etc. |
Full Migration Example
Before (OpenAI)
// OpenAI Apps SDK
const theme = window.openai.theme;
const toolArgs = window.openai.toolInput;
const toolResult = 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" });
After (MCP Apps)
import { App } from "@modelcontextprotocol/ext-apps";
const app = new App(
{ name: "MyApp", version: "1.0.0" },
{},
{ autoResize: true }, // auto height reporting
);
// Register handlers BEFORE connect
app.ontoolinput = (params) => {
console.log("Tool args:", params.arguments);
};
app.ontoolresult = (params) => {
console.log("Tool result:", params.content);
};
app.onhostcontextchanged = (ctx) => {
if (ctx.theme) applyTheme(ctx.theme);
};
// Connect (auto-detects OpenAI vs MCP)
await app.connect();
// Access context
const theme = 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" });
Key Differences Summary
- Initialization: OpenAI is implicit; MCP requires
new App() + await app.connect()
- Data Flow: OpenAI pre-populates; MCP uses async notifications (register handlers before
connect())
- Auto-resize: MCP has built-in ResizeObserver support via
autoResize option
- Structured Content: MCP uses
{ type: "text", text: "..." } arrays for messages
- Context Changes: MCP pushes updates via
onhostcontextchanged; no polling needed
- Capabilities: MCP lets you check what the host supports before calling methods