Agent-to-user protocols let an AI agent stream events, change shared state, call tools in the browser and even compose the interface itself, yet neither AG-UI nor A2UI defines authentication, authorisation or a threat model of its own. MCP connects agents to tools and A2A connects agents to each other; AG-UI and A2UI cover the last hop, between the agent and the person using it. That hop carries prompt-injection payloads in one direction and agent-generated interface in the other, so it is a trust boundary in both directions. This guide explains how the two protocols work, where the trust boundaries sit, what can go wrong, and how to deploy them safely. It is written for application security engineers, platform teams building agent front ends, and architects reviewing agentic products. Specification details were checked on 11 November 2026; both projects are moving quickly, so confirm against the current versions.
Key takeaways
- Neither protocol brings its own security. The AG-UI draft specification states that AG-UI defines no credential, and the A2UI v0.9.1 specification has no security section, so authentication, authorisation and tenant isolation are entirely your application's job.
- Everything the browser sends is untrusted. Messages, shared state, client-declared tools, context and forwarded properties in an AG-UI run input are all prompt-injection and privilege-escalation vectors if a browser can set them directly.
- Put a trusted server between the user and the agent. Microsoft's AG-UI security guidance recommends that AG-UI servers are not exposed directly to browser or mobile clients; a frontend server should build the protocol messages and pass through only user text.
- Agent-generated UI is untrusted output. A2UI's component catalog removes arbitrary code, but images, media URLs,
openUrl, labels and display names still come from the model and need allowlists, escaping and impersonation checks. - A thread ID is not a credential. Bind every thread, snapshot, checkpoint and approval to the authenticated user and tenant on every request, or one user can resume another user's agent session.
What AG-UI and A2UI are, and how they differ
AG-UI (Agent-User Interaction Protocol) is an open, event-based protocol that connects an agent backend to a user-facing application. Its documentation says it came out of CopilotKit's partnership with LangChain and CrewAI. A client sends a run input with an HTTP POST and the agent streams typed events back. The current release is 1.0; a working draft of 1.1 exists and, as of November 2026, no SDK implements it.
A2UI is a declarative generative UI format announced by Google's A2UI team on 15 December 2025 and released as an Apache 2.0 open-source project. Instead of sending HTML or JavaScript, an agent sends JSON messages that describe a surface built from components the client has pre-approved in a catalog, and the client renders them with its own native widgets (React, Lit, Angular, Flutter, SwiftUI and Jetpack Compose renderers exist). The project repository lists v0.9.1 as the current production release, a v1.0 specification as a release candidate and v0.8 as legacy, and describes the project as an early public preview.
The two fit together: AG-UI is the interaction channel, A2UI is one payload format it can carry, and A2UI can also travel over A2A. A third protocol in the same family, the Agent Client Protocol (ACP), connects code editors to coding agents using JSON-RPC over stdio for local agents, with remote HTTP or WebSocket support still in progress; it reuses MCP JSON types where it can.
| Protocol | Layer | What crosses the wire | Built-in security | Main risk |
|---|---|---|---|---|
| MCP | Agent to tools and data | JSON-RPC tool calls, resources, prompts | OAuth-based authorisation specification for HTTP transports | Tool poisoning, over-scoped tokens |
| A2A | Agent to agent | Tasks, messages, Agent Cards | Authentication schemes declared in the Agent Card | Agent impersonation, delegated trust |
| AG-UI | Agent to user application | Run input (messages, state, tools, context) and a stream of typed events | None: "AG-UI defines no credential" | Client-side injection, session takeover across threads |
| A2UI | Agent-generated interface | Declarative JSON surfaces bound to a data model | Component catalog; no executable code in the format | Malicious content inside allowed components, URL abuse, impersonation |
| MCP Apps | Server-supplied interface | HTML resources (ui://) in sandboxed iframes | Iframe sandbox and CSP rules in the extension | Script running in a sandbox that is configured too loosely |
| ACP | Editor to coding agent | JSON-RPC over stdio (local) | Process boundary of the editor | Coding agent acting with the developer's local privileges |
For the iframe-based alternative, see the MCP Apps Security Guide. For the agent-to-agent layer, see the A2A Protocol Security Guide.
The AG-UI attack surface: run inputs and events
An AG-UI run input carries five things the model will read: the message list, shared state, the tools the frontend offers, context, and forwarded properties. If a browser can set any of them directly, an attacker can set them too. Microsoft's Agent Framework guidance names each one as an injection vector and calls the message list and state the primary prompt-injection paths.
| Run input element | Threat if the client is untrusted | Control |
|---|---|---|
| Messages | Forged system or assistant messages that rewrite instructions; fake tool results | Accept only role: "user" text from the client; rebuild history server side from your own store |
| Tools (frontend tools) | Injected tool definitions whose descriptions carry hidden instructions or lure the model into passing sensitive arguments | Server-side allowlist of tool names and parameter schemas; reject unknown tools; authorise each tool per user |
| State | Hidden instructions in state values; fields such as systemOverride that try to switch off controls | JSON Schema validation, size limits, reject unknown fields, strip server-owned namespaces |
| Context | Instructions embedded in context descriptions or values | Generate context on the trusted server; sanitise anything user-derived |
| Forwarded properties | Arbitrary data that downstream systems treat as configuration or instructions | Treat as untrusted; never read authorisation decisions from them |
threadId and runId | Guessing or replaying another user's thread to read history or resume a run | Treat as a continuation identifier only; check ownership against the authenticated user and tenant |
The event stream back to the client also needs attention. AG-UI defines lifecycle events (RunStarted, RunFinished, RunError, StepStarted, StepFinished), text message events, tool call events (ToolCallStart, ToolCallArgs, ToolCallEnd, ToolCallResult), state events (StateSnapshot, StateDelta, MessagesSnapshot), activity, subagent and reasoning events, plus Raw and Custom events whose meaning the application defines. Three points matter for security:
- State deltas are JSON Patch:
StateDeltacarries RFC 6902 operations that the frontend applies in sequence. A frontend that applies patches to an object holding anything security relevant (role flags, approval status, feature switches) lets the agent, and anyone who can steer the agent, change it. Keep security decisions out of shared state. - Tool results reach the screen: backend tool rendering and
ToolCallResultcan carry raw data from internal systems. Microsoft's guidance says to filter responses for keys, tokens, passwords, PII, internal paths and stack traces before they reach the client. - Truncation is not success: the draft specification says a consumer must not invent a
RUN_FINISHEDfor a stream that ends without a terminal event. Treat a cut-off stream as an unknown outcome, especially when a tool with side effects was in flight.
The A2UI attack surface: agent-generated interface
A2UI's design goal, in Google's words, is UI that is "safe like data, but expressive like code". The agent sends createSurface, updateComponents, updateDataModel and deleteSurface messages; the client replies with action messages (name, surface ID, source component ID, timestamp, context) and error messages. Components must come from the catalog the surface was created with (catalogId, fixed once the surface exists), data binds through JSON Pointer paths (RFC 6901), and logic is limited to named catalog functions such as regex, formatCurrency and openUrl.
That removes script injection by design, which is a real gain over rendering model-written HTML. It does not make the content trustworthy. The catalog limits which widgets appear, not what they say or where they point:
- Phishing inside trusted components: a prompt-injected agent can render a native-looking form asking for a password, an MFA code or card details. The form inherits the host application's styling, which makes it more convincing than an iframe would be.
- Agent impersonation: the specification itself says orchestrators should set or validate
iconUrlandagentDisplayNameso a malicious agent cannot pose as a trusted service. In multi-agent front ends this is the main spoofing control. - URL abuse: Image, Video and AudioPlayer components load media from URLs and
openUrlopens a browser. The specification gives no URL validation rules, so an injected agent can use image URLs as a data exfiltration channel (query strings fetched automatically on render) or send users to a lookalike site. Rendered links and images were the exfiltration path in the EchoLeak Microsoft 365 Copilot incident. - Actions with side effects: an
actionsent back to the agent is a user decision only if the user actually saw and understood what they clicked. A surface that labels a destructive action innocently turns a click into consent the user never gave. - Custom components: the project describes custom "smart wrapper" components as the place to enforce sandboxing policy, which "places security firmly in the developer's hands". Every custom catalog entry is code you own and must review like any other UI component.
Treat every A2UI payload as model output under OWASP LLM05:2025 Improper Output Handling. See the OWASP LLM05 Improper Output Handling Guide.
Reference architecture: where the trust boundaries sit
Microsoft's guidance describes three tiers: the end user (untrusted), a trusted frontend server that builds protocol messages, and the AG-UI server that runs agent logic and tools. The same pattern works for any AG-UI or A2UI deployment:
- Browser or mobile app: sends only user text and user actions, authenticated with the application's normal session (cookie with
SameSiteand CSRF protection, or a short-lived bearer token). Renders events and A2UI surfaces through escaping renderers. - Trusted frontend server (backend for frontend): authenticates the user, resolves tenant and permissions, rebuilds the message history from server storage, attaches the server-approved tool list and context, validates state against a schema, and calls the agent with a service identity scoped to that user.
- Agent server: runs the model and tools, enforces per-tool authorisation again (defence in depth), filters tool output, and emits events. It is never reachable from the internet directly.
- Downstream tools and APIs: reached with credentials the agent holds, ideally delegated per user rather than a shared super-user key. See the MCP Security Guide for the tool side.
Where identity fits
The agent server and the frontend server are non-human identities with real access: API keys for model providers, tokens for tool back ends, and storage credentials for thread snapshots. Keep these in a secrets manager, rotate them, and scope storage by authenticated user, tenant or workspace. Microsoft's Python adapter makes this explicit: endpoints with a snapshot store must declare an authorisation scope resolver, it must return a non-empty scope on every request, and failure returns an error rather than falling back to unscoped operation. Its single_user=True mode is for local or genuinely single-user apps only. Approval state in that framework is, in its own words, not an authentication or tenant authorisation mechanism.
Implementing securely: a phased approach
- Inventory and classify: list every AG-UI endpoint, A2UI renderer and catalog, the agents that can reach each surface (first party or third party), and the tools each agent can call. Mark tools with side effects (payments, deletions, outbound email) as high risk.
- Close direct exposure: put the agent server behind the trusted frontend server; block it at the network edge. Add authentication to the endpoint with your web framework, since the protocol has none.
- Lock down run inputs: accept only user-role messages, enforce a tool allowlist with parameter schemas, validate state with JSON Schema (types, ranges, size limits, reject unknown fields) and generate context server side.
- Bind sessions: store threads, snapshots and checkpoints keyed by tenant and user; check ownership of the
threadIdon every request; test with two accounts that thread IDs from one cannot be used by the other. - Harden rendering: HTML-escape message text, render Markdown through a sanitiser, restrict A2UI media and
openUrlto an allowlist of schemes and domains (blockjavascript:anddata:navigations), add a Content Security Policy with tightimg-srcandconnect-src, and set display name and icon for each agent from your own registry, never from the payload. - Gate side effects: use AG-UI interrupts for human approval on high-risk tools, show the exact action and parameters in a host-controlled (not agent-rendered) dialog, and enforce the approval server side. See the Human-in-the-Loop for AI Agents Guide.
- Test before release: run prompt-injection tests that target state, context and tool descriptions; send malformed and oversized JSON Patch deltas; try forged system messages; render A2UI payloads with hostile URLs and labels. Pilot with internal users and a single catalog first.
- Roll back cleanly: keep a feature flag that switches the front end back to plain text responses (no generative UI, no frontend tools) so you can disable the richer surface without taking the agent offline.
Decision guidance
| Situation | Recommended approach | Trade-off |
|---|---|---|
| First-party agent, internal users only | AG-UI behind a backend for frontend, A2UI with a small catalog | Low effort; still needs session binding and output escaping |
| Third-party or partner agents render into your app | A2UI with a restricted catalog per agent; host sets identity labels; no frontend tools for third parties | Less expressive UI; much smaller blast radius |
| Rich custom widgets or existing web apps must appear | MCP Apps or iframes with a strict sandbox and CSP | More flexibility, but script runs in the sandbox and isolation must be right |
| Agent needs to act in the user's browser (frontend tools) | Frontend tools declared by the server, approval for anything with side effects | Extra clicks for users; prevents silent actions |
| Multi-tenant SaaS | Per-request tenant scope, per-tenant storage keys, isolation tests in CI | More engineering; needed to stop cross-tenant thread access |
Detection and measurement
Log the protocol at the trusted frontend server, where identity is known. Record user, tenant, threadId, runId, event types, tool names called, approvals granted or denied, and validation rejections. Microsoft's adapter logs event types at INFO level, not payloads; if you add payload logging for debugging, apply the same redaction and access controls as other sensitive logs. Alert on:
- Run inputs rejected for non-user roles, unknown tools, schema failures or oversized state (attempted injection or a broken client).
- A
threadIdpresented by a user who does not own it. - A2UI surfaces requesting components outside the agent's catalog, or URLs outside the allowlist.
- High-risk tool calls without a matching approval, and runs that end without a terminal event while a tool was executing.
| Metric | Target |
|---|---|
| AG-UI endpoints reachable without authentication | Zero |
| Thread and snapshot stores scoped by user and tenant | 100% |
| High-risk tools gated by server-enforced approval | 100% |
| Rendered URL domains on the allowlist | 100%, with blocked attempts reviewed weekly |
| Injection test cases (state, context, tools, messages) passing in CI | All, on every release |
Framework mapping
| Risk | OWASP and MITRE ATLAS | NIST SP 800-53 Rev 5 | NIST CSF 2.0 |
|---|---|---|---|
| Injection through messages, state, context, tool descriptions | LLM01:2025 Prompt Injection; AML.T0051 LLM Prompt Injection (AML.T0051.001 Indirect) | SI-10 Information Input Validation | PR.AA-05 |
| Unsafe rendering of agent output and A2UI content | LLM05:2025 Improper Output Handling | SI-15 Information Output Filtering | PR.DS-02 |
| Frontend tools and side effects without approval | LLM06:2025 Excessive Agency; AML.T0053 AI Agent Tool Invocation | AC-3 Access Enforcement | PR.AA-05 |
| Cross-user thread access | LLM02:2025 Sensitive Information Disclosure | SC-23 Session Authenticity; AC-4 Information Flow Enforcement | PR.AA-05 |
| Missing audit of runs and approvals | AU-2 Event Logging; AU-12 Audit Record Generation | DE.CM-09 | |
| Event stream exposure in transit | SC-8 Transmission Confidentiality and Integrity | PR.DS-02 |
Common failure patterns
- Browser talks straight to the agent: the client posts the full run input, so anyone with developer tools can add a system message or a new tool. Fix with a backend for frontend that only forwards user text.
- Thread ID used as the session key: history and checkpoints are fetched by
threadIdalone, so a leaked or guessed ID exposes another user's conversation and tool results. - Trusting the catalog to stop phishing: the team assumes "no code" means "safe", then an injected agent renders a credential form with the host's own styling.
- Raw tool output streamed to the client: stack traces, internal hostnames and tokens appear in
ToolCallResultevents and end up in browser history and support screenshots.
Practitioner checklist
- Confirm no AG-UI endpoint is reachable from browsers or the internet without your own authentication in front of it.
- Accept only user-role text from clients and rebuild message history from server storage.
- Enforce a server-side tool allowlist with parameter schemas and per-user authorisation.
- Validate shared state with JSON Schema, size limits and rejection of unknown fields; keep security decisions out of state.
- Bind every thread, snapshot, checkpoint and approval to the authenticated user and tenant.
- Escape and sanitise all rendered text; allowlist URL schemes and domains for A2UI media and
openUrl. - Set agent display names and icons from your own registry, never from the agent payload.
- Require host-rendered, server-enforced approval for tools with side effects.
- Filter tool results for secrets, PII and internal detail before they are streamed.
Standards and references
- AG-UI: Introduction to the Agent-User Interaction Protocol
- AG-UI: Events
- AG-UI: Draft specification, transports
- A2UI: Specification v0.9.1
- A2UI project: GitHub repository
- Google Developers Blog: Introducing A2UI, an open project for agent-driven interfaces
- Microsoft Learn: Security considerations for AG-UI
- Agent Client Protocol: Introduction
Related NHI Mgmt Group resources: MCP Apps Security Guide · A2A Protocol Security Guide · Prompt Injection Guide · Human-in-the-Loop for AI Agents Guide