MCP Apps vs A2UI vs AG-UI vs OpenAI Apps SDK

Three of these four are not competitors, and picking by feature list gets you the wrong one. They split on a single fact — whether executable code crosses the wire — and that fact, not any feature, decides which surfaces your interface can ever appear on. The genuine either/or is MCP Apps against A2UI: an HTML document in a locked-down iframe, or a declarative component list the client draws with widgets you do not control.

By Agentic AI Wiki 15 min read

Pick between these four on their feature lists and you will choose the wrong one, because only two of them are actually alternatives. They divide on a single fact — whether executable code crosses the wire — and that one fact, not any feature, decides whether the interface your agent draws can ever appear anywhere but a browser. Decide where your UI must render first; the standard follows from that, and three of the four can then be used together.

At a glance

Four specifications that all get described as "letting agents build UI", read against the one axis that separates them.

ProjectStewardStatusWhat crosses the wire
MCP Apps (SEP-1865)Model Context ProtocolSpec 2026-01-26 stable; draft openAn HTML5 document
OpenAI Apps SDKOpenAIShipping in ChatGPTAn HTML document plus a host API
A2UIGooglev0.9.1 stable, v1.0 release candidate, "early stage public preview"Declarative JSON
AG-UIag-ui-protocol, from CopilotKit's work with LangChain and CrewAIMIT, ~16 standard event typesEvents — no interface at all

Read that last column twice. It is the whole comparison. Two of these ship something a browser runs, one ships something a renderer interprets, and one ships neither because it is a transport that happens to live in the same conversations.

Where each agent-interface standard leans A four by four matrix. MCP Apps ships executable markup, is host-independent where the extension is negotiated, is web-only, and needs no frontend of your own. The OpenAI Apps SDK ships executable markup, is tied to one host, is web-only, and needs no frontend of your own. A2UI ships data only, is host-independent, reaches non-web renderers, and constrains you to a catalogue. AG-UI ships no interface at all, is host-independent, is renderer-agnostic, and requires you to build the frontend. What each one actually standardises Ships runnable UI Host-independent Non-web renderers You build the UI MCP Apps HTML document If negotiated Web only No OpenAI Apps SDK HTML document ChatGPT Web only No A2UI Data only Yes Flutter, SwiftUI Catalogue only AG-UI Nothing Yes Agnostic All of it Leans hardest Partly Not at all
The two rows that look most alike in a feature list — MCP Apps and the Apps SDK — differ on exactly one column, and it is the one that decides portability.

The question that sorts them

Two execution models for agent-drawn interfaces Who executes the interface Code travels — MCP Apps, OpenAI Apps SDK Tool server owns the markup HTML5 document ui:// resource Host fetches may cache it Sandboxed iframe separate origin Default policy is deny: no network, no nested frames, until the server declares the origins it needs. Data travels — A2UI Agent owns the intent Component list declarative JSON Client reads validates IDs Native widgets from its catalogue Nothing executable crosses the wire, and nothing outside the catalogue can be drawn. AG-UI — an event stream, not a component model carries either payload above; around sixteen event types over any transport
Upper lane: the server decides what the pixels do. Lower lane: the client does, and the server only decides what they mean.

Ask of any agent-UI proposal: when the agent wants a date picker, does it send a date picker or does it send the word "date picker"? Everything else follows. Send the picker and you get pixel control, arbitrary libraries, and animation, at the cost of needing a JavaScript runtime at the far end and a host willing to run your code. Send the word and you get a UI that renders on a phone, a watch, or a car dashboard, at the cost of being able to express only what the far end already knows how to draw.

This is not a new trade-off; it is the one email clients, terminal escape codes, and native mobile SDKs all made before. What is new is that both answers now have a 2026 specification behind them, which means the choice is live rather than theoretical, and it is being made by teams reading feature comparisons that put the two in the same column.

The test that actually decides it: write down every surface your product must support in eighteen months. If that list is "a chat client we do not own", you want MCP Apps. If it contains anything without a browser in it, the executable lane is closed to you and no amount of feature parity reopens it.

MCP Apps — deep dive

The mechanism

A tool declares _meta.ui.resourceUri pointing at a resource the same server hosts. That resource MUST use the ui:// scheme and the MIME type text/html;profile=mcp-app, and its content MUST be a valid HTML5 document. The host reads it with an ordinary resources/read and renders it in a sandboxed iframe; if the host is itself a web page, the spec requires an intermediate sandbox proxy, and the host and sandbox MUST have different origins. Communication is JSON-RPC over postMessage, with a handshake of ui/initialize then ui/notifications/initialized, after which the host sends ui/notifications/tool-input and later ui/notifications/tool-result.

The part teams miss

The content security policy is deny-by-default and your app is the thing that has to ask. Omit ui.csp entirely and the host applies default-src 'none' with connect-src 'none'. Leave connectDomains empty and you get no external connections; leave frameDomains empty and you get no nested iframes. The host MUST construct the policy from what you declared, MAY restrict it further, and MUST NOT allow a domain you did not declare. Every origin counts, including wherever your bundled JavaScript and CSS are served from — which is why the practical answer is to inline the bundle rather than discover this in a host that does not log policy failures to you.

The normative sentence worth printing out

Tools MUST return a meaningful content array even when UI is available, and servers SHOULD provide text-only fallback behaviour for every UI-enabled tool. The extension is optional and negotiated under io.modelcontextprotocol/ui; a host that does not support it treats your tool as an ordinary tool. So the text path is not a graceful degradation you add in the second sprint. It is the contract, and the interface is an enhancement on top of it. Teams that build the widget first and the text result later ship a tool that is broken in every host that has not adopted the extension — which, for the foreseeable future, is most of them.

Reach

At announcement in January 2026 the supported hosts were Claude on web and desktop, Goose, VS Code Insiders, and ChatGPT rolling out that week. The MCP blog credits the work to a partnership with OpenAI and the MCP-UI project, whose patterns were folded into the official extension; MCP-UI continues to be maintained and its client SDK is what several hosts actually ship. One honest caveat from the repository itself: it contains no production host implementation beyond a basic example, so host behaviour is a per-host fact, not a spec fact.

A2UI — deep dive

The mechanism

The agent emits a flat list of components referenced by ID, as JSON, and the client draws each one with a widget from a catalogue the client maintains. The README is unusually clear about the intent: A2UI "is a declarative data format, not executable code", agents "can only request components from a client-maintained catalog of pre-approved components", and the design goal is to be "safe like data, but expressive like code". A flat list rather than a tree is a deliberate concession to language models, which generate a list incrementally far more reliably than they generate correctly-nested markup.

What you buy

Renderers that are not browsers. Web renderers exist for Lit, Angular and React, with Dart packages published for Flutter and a SwiftUI renderer in the tree but not yet released; Python packages are on PyPI. Because nothing executable crosses the wire, the security review is a schema review — the worst an agent can do is request a component that does not exist, which the client rejects. That is a materially different conversation from "we will run a third party's HTML in an iframe", and in regulated environments it is sometimes the only conversation available.

What you give up

Anything the catalogue cannot draw. The README's examples are Card, Button and TextField. If your interface needs a candlestick chart with a brushable time axis, you are not sending it; you are either adding it to every client's catalogue or abandoning it. That is the cost of the safety property, and it is permanent rather than a gap that closes with v1.0. Treat the catalogue as a design system you now have to govern across every renderer you ship.

Status

v0.9.1 is the current production release, v1.0 is a release candidate, and the project describes itself as "early stage public preview" with the specification and implementations "functional but still evolving". Several packages — the TypeScript agent SDK, the Flutter renderer and CLI, the A2A and ADK connectors — are usable from source but not published. Build on it with the expectation of tracking the repository, not a version pin.

AG-UI — deep dive

Why it is not in the same race

AG-UI calls itself "an open, lightweight, event-based protocol that standardizes how AI agents connect to user-facing applications", and its own framing of the landscape is the clearest statement of scope anyone has published: "MCP gives agents tools", "A2A allows agents to communicate with other agents", "AG-UI brings agents into user-facing applications". It defines around sixteen standard event types and works over any event transport — SSE, WebSockets, webhooks. It does not define a component, a layout, or a pixel. You still write the frontend.

What that gets you

The unglamorous half of the problem, which is also the half most teams underestimate: streaming token deltas, tool-call start and end, state patches, run lifecycle, and interruption, all with one wire shape instead of a bespoke event envelope per backend. The integration list is the strongest argument for it — LangChain, CrewAI, Microsoft Agent Framework, Google ADK, AWS Strands, Mastra, Pydantic AI, Agno, LlamaIndex, Amazon Bedrock AgentCore, with community integrations including the Claude Agent SDK and Cloudflare Agents, and SDKs for Kotlin, Go, Dart, Java, Rust, Ruby, C++ and .NET. MIT licensed.

The composition that matters

A2UI lists AG-UI as a compatible transport, alongside A2A. So "AG-UI or A2UI" is a category error: the normal arrangement is AG-UI carrying A2UI payloads, with your own components doing the drawing. If you are building your own product surface rather than publishing into someone else's client, that pairing — plus the in-app patterns in embedding an agent in an existing app — is very likely the whole answer.

OpenAI Apps SDK — deep dive

The mechanism

Also built on MCP, also an HTML document in an iframe, and in shape the nearest thing to MCP Apps of the four. The difference is the host API. Inside a ChatGPT widget your code reads toolInput, toolOutput, displayMode, theme and widgetState from window.openai, writes with setWidgetState, invokes tools with callTool, and reaches the host with requestDisplayMode, openExternal and sendFollowUpMessage. The examples repository presents that as the full surface and frames the whole thing as building "apps for ChatGPT", with setup steps that run through ChatGPT developer mode and connector settings.

How to read the overlap

Both the MCP blog and OpenAI's own materials describe the Apps SDK as MCP-based, and the MCP Apps announcement credits OpenAI's involvement in building the shared extension. The useful mental model is that the component model converged and the host API did not: the same HTML-in-an-iframe shape, reached through window.openai in ChatGPT and through the standardised ui/ JSON-RPC methods elsewhere. Code written against one host API does not run under the other without a shim, and the shim is the only part of this you will be maintaining in a year.

When it is the right pick anyway

When distribution in ChatGPT is the point. One client with an enormous installed base and host capabilities the generic extension does not define is a legitimate reason to accept host coupling — the same reason people shipped iOS-only apps for years. Just budget for it as a port, not a build, and keep the server's tool results fully usable as text so the same server can serve a generic MCP host later. Which, usefully, is also what the MCP Apps spec requires of you.

Cross-cutting comparison

Portability

Rank the four by how far a working interface travels, and the ordering inverts the usual "which is more capable" ranking. AG-UI travels furthest because it carries no interface to be incompatible. A2UI travels next because its payload is data that any renderer with a matching catalogue can draw, including renderers with no browser in them. MCP Apps travels across hosts but only hosts that negotiate the extension and only hosts that are web-capable. The Apps SDK travels within ChatGPT. Every step up that ladder costs you expressiveness; every step down costs you reach.

Security posture

The two executable options are honest about running untrusted code and spend their specification text on confinement — different origins, mandatory sandbox attributes, deny-by-default policy, a host that proxies every tool call and MUST reject calls to tools whose visibility does not include "app". The declarative option avoids the problem by construction: no code, so no confinement needed, and the attack surface shrinks to schema validation and whatever your own widgets do with the data. Neither posture is strictly safer in practice, because the executable path's confinement is real and the declarative path's widgets are still yours to get wrong — but they fail differently, and the reviews they require are staffed by different people.

Who holds the model's context

All four eventually have to answer "what does the model learn from what the user did in the interface", and only MCP Apps specifies it tightly: ui/update-model-context, where each request overwrites the previous context from that view, and the host SHOULD send only the last one if several arrive before the next user message. The Apps SDK does it through widget state and follow-up messages. A2UI moves it through a data model the agent reads back. AG-UI leaves it to you. If you have ever debugged an agent that confidently described a form the user had already changed, this is the field to read first. The underlying failure is the one described in context engineering: the model's picture of the world is whatever last got written into it.

When to pick which

SituationPickBecause
Publishing a tool server into chat clients you do not ownMCP AppsThe only option with cross-host negotiation and a text fallback mandated by spec
Distribution inside ChatGPT is the business caseApps SDK, same serverHost capabilities the generic extension does not define; keep tool results text-usable
Your surfaces include mobile, desktop or embeddedA2UIData crosses the wire, so a non-browser renderer is possible at all
Regulated environment that will not run third-party markupA2UISchema review instead of sandbox review; catalogue is a hard ceiling by design
Your own app, your own components, many agent backendsAG-UIOne event shape across frameworks; pair with A2UI if the agent should choose components
You cannot write the list of screens downRead generative UI patterns firstThe standard is the easy decision; whether to let the agent compose at all is the hard one

If you are shipping a UI into a host someone else operates, the standard is about a week of work and the operational consequences last for years — cache behaviour you do not control, a policy that blocks your telemetry by default, and a capability set that varies per host. We wrote those up separately in shipping UI into a host you do not control.

FAQ

Is MCP Apps the same thing as MCP-UI?

No, but they are directly related. MCP-UI was the community project by Ido Salomon and Liad Yosef whose patterns were standardised into MCP Apps, the first official MCP extension. MCP-UI continues to be maintained, and its client SDK is what several hosts use to implement the official extension.

Can I use AG-UI and A2UI together?

Yes, and that is the intended arrangement. The A2UI repository lists AG-UI and A2A as compatible transports. AG-UI carries the event stream; A2UI describes the components inside it; your renderer draws them.

Does an MCP App have network access?

Not by default. If ui.csp is omitted the host applies default-src 'none' and connect-src 'none'; an empty or absent connectDomains means no external connections. The server declares the origins it needs and the host builds the policy from that declaration, and it must not permit anything undeclared.

What happens in a host that does not support the UI extension?

Your tool is treated as an ordinary tool. The extension is negotiated under io.modelcontextprotocol/ui, servers SHOULD check client capabilities before registering UI-enabled tools, and every UI-enabled tool MUST still return a meaningful content array. In other words the text result is the contract and the interface is the enhancement.

Is A2UI ready for production?

Its own README calls it "early stage public preview" and warns the specification and implementations are still evolving, with v0.9.1 as the production release and v1.0 a release candidate. Several packages are unpublished and used from source. Treat it as a tracked dependency rather than a pinned one.

Which one does the model see?

In MCP Apps, visibility is explicit: _meta.ui.visibility defaults to ["model", "app"], hosts MUST NOT list a tool for the model when visibility excludes "model", and MUST reject calls from an app to a tool that excludes "app". That gives you app-only tools the model cannot invoke, which is the right home for anything the interface needs and the model should not be able to trigger on its own.

Further reading

On this wiki:

Project sources: