Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4ea90d1
feat: experimental OpenAI Apps SDK compatibility
ochafik Dec 16, 2025
28a3924
feat: add cross-platform support for OpenAI Apps SDK
ochafik Dec 16, 2025
38e098c
test: update Three.js golden snapshot
ochafik Dec 17, 2025
c4ac06e
Fix Unknown message type error for tool notifications
ochafik Dec 17, 2025
4e1380a
Add tests for notification handler fixes
ochafik Dec 17, 2025
f10e178
Fix null toolOutput being sent as text 'null'
ochafik Dec 17, 2025
e554054
Fix double-stringification of toolOutput in OpenAI transport
ochafik Dec 17, 2025
a5ad9e4
Add structuredContent support to OpenAI transport
ochafik Dec 17, 2025
35dc00a
style: format OpenAI transport
ochafik Jan 7, 2026
d6048d8
fix: include autoResize option in useApp hook
ochafik Jan 8, 2026
c261e67
feat: experimental OpenAI Apps SDK compatibility
ochafik Dec 16, 2025
5014876
feat: add cross-platform support for OpenAI Apps SDK
ochafik Dec 16, 2025
1df66a3
test: update Three.js golden snapshot
ochafik Dec 17, 2025
32cfe58
Fix Unknown message type error for tool notifications
ochafik Dec 17, 2025
c6d5a6d
Add tests for notification handler fixes
ochafik Dec 17, 2025
ca12b5b
Fix null toolOutput being sent as text 'null'
ochafik Dec 17, 2025
b320fda
Fix double-stringification of toolOutput in OpenAI transport
ochafik Dec 17, 2025
d0a9096
Add structuredContent support to OpenAI transport
ochafik Dec 17, 2025
e3acd70
style: format OpenAI transport
ochafik Jan 7, 2026
a171447
fix: include autoResize option in useApp hook
ochafik Jan 8, 2026
3862794
docs: add OpenAI to MCP Apps SDK migration guide
ochafik Jan 13, 2026
6d2bac5
feat(openai): Add widget state management and file operations
ochafik Jan 13, 2026
e97a571
fix(openai): Use main's request-based updateModelContext API
ochafik Jan 13, 2026
e119d64
feat(openai): Forward runtime context changes and add safe area handling
ochafik Jan 14, 2026
7b4f76d
feat(examples): Add debug-server for comprehensive SDK testing
ochafik Jan 14, 2026
439d562
docs(examples): Add README for debug-server
ochafik Jan 14, 2026
e1fd055
feat(debug-server): Add file logging with debug-log tool
ochafik Jan 14, 2026
2d0f1cc
update
ochafik Jan 14, 2026
06af4d3
style: formatting fixes
ochafik Jan 15, 2026
786cf5d
Merge origin/main into ochafik/openai-compatibility
ochafik Jan 15, 2026
a7e8dd4
chore: update package-lock.json after merge
ochafik Jan 15, 2026
a933249
Merge remote-tracking branch 'origin/main' into ochafik/openai-compat…
ochafik Jan 15, 2026
e0a587c
Merge origin/ochafik/openai-compatibility
ochafik Jan 15, 2026
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
198 changes: 198 additions & 0 deletions docs/openai-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# 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 explicitly |
| — | `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 |
| Returns `{ structuredContent?, content?, isError? }` | Returns `{ content, structuredContent?, isError? }` | Same shape, slightly different ordering |

## 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` | `app.onwidgetstate = (params) => { params.state }` | MCP uses notification callback |
| `window.openai.setWidgetState(state)` | `app.updateModelContext({ modelContent, privateContent, imageIds })` | MCP uses structured format |

## File Operations

| OpenAI | MCP Apps | Notes |
| ---------------------------------------------------- | ------------------------------------------ | -------------------- |
| `await window.openai.uploadFile(file)` | `await app.uploadFile(file)` | Returns `{ fileId }` |
| `await window.openai.getFileDownloadUrl({ fileId })` | `await app.getFileDownloadUrl({ fileId })` | Returns `{ url }` |

## 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
55 changes: 55 additions & 0 deletions examples/debug-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Debug Server

A comprehensive testing/debugging tool for the MCP Apps SDK that exercises every capability, callback, and result format combination.

## Tools

### debug-tool

Configurable tool for testing all result variations:

| Parameter | Type | Default | Description |
| -------------------------- | ----------------------------------------------------------------------------------- | -------- | ------------------------------------------- |
| `contentType` | `"text"` \| `"image"` \| `"audio"` \| `"resource"` \| `"resourceLink"` \| `"mixed"` | `"text"` | Content block type to return |
| `multipleBlocks` | boolean | `false` | Return 3 content blocks |
| `includeStructuredContent` | boolean | `true` | Include structuredContent in result |
| `includeMeta` | boolean | `false` | Include \_meta in result |
| `largeInput` | string | - | Large text input (tests tool-input-partial) |
| `simulateError` | boolean | `false` | Return isError: true |
| `delayMs` | number | - | Delay before response (ms) |

### debug-refresh

App-only tool (hidden from model) for polling server state. Returns current timestamp and call counter.

## App UI

The debug app provides a dashboard with:

- **Event Log**: Real-time log of all SDK events with filtering
- **Host Info**: Context, capabilities, container dimensions, styles
- **Callback Status**: Table of all callbacks with call counts
- **Actions**: Buttons to test every SDK method:
- Send messages (text/image)
- Logging (debug/info/warning/error)
- Model context updates
- Display mode requests
- Link opening
- Resize controls
- Server tool calls
- File operations

## Usage

```bash
# Build
npm run --workspace examples/debug-server build

# Run standalone
npm run --workspace examples/debug-server serve

# Run with all examples
npm start
```

Then open `http://localhost:8080/basic-host/` and select "Debug MCP App Server" from the dropdown.
Loading
Loading