Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions docs/migrate_from_openai_apps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# 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](https://github.com/modelcontextprotocol/ext-apps/pull/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)

```typescript
// 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)

```typescript
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

1. **Initialization**: OpenAI is implicit; MCP requires `new App()` + `await app.connect()`
2. **Data Flow**: OpenAI pre-populates; MCP uses async notifications (register handlers before `connect()`)
3. **Auto-resize**: MCP has built-in ResizeObserver support via `autoResize` option
4. **Structured Content**: MCP uses `{ type: "text", text: "..." }` arrays for messages
5. **Context Changes**: MCP pushes updates via `onhostcontextchanged`; no polling needed
6. **Capabilities**: MCP lets you check what the host supports before calling methods
Loading