|
| 1 | +# Migrating from OpenAI Apps SDK to MCP Apps SDK |
| 2 | + |
| 3 | +This guide helps you migrate from the OpenAI Apps SDK (`window.openai.*`) to the MCP Apps SDK (`@modelcontextprotocol/ext-apps`). |
| 4 | + |
| 5 | +## Quick Start Comparison |
| 6 | + |
| 7 | +| OpenAI Apps SDK | MCP Apps SDK | |
| 8 | +| --------------------------------- | ---------------------------------- | |
| 9 | +| Implicit global (`window.openai`) | Explicit instance (`new App(...)`) | |
| 10 | +| Properties pre-populated on load | Async connection + notifications | |
| 11 | +| Sync property access | Getters + event handlers | |
| 12 | + |
| 13 | +## Setup & Connection |
| 14 | + |
| 15 | +| OpenAI | MCP Apps | Notes | |
| 16 | +| -------------------------------- | -------------------------------------------------- | ------------------------------------------------------ | |
| 17 | +| `window.openai` (auto-available) | `const app = new App({name, version}, {})` | MCP requires explicit instantiation | |
| 18 | +| (implicit) | `await app.connect()` | MCP requires async connection; auto-detects OpenAI env | |
| 19 | +| — | `await app.connect(new OpenAITransport())` | Force OpenAI mode (not yet available, see [PR #172](https://github.com/modelcontextprotocol/ext-apps/pull/172)) | |
| 20 | +| — | `await app.connect(new PostMessageTransport(...))` | Force MCP mode explicitly | |
| 21 | + |
| 22 | +## Host Context Properties |
| 23 | + |
| 24 | +| OpenAI | MCP Apps | Notes | |
| 25 | +| --------------------------- | --------------------------------------------- | --------------------------------------- | |
| 26 | +| `window.openai.theme` | `app.getHostContext()?.theme` | `"light"` \| `"dark"` | |
| 27 | +| `window.openai.locale` | `app.getHostContext()?.locale` | BCP 47 language tag (e.g., `"en-US"`) | |
| 28 | +| `window.openai.displayMode` | `app.getHostContext()?.displayMode` | `"inline"` \| `"pip"` \| `"fullscreen"` | |
| 29 | +| `window.openai.maxHeight` | `app.getHostContext()?.viewport?.maxHeight` | Max container height in px | |
| 30 | +| `window.openai.safeArea` | `app.getHostContext()?.safeAreaInsets` | `{ top, right, bottom, left }` | |
| 31 | +| `window.openai.userAgent` | `app.getHostContext()?.userAgent` | Host user agent string | |
| 32 | +| — | `app.getHostContext()?.availableDisplayModes` | MCP adds: which modes host supports | |
| 33 | +| — | `app.getHostContext()?.toolInfo` | MCP adds: tool metadata during call | |
| 34 | + |
| 35 | +## Tool Data (Input/Output) |
| 36 | + |
| 37 | +| OpenAI | MCP Apps | Notes | |
| 38 | +| ------------------------------------ | ---------------------------------------------------- | ----------------------------------- | |
| 39 | +| `window.openai.toolInput` | `app.ontoolinput = (params) => { params.arguments }` | Tool arguments; MCP uses callback | |
| 40 | +| `window.openai.toolOutput` | `app.ontoolresult = (params) => { params.content }` | Tool result; MCP uses callback | |
| 41 | +| `window.openai.toolResponseMetadata` | `app.ontoolresult` → `params._meta` | Widget-only metadata from server | |
| 42 | +| — | `app.ontoolinputpartial = (params) => {...}` | MCP adds: streaming partial args | |
| 43 | +| — | `app.ontoolcancelled = (params) => {...}` | MCP adds: cancellation notification | |
| 44 | + |
| 45 | +## Calling Tools |
| 46 | + |
| 47 | +| OpenAI | MCP Apps | Notes | |
| 48 | +| ------------------------------------------ | ----------------------------------------------------- | ---------------------------- | |
| 49 | +| `await window.openai.callTool(name, args)` | `await app.callServerTool({ name, arguments: args })` | Call another MCP server tool | |
| 50 | + |
| 51 | +## Sending Messages |
| 52 | + |
| 53 | +| OpenAI | MCP Apps | Notes | |
| 54 | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------- | |
| 55 | +| `await window.openai.sendFollowUpMessage({ prompt })` | `await app.sendMessage({ role: "user", content: [{ type: "text", text: prompt }] })` | MCP uses structured content array | |
| 56 | + |
| 57 | +## External Links |
| 58 | + |
| 59 | +| OpenAI | MCP Apps | Notes | |
| 60 | +| -------------------------------------------- | ----------------------------------- | ------------------------------------ | |
| 61 | +| `await window.openai.openExternal({ href })` | `await app.openLink({ url: href })` | Different param name: `href` → `url` | |
| 62 | + |
| 63 | +## Display Mode |
| 64 | + |
| 65 | +| OpenAI | MCP Apps | Notes | |
| 66 | +| -------------------------------------------------- | --------------------------------------------------------- | ----------------------------------- | |
| 67 | +| `await window.openai.requestDisplayMode({ mode })` | `await app.requestDisplayMode({ mode })` | Same API | |
| 68 | +| — | Check `app.getHostContext()?.availableDisplayModes` first | MCP lets you check what's available | |
| 69 | + |
| 70 | +## Size Reporting |
| 71 | + |
| 72 | +| OpenAI | MCP Apps | Notes | |
| 73 | +| --------------------------------------------- | ----------------------------------------- | ----------------------------------- | |
| 74 | +| `window.openai.notifyIntrinsicHeight(height)` | `app.sendSizeChanged({ width, height })` | MCP includes width | |
| 75 | +| Manual only | Auto via `{ autoResize: true }` (default) | MCP auto-reports via ResizeObserver | |
| 76 | + |
| 77 | +## State Persistence |
| 78 | + |
| 79 | +| OpenAI | MCP Apps | Notes | |
| 80 | +| ------------------------------------- | -------- | ----------------------------------------------- | |
| 81 | +| `window.openai.widgetState` | — | Not directly available in MCP | |
| 82 | +| `window.openai.setWidgetState(state)` | — | Use alternative mechanisms (`localStorage`, server-side state, etc.) | |
| 83 | + |
| 84 | +## File Operations (Not Yet in MCP Apps) |
| 85 | + |
| 86 | +| OpenAI | MCP Apps | Notes | |
| 87 | +| ---------------------------------------------------- | -------- | ------------------- | |
| 88 | +| `await window.openai.uploadFile(file)` | — | Not yet implemented | |
| 89 | +| `await window.openai.getFileDownloadUrl({ fileId })` | — | Not yet implemented | |
| 90 | + |
| 91 | +## Other (Not Yet in MCP Apps) |
| 92 | + |
| 93 | +| OpenAI | MCP Apps | Notes | |
| 94 | +| ------------------------------------------- | -------- | ------------------- | |
| 95 | +| `await window.openai.requestModal(options)` | — | Not yet implemented | |
| 96 | +| `window.openai.requestClose()` | — | Not yet implemented | |
| 97 | +| `window.openai.view` | — | Not yet mapped | |
| 98 | + |
| 99 | +## Event Handling |
| 100 | + |
| 101 | +| OpenAI | MCP Apps | Notes | |
| 102 | +| ------------------------------ | ------------------------------------------- | -------------------------------- | |
| 103 | +| Read `window.openai.*` on load | `app.ontoolinput = (params) => {...}` | Register before `connect()` | |
| 104 | +| Read `window.openai.*` on load | `app.ontoolresult = (params) => {...}` | Register before `connect()` | |
| 105 | +| Poll or re-read properties | `app.onhostcontextchanged = (ctx) => {...}` | MCP pushes context changes | |
| 106 | +| — | `app.onteardown = async () => {...}` | MCP adds: cleanup before unmount | |
| 107 | + |
| 108 | +## Logging |
| 109 | + |
| 110 | +| OpenAI | MCP Apps | Notes | |
| 111 | +| ------------------ | --------------------------------------------- | ------------------------------- | |
| 112 | +| `console.log(...)` | `app.sendLog({ level: "info", data: "..." })` | MCP provides structured logging | |
| 113 | + |
| 114 | +## Host Info |
| 115 | + |
| 116 | +| OpenAI | MCP Apps | Notes | |
| 117 | +| ------ | --------------------------- | ------------------------------------------------- | |
| 118 | +| — | `app.getHostVersion()` | Returns `{ name, version }` of host | |
| 119 | +| — | `app.getHostCapabilities()` | Check `serverTools`, `openLinks`, `logging`, etc. | |
| 120 | + |
| 121 | +## Full Migration Example |
| 122 | + |
| 123 | +### Before (OpenAI) |
| 124 | + |
| 125 | +```typescript |
| 126 | +// OpenAI Apps SDK |
| 127 | +const theme = window.openai.theme; |
| 128 | +const toolArgs = window.openai.toolInput; |
| 129 | +const toolResult = window.openai.toolOutput; |
| 130 | + |
| 131 | +// Call a tool |
| 132 | +const result = await window.openai.callTool("get_weather", { city: "Tokyo" }); |
| 133 | + |
| 134 | +// Send a message |
| 135 | +await window.openai.sendFollowUpMessage({ prompt: "Weather updated!" }); |
| 136 | + |
| 137 | +// Report height |
| 138 | +window.openai.notifyIntrinsicHeight(400); |
| 139 | + |
| 140 | +// Open link |
| 141 | +await window.openai.openExternal({ href: "https://example.com" }); |
| 142 | +``` |
| 143 | + |
| 144 | +### After (MCP Apps) |
| 145 | + |
| 146 | +```typescript |
| 147 | +import { App } from "@modelcontextprotocol/ext-apps"; |
| 148 | + |
| 149 | +const app = new App( |
| 150 | + { name: "MyApp", version: "1.0.0" }, |
| 151 | + {}, |
| 152 | + { autoResize: true }, // auto height reporting |
| 153 | +); |
| 154 | + |
| 155 | +// Register handlers BEFORE connect |
| 156 | +app.ontoolinput = (params) => { |
| 157 | + console.log("Tool args:", params.arguments); |
| 158 | +}; |
| 159 | + |
| 160 | +app.ontoolresult = (params) => { |
| 161 | + console.log("Tool result:", params.content); |
| 162 | +}; |
| 163 | + |
| 164 | +app.onhostcontextchanged = (ctx) => { |
| 165 | + if (ctx.theme) applyTheme(ctx.theme); |
| 166 | +}; |
| 167 | + |
| 168 | +// Connect (auto-detects OpenAI vs MCP) |
| 169 | +await app.connect(); |
| 170 | + |
| 171 | +// Access context |
| 172 | +const theme = app.getHostContext()?.theme; |
| 173 | + |
| 174 | +// Call a tool |
| 175 | +const result = await app.callServerTool({ |
| 176 | + name: "get_weather", |
| 177 | + arguments: { city: "Tokyo" }, |
| 178 | +}); |
| 179 | + |
| 180 | +// Send a message |
| 181 | +await app.sendMessage({ |
| 182 | + role: "user", |
| 183 | + content: [{ type: "text", text: "Weather updated!" }], |
| 184 | +}); |
| 185 | + |
| 186 | +// Open link (note: url not href) |
| 187 | +await app.openLink({ url: "https://example.com" }); |
| 188 | +``` |
| 189 | + |
| 190 | +## Key Differences Summary |
| 191 | + |
| 192 | +1. **Initialization**: OpenAI is implicit; MCP requires `new App()` + `await app.connect()` |
| 193 | +2. **Data Flow**: OpenAI pre-populates; MCP uses async notifications (register handlers before `connect()`) |
| 194 | +3. **Auto-resize**: MCP has built-in ResizeObserver support via `autoResize` option |
| 195 | +4. **Structured Content**: MCP uses `{ type: "text", text: "..." }` arrays for messages |
| 196 | +5. **Context Changes**: MCP pushes updates via `onhostcontextchanged`; no polling needed |
| 197 | +6. **Capabilities**: MCP lets you check what the host supports before calling methods |
0 commit comments