Comparison · Agent hosts
ChatGPT Apps SDK vs. Claude MCP Apps
ChatGPT and Claude both render the MCP Apps standard. The only comparison worth making is how to avoid building the same widget twice.
Last verified . Hosts and protocols move fast, so details here may be stale. Check the primary sources before you build.
The widget rendered perfectly in ChatGPT and came up blank in Claude, and the MCP server behind it was correct the whole time. Nothing in the protocol failed. The component had quietly assumed window.openai and ChatGPT's exact viewport, and assumptions like that only surface when a widget leaves home.
An awkward premise for a comparison post, but honest: the differences are things you adapt to rather than rebuild for.
The portable center is most of the app
Strip either product to its contract and you find the same six parts:
- an MCP server exposing standard tool and resource primitives;
- a resource URI on the
ui://scheme; - a tool descriptor linking the UI through
_meta.ui.resourceUri; - HTML served as
text/html;profile=mcp-app; - a sandboxed iframe speaking JSON-RPC with the host;
- tool results whose structured data updates the component.
That layer is the MCP Apps standard, and it is where business logic, data contract, and approval flow should live. Hosts change independently; the official client matrix is the place to check current support.
ChatGPT adds a surface, then tells you not to lean on it
On top of the standard, ChatGPT exposes optional APIs through window.openai: host context, plus interaction patterns specific to the ChatGPT product. OpenAI's own guidance is the interesting part: build on the standard ui/* bridge first, feature-detect the extras, degrade gracefully when they are absent. The vendor shipping the proprietary API tells you to treat it as optional.
Followed honestly, that gives a clean dependency direction: core tool calls and widget updates depend on MCP Apps; a small adapter depends on ChatGPT. If the adapter disappears tomorrow, the workflow keeps working.
Claude varies where hosts are allowed to vary
Claude is a compliant MCP Apps host. What changes is everything the standard leaves to hosts: frame dimensions, the surrounding conversation chrome, the authentication journey, the timing of UI updates. A component that hardcodes ChatGPT's viewport, or reaches for its globals without checking, can render blank or cramped in Claude while its server behaves flawlessly.
The failure mode is accidental host coupling, not a missing rewrite. Keep business logic on the server, make calls through the standard bridge, keep the layout responsive, and test the actual widget in both hosts.
When a host feature tempts you, the question is narrow.
Start with the shared layer when
- the same workflow should appear in ChatGPT, Claude, and future MCP Apps clients;
- the component mainly renders tool results and invokes MCP tools;
- one authorization and policy path matters more than a host novelty.
Add a host enhancement when
- the capability materially improves the task itself;
- you can feature-detect it without touching the core data contract;
- the fallback stays complete and understandable on its own.
Seven verbs decide the comparison
Screenshots are the wrong test. What matters in production is whether both hosts can discover the tool, read the UI resource, authenticate the same user, render the result, invoke the confirmed action, recover from a reload, and show a useful error. Pass those seven and the two hosts have nowhere left to drift apart. The concrete checklist lives in How to make MCP widgets work across ChatGPT and Claude; the app pages in the portfolio let you switch the surrounding host chrome to inspect exactly these assumptions.
My default has not moved: one server, one widget bundle, one portable approval flow, thin adapters for whatever a host offers on top. Every widget you build twice is a divergence you maintain forever. The cheapest second implementation is the one that never exists.