Window.openai Patterns
Load this reference when a task needs ChatGPT-only widget features, when translating older examples that use an app wrapper, or when a React widget should read host globals safely.
Core Rule
- Build baseline widget behavior on the MCP Apps bridge:
ui/*notifications,tools/call,ui/message, andui/update-model-context. - Use
window.openaionly when the task specifically benefits from ChatGPT-only runtime conveniences. - Treat
window.openaias additive. The app should still have a coherent baseline path on the MCP Apps standard when possible.
Canonical window.openai Surface
State And Data
window.openai.toolInput: tool arguments supplied by the hostwindow.openai.toolOutput: currentstructuredContentwindow.openai.toolResponseMetadata: current_metapayload (widget-only)window.openai.widgetState: persisted widget-local snapshotwindow.openai.setWidgetState(state): persist widget-local snapshot after meaningful UI changes
Runtime APIs
window.openai.callTool(name, args): call another MCP tool from the widgetwindow.openai.sendFollowUpMessage({ prompt, scrollToBottom? }): ask ChatGPT to post a widget-authored follow-up messagewindow.openai.openExternal({ href, redirectUrl? }): open an external URL through ChatGPT's vetted flowwindow.openai.requestDisplayMode({ mode }): requestinline,pip, orfullscreenwindow.openai.requestModal({ params, template? }): open a host-owned modalwindow.openai.requestClose(): ask ChatGPT to close the widgetwindow.openai.uploadFile(file, options?): upload a file from the widgetwindow.openai.selectFiles(): open ChatGPT's file library picker and return app-authorized fileswindow.openai.getFileDownloadUrl({ fileId }): resolve a temporary download URLwindow.openai.notifyIntrinsicHeight(...): report dynamic height changeswindow.openai.setOpenInAppUrl({ href }): override the fullscreen punch-out target
Context Signals
window.openai.themewindow.openai.displayModewindow.openai.maxHeightwindow.openai.safeAreawindow.openai.viewwindow.openai.userAgentwindow.openai.locale
Mapping From Repo Wrapper Examples
app.callServerTool({ name, arguments }): Usewindow.openai.callTool(name, args)when you intentionally want the ChatGPT compatibility layer. Usetools/callover the bridge when you want the portable MCP Apps path.app.sendMessage(...): Useui/messagefor portable bridge messaging. If the task is intentionally ChatGPT-specific,window.openai.sendFollowUpMessage({ prompt })is the closest supported path.app.updateModelContext(...): Useui/update-model-contextover the bridge. This is part of the standard bridge, not awindow.openaifeature.app.openLink({ url }): Usewindow.openai.openExternal({ href: url })when you intentionally want ChatGPT's external navigation flow.app.requestDisplayMode({ mode }): Usewindow.openai.requestDisplayMode({ mode }).app.getHostContext(): Read the documented globals directly (theme,displayMode,locale,maxHeight,safeArea,userAgent).app.getHostCapabilities()/app.getHostVersion(): These are wrapper-level convenience APIs. Prefer feature detection (if (window.openai?.requestModal)) and the documented globals instead of teaching these as the primary public surface.
File Patterns
- Use
window.openai.uploadFile(file)when the user is adding a new local file inside the widget. - Use
window.openai.uploadFile(file, { library: true })when the upload should also be saved into the user's ChatGPT file library. - Use
window.openai.selectFiles()when the user should be able to reuse files that are already in their ChatGPT file library instead of uploading again. - Use
window.openai.getFileDownloadUrl({ fileId })when the widget needs a temporary URL for previewing a file or forwarding it through a file-param payload. - Feature-detect these helpers in the widget (
if (window.openai?.selectFiles)) and provide a fallback upload flow when a ChatGPT-only helper is unavailable.
React Helper Extraction
- The repo's
src/use-openai-global.tsis a good baseline for subscribing to host global changes without scattering directwindow.openaireads through components. - The repo's
src/use-widget-state.tsis a good baseline for mirroring React state intowindow.openai.setWidgetState(...). - The repo's
src/use-widget-props.tsis a good baseline for reading typedtoolOutputwith a local fallback. - Keep these helpers optional. Do not force a React abstraction when a simple vanilla widget is enough.