posts/a2ui-ag-ui-mcp-landscape.ts
A2UI vs. AG-UI vs. MCP Apps: the map nobody draws properly
// · 7 min read · Architecture
If you’re working on agent-generated UI right now, you have a problem that has nothing to do with code: nobody explains what is what. A2UI, AG-UI, A2A, MCP Apps, Generative UI. Five terms that show up in every other blog post and get used incorrectly in every third one. Even CopilotKit and Google dedicate entire doc sections to the question “Confused about A2UI and AG-UI? That’s understandable!”
Mix up the terms and you’ll make the wrong architecture decisions. So here’s the map I wish I’d had before I started working through this topic for Angular.
Four protocols, four problems
There are four specs in this space. They solve different problems at different layers.
A2UI (Agent-to-UI) is Google’s declarative UI spec protocol. The agent describes as JSON what the user should see: components, data, layout. The design decision you need to understand: A2UI uses a flat adjacency list instead of a nested component tree. That sounds like an implementation detail, but it’s not. A flat list can be streamed incrementally. Individual components can be corrected without regenerating the entire tree. Data binding runs through JSON Pointers (RFC 6901). Current status: v0.9.1 stable, v1.0 as release candidate. Around 15,000 GitHub stars, official renderers for React, Angular, Lit, Flutter, and Markdown.
AG-UI (Agent-User Interaction Protocol) from CopilotKit is the runtime protocol. It doesn’t define how UI looks. It defines how agent and app talk to each other: a bidirectional stream of typed JSON events (TEXT_MESSAGE_CONTENT, TOOL_CALL_START, STATE_DELTA) over HTTP/SSE/WebSockets. AG-UI is spec-agnostic. It transports A2UI payloads, MCP Apps payloads, or whatever your stack emits. Adopted by Google, LangChain, AWS, Microsoft, Mastra, and others. Around 13,000 stars.
MCP Apps (the official MCP extension, evolved from the community project mcp-ui) take a third approach: UI as iframe sandbox. An MCP tool returns a UIResource that gets rendered as inline HTML or remote DOM in a sandboxed iframe. Communication via postMessage. Live in Claude since January 2026, with launch apps from Slack, Figma, Asana, and others. Framework-neutral because everything lives inside the iframe.
A2A (Agent-to-Agent) from the Linux Foundation is not a UI protocol. It governs how agents communicate with each other: Agent Cards, task lifecycle, JSON-RPC transport. The UI connection only exists because A2UI payloads can be transported over A2A. A2A itself renders nothing.

Why the confusion persists
The four protocols address different layers, but they interact. AG-UI transports A2UI specs. A2A can relay A2UI payloads between agents. MCP Apps run in parallel. A layer model where each layer is clear on its own, but the interplay rarely gets explained.
On top of that: the React ecosystem has claimed the term “Generative UI” since Vercel’s AI SDK 3.0. streamUI and React Server Components became synonymous with the entire concept. Google “Generative UI” and you’ll land on React tutorials, missing the fact that A2UI, AG-UI, and MCP Apps are framework-agnostic. They approach the problem architecturally differently from Vercel’s RSC approach.
Where does Angular stand?
Better than most people think. The infrastructure is there. The attention isn’t.
The A2UI Angular renderer (@a2ui/angular, v0.10.0) is maintained by the A2UI team and uses Angular’s native component model: DI, change detection, A2UI_RENDERER_CONFIG as injection token for the component catalog. Caveat: it requires Angular v21.

CopilotKit ships first-class Angular bindings (@copilotkitnext/angular, Angular 19–21) for AG-UI. registerFrontendTool, registerRenderToolCall, provideCopilotKit, the full AG-UI client with DI integration. What’s missing: CopilotKit doesn’t have its own A2UI renderer for Angular. Only for React, confirmed in issue #3582. For A2UI in Angular, you need Google’s renderer.
Then there’s WebMCP in Angular v22 (experimental): provideExperimentalWebMcpTools and provideExperimentalWebMcpForms expose Signal-based state and Signal Forms as typed tools for browser AI. The app becomes the tool, not just the surface. That’s the inverse direction: not Agent→UI, but App→Agent. Complementary to A2UI.
Why Angular is well-positioned here
Not cheerleading. An observation.
Angular’s core concepts fit what declarative agent UI needs, and that’s not a coincidence.
A2UI’s component catalog is fundamentally a DI token. The agent can only request components that exist in the registered catalog. Christine Vallaure describes this in her A2UI analysis as the central security boundary: the catalog is the menu the agent gets to order from. In Angular, dependency injection is that boundary. Natively. No workaround.
A2UI’s dataModelUpdate stream maps onto Signals. JSON Pointer paths become signal reads in the template, a signals-in-a-service pattern keeps agent state centralized. No RxJS overhead, no manual subscriptions. And a TypeScript union like 'Button' | 'Card' | 'TextField' is both a compile-time contract and the catalog the LLM generates against. If the agent invents a component that doesn’t exist, validation catches it. At compile time, not at runtime.
For dynamic rendering, there’s *ngComponentOutlet with inputs via ngComponentOutletInputs and a custom injector via ngComponentOutletInjector. Selectorless in v22.
Look at the @a2ui/angular renderer source, and you’ll find exactly these patterns under the hood.
The freedom-vs-control spectrum
Shubham Saboo (Google) framed the pattern spectrum most clearly. Three levels:
Controlled: pre-built components, the agent only selects. Pixel-perfect, but expensive in token budget. Every registered component costs roughly 400 tokens per turn. With 25 components, you’re at 10,000 tokens before the user has said anything.
Declarative: the agent emits a structured UI spec (A2UI). The client maps to its own components. Scales flat: 50 or 500 card types cost the same per turn. Default choice for most teams.
Open-ended: raw HTML in a sandbox (MCP Apps). Maximum flexibility, zero brand guarantee. Saboo calls it “neo-brutalist on Tuesday, iOS 4 clone on Wednesday.” Only useful for one-off visualizations.

Most teams drift into a pattern instead of choosing deliberately. That’s where architecture decisions go wrong.
Correctness and drift
What happens when the LLM generates components that don’t exist in the catalog? Schema drift between catalog and LLM output is a real problem. A2UI’s validator catches invalid widgets. But it doesn’t catch bad taste, as Vallaure puts it.

In Atelier, my Angular toolkit for component libraries, the spec layer exists for exactly this purpose. It formalizes what a component can and cannot do as a machine-readable contract. When an agent generates against that contract, there’s a clear validation boundary. The same principle as A2UI’s catalog, just approached from the component library side.
What I keep thinking about: how do we build drift gates that ensure catalog and LLM output don’t diverge? Compile-time validation via narrow unions is a start. Runtime validation against the registered catalog is the second step. The bridge between those two, a continuous reconciliation between catalog promises and actual agent output: nobody has a convincing solution yet. If you have good ideas, I’d like to hear them.
The map, summarized
| Protocol | Solves | Angular support |
|---|---|---|
| A2UI | What the user sees (UI spec) | @a2ui/angular (official, v21+) |
| AG-UI | How agent and app talk (runtime) | @copilotkitnext/angular (v19–21) |
| MCP Apps | UI as iframe sandbox | Framework-neutral (iframe) |
| A2A | Agent-to-agent communication | No direct UI relevance |
Keeping these terms straight is not an academic exercise. It determines which libraries you install, what your data stream looks like, and where your trust boundary sits. For Angular teams: the building blocks are there. What’s missing are architecture patterns that bring them together, and experience reports from teams that have shipped this to production.
A2UI is at v0.9.1, not v3.0. WebMCP in Angular is experimental. The field moves fast, but the layers I’ve described here will stick around. Transport, runtime, UI spec, agent mesh. The abstraction layers won’t change, only the implementations.
