Comparisons
MCP Apps vs MCP-UI vs OpenAI Apps SDK: which one to build on
Three names for the same idea: an interactive UI that a tool call draws inside a chat. In 2026 one of them is the standard and the other two are where it came from.
6 min read
#The short answer
MCP Apps is the standard; MCP-UI and the OpenAI Apps SDK are the two projects it grew out of. If you are comparing MCP Apps vs MCP-UI, or MCP Apps vs ChatGPT apps, build to MCP Apps in 2026: it is an official MCP extension, ChatGPT implements it, Claude, VS Code and Microsoft 365 Copilot render it, and MCP-UI now emits it. Use the ChatGPT-only extensions on top of it when you need them, never instead of it.
The rest of this page explains where each one came from, how they differ field by field, and what to do with code you already wrote for either of the older two.
#Where each one came from
MCP-UI came first. It was an open-source community project, led by Ido Salomon and Liad Yosef, that showed MCP tools could return interactive HTML instead of plain text. Its server SDKs covered TypeScript, Python and Ruby, and it was adopted by teams such as Postman, Hugging Face, Shopify, Goose and ElevenLabs (they are credited in the MCP Apps spec). Its site now says plainly that "MCP-UI is now standardized into MCP Apps" (mcpui.dev).
The OpenAI Apps SDK was OpenAI's way to put interfaces inside ChatGPT: an MCP server, an HTML template served as text/html+skybridge, and a window.openai object in the page. As of October 2026 OpenAI calls ChatGPT apps "plugins" and documents them under developers.openai.com/plugins. Many tutorials from late 2025 still teach the skybridge path.
MCP Apps is the merge. The spec, SEP-1865, was proposed on 21 November 2025 by authors from MCP-UI, OpenAI and Anthropic, and became the "first official MCP extension" on 26 January 2026 (MCP blog). Its extension id is io.modelcontextprotocol/ui. On 22 February 2026 OpenAI wrote that "ChatGPT is now fully compatible with the MCP Apps spec" (changelog).
If you want the concepts behind it first, start with what MCP Apps are.
#MCP Apps vs MCP-UI vs Apps SDK, side by side
| MCP Apps | MCP-UI | OpenAI Apps SDK | |
|---|---|---|---|
| Owner | MCP project (official extension) | Community project, now part of MCP Apps | OpenAI |
| Status in 2026 | Stable since 26 Jan 2026 | Standardized into MCP Apps; old content types are legacy | Still works; ChatGPT implements MCP Apps and keeps its own fields as aliases |
| MIME type | text/html;profile=mcp-app | Now text/html;profile=mcp-app. Legacy: rawHtml, externalUrl (text/uri-list), remoteDom (application/vnd.mcp-ui.remote-dom) | text/html+skybridge |
| How the tool points to the UI | _meta.ui.resourceUri on the tool, a ui:// resource | Legacy: the tool returned the UI resource in its result. Now: _meta.ui.resourceUri | _meta["openai/outputTemplate"] |
| CSP fields | _meta.ui.csp: connectDomains, resourceDomains, frameDomains, baseUriDomains | Now the MCP Apps fields | openai/widgetCSP: connect_domains, resource_domains, frame_domains |
| Bridge API | JSON-RPC over postMessage (ui/initialize, ui/notifications/tool-result, tools/call, ui/message); App class in @modelcontextprotocol/ext-apps | Now the MCP Apps bridge; AppRenderer in @mcp-ui/client for hosts | window.openai (callTool, widgetState, requestModal...) |
| Hosts that render it | Claude (web and desktop), ChatGPT, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, Cursor and more (client matrix) | MCP Apps hosts, plus legacy MCP-UI hosts for the old types | ChatGPT |
Two things stand out. First, the MCP Apps fields are almost a one-to-one rename of the OpenAI ones, which is why migrating is mostly mechanical. Second, the only host that reads the skybridge fields is ChatGPT, while the MCP Apps fields reach every host in the client matrix, ChatGPT included.
#What if I have MCP-UI code?
Upgrade the packages and check which content type you used.
createUIResourcein@mcp-ui/servernow produces a resource with the MIME typetext/html;profile=mcp-app. If you served HTML, upgrading moves you onto the standard with little else to change.- Make the tool point to the resource with
_meta.ui.resourceUriand serve the HTML from aui://resource handler, instead of returning the UI inside the tool result. That is the MCP Apps pattern: the tool definition names its UI, and the host reads the resource separately. externalUrlandremoteDomare legacy. An external URL becomes a self-contained HTML page (or an iframe declared inframeDomains, which some hosts restrict). A Remote DOM view needs to be rebuilt as an HTML view.- If you build a host rather than a server,
AppRendererin@mcp-ui/clientis the renderer MCP-UI recommends, and the official@modelcontextprotocol/ext-apps/app-bridgeis the other option.
#What if I have OpenAI Apps SDK code?
Keep it running and move it field by field. The official migration guide maps each piece:
| Apps SDK | MCP Apps |
|---|---|
openai/outputTemplate | ui.resourceUri |
text/html+skybridge | text/html;profile=mcp-app |
openai/widgetCSP (connect_domains, resource_domains, frame_domains) | ui.csp (connectDomains, resourceDomains, frameDomains) |
openai/widgetDomain | ui.domain |
openai/widgetPrefersBorder | ui.prefersBorder |
openai/widgetAccessible, openai/visibility | ui.visibility |
window.openai.callTool | app.callServerTool |
ChatGPT still honors openai/outputTemplate as a compatibility alias, and openai/visibility was deprecated on 21 July 2026. The official repo also ships a migrate-oai-app agent skill that does the conversion with you. The step-by-step version, including the view code, is in ChatGPT plugin UI with MCP Apps.
#What stays host-specific
The standard covers the common ground: the resource, the handshake, tool input and results, calling tools, opening links, sending messages, display modes and theme. Some things remain per host:
- ChatGPT extensions.
requestCheckout,requestModal,uploadFile,selectFiles,getFileDownloadUrlandwidgetState/setWidgetStatelive onwindow.openai. OpenAI's own advice is to get the MCP Apps flow working first and usewindow.openai"only for capabilities that the shared specification does not cover", with feature detection and a fallback (OpenAI docs). - The sandbox domain.
ui.domainis host-specific (for example a hash underclaudemcpcontent.comon Claude, or a name underoaiusercontent.comon ChatGPT). - Nested frames. Claude restricts
frameDomainspending security review, and ChatGPT blocks nested frames by default. See MCP Apps CSP explained. - Look and feel. Each host passes its own CSS variables, fonts and theme in the host context; your view should read them rather than hardcode colors. More in theming MCP Apps.
- Where it renders at all. Text-only clients such as Claude Code in the terminal call the tool and show its text content. Which clients draw the UI is covered in MCP Apps clients.
#Which one should I use?
- Starting from scratch: MCP Apps. Use
@modelcontextprotocol/ext-apps(registerAppTool,registerAppResource, theAppclass), or a framework on top of it. See build an MCP App in TypeScript. - You only target ChatGPT and need checkout, modals or files: still MCP Apps for the base, with those
window.openaiextensions layered on and feature-detected. - You have an Apps SDK app in production: migrate field by field; the aliases give you time, but new UI should use the shared fields.
- You have MCP-UI code: upgrade
@mcp-ui/serverand move to_meta.ui.resourceUri; dropexternalUrlandremoteDom. - You build a host: implement MCP Apps with the official app bridge or MCP-UI's
AppRenderer. - You want the UI without writing the view: the builders and frameworks in MCP Apps builders compared all target MCP Apps.
Widgetry is one of those: it produces standard MCP Apps widgets (the text/html;profile=mcp-app document plus a manifest with _meta.ui.resourceUri and _meta.ui.csp) from a template and a design, previews them in a real MCP Apps host, and lets your server link to them. It never uses skybridge or window.openai, so the same widget renders in ChatGPT, Claude and the other hosts.