docs: how to migrate from openai apps (#260)
* Create migrate_from_openai_apps.md * Update AGENTS.md * Update docs/migrate_from_openai_apps.md Co-authored-by: Jonathan Hefner <jonathan@hefner.pro> * address PR review comments - Remove AGENTS.md reference to migration guide - Add note that OpenAITransport is not yet available (PR #172) - Remove unnecessary note about return type ordering --------- Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
This commit is contained in:
parent
574a768e09
commit
152b953ae6
1 changed files with 197 additions and 0 deletions
197
docs/migrate_from_openai_apps.md
Normal file
197
docs/migrate_from_openai_apps.md
Normal file
|
|
@ -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…
Reference in a new issue