Agentic AI

Agent-User Interaction Protocol (AG-UI)

AG-UI is an open, event-based protocol that standardizes how an AI agent's backend streams to a user-facing frontend: text tokens, tool calls, and shared-state updates all travel as typed events over a transport such as Server-Sent Events, instead of a bespoke, framework-specific wire format. Where the Model Context Protocol connects an agent to tools and A2A connects one agent to another, AG-UI is the third leg: the interface between an agent and the human watching it work.

The Agent-User Interaction Protocol (AG-UI) is an open, lightweight standard for the connection between an AI agent’s backend and the application a person actually looks at. It defines a fixed vocabulary of streamed events, text deltas, tool-call lifecycle markers, state snapshots and patches, so that a frontend can render an agent’s work as it happens instead of waiting for a single finished response. AG-UI grew out of CopilotKit’s early integrations with LangChain and CrewAI and was open-sourced as a framework-agnostic protocol, positioning itself as the missing third leg of the agent protocol stack: MCP connects an agent to its tools, A2A connects one agent to another, and AG-UI connects an agent to the human on the other side of the screen.

The Problem: Every Framework Invented Its Own Wire Format

Before AG-UI, “streaming an agent to a UI” meant something different in every stack. LangGraph exposed its own event schema, CrewAI another, a hand-rolled FastAPI backend a third, and each one shipped its own ad hoc way to signal “a tool is running,” “the model is still thinking,” or “here is an update to the shared document the user and agent are both editing.” A frontend built against one framework’s event shape had to be rewritten to support the next, and the increasingly common pattern of showing a tool call while it is still streaming its arguments, so a form can pre-fill before the agent finishes speaking, had no shared vocabulary at all.

This is the same integration problem MCP solved for tools, moved one layer up the stack. AG-UI collapses it the same way: any AG-UI-compliant backend can drive any AG-UI-compliant frontend, regardless of which agent framework, model, or UI library sits behind either end.

%%{init: {'theme': 'base'}}%%
graph TD
    classDef default fill:#ffffff,stroke:#4338CA,stroke-width:2px,color:#0F172A,rx:8px,ry:8px;
    classDef data    fill:#EEF0F7,stroke:#0D9488,stroke-width:2px,color:#0F172A,rx:8px,ry:8px;
    classDef process fill:#F7F8FC,stroke:#6366F1,stroke-width:2px,color:#0F172A,rx:8px,ry:8px;
    classDef output  fill:#4338CA,stroke:#4338CA,stroke-width:2px,color:#ffffff,rx:8px,ry:8px;

    subgraph AGENT["Agent backend (any framework)"]
      LG[LangGraph, CrewAI, Pydantic AI, custom]:::process
      MCPT[(MCP: tools and data)]:::data
      A2AP[(A2A: peer agents)]:::data
    end

    LG --> MCPT
    LG --> A2AP
    LG -->|"AG-UI event stream over SSE / WebSocket"| BRIDGE[AG-UI encoder]:::process
    BRIDGE -->|"TEXT_MESSAGE_*, TOOL_CALL_*, STATE_*"| FE[Frontend: chat UI, canvas, form]:::output
    FE -->|"user input, tool approvals"| LG

The Event Model

AG-UI defines roughly sixteen standard event types, grouped into five categories. Every event carries a type discriminator, so a client can route on a single field rather than parsing framework-specific payloads.

CategoryEventsPurpose
LifecycleRUN_STARTED, RUN_FINISHED, RUN_ERROR, STEP_STARTED, STEP_FINISHEDMark the boundaries of an agent run and its internal steps, so a UI can show progress even before any text arrives.
Text messagesTEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_ENDStream assistant (or user) text token-by-token, producing the familiar typing effect.
Tool callsTOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, TOOL_CALL_RESULTStream a tool invocation as it is decided, argued, and resolved, so the UI can show “calling lookup_order…” before the call even completes.
State managementSTATE_SNAPSHOT, STATE_DELTAKeep a shared state object, a document, a form, a canvas, in sync between agent and frontend without resending the whole object on every change.
SpecialRAW, CUSTOMEscape hatches for framework-specific payloads that do not fit the standard vocabulary, so adapters do not need to drop information.

The state events are the least obvious and most consequential part of the design. A STATE_SNAPSHOT carries a full copy of whatever shared object the agent and UI both read and write, useful once, at the start of a run. Every subsequent change is a STATE_DELTA: a JSON Patch document describing only what changed, not the whole object again.

// Scenario: a frontend rendering a live-editing agent that fills in a
// multi-field intake form as the user talks. Two state events, same object.

// Sent once, at run start:
{ type: "STATE_SNAPSHOT", snapshot: { name: "", email: "", plan: "" } }

// Sent after the agent extracts a name from the conversation. The client
// applies this patch to its local copy instead of re-rendering from scratch.
{ type: "STATE_DELTA", delta: [
  { op: "replace", path: "/name", value: "Priya Shah" }
] }

Interactive: Full State Resend vs. Delta Patching

The cost of resending the whole shared state on every update, versus patching only what changed, scales with two things: how big the state object is, and how many updates a run produces. A short chat with a tiny state object barely notices the difference. A collaborative document or a large form filled in across dozens of incremental edits does.

Interactive: grow the state object and the update count, and watch full-snapshot traffic diverge from delta traffic

Full-snapshot total: - Delta total: - Bandwidth saved: -

Two things fall out of the chart. The full-snapshot line is always straight, because it pays the whole state size on every single update regardless of how much actually changed. The delta line pays that cost once, then grows at whatever rate real edits happen at, which is nearly always far smaller than the object itself. Push the patch percentage toward 50% and the two lines converge, a reminder that delta patching only wins when updates are genuinely incremental; an agent that rewrites most of the state on every turn gets little benefit from STATE_DELTA and might as well snapshot.

AG-UI, MCP, and A2A: Three Protocols, Three Directions

MCPA2AAG-UI
ConnectsAgent to tools and dataAgent to another agentAgent to a user-facing application
DirectionAgent pulls context, calls functionsPeer to peer, both sides autonomousAgent pushes updates, user pushes input and approvals
Interaction shapeCall a function, get a resultDelegate a task, get status and artifactsStream events: text, tool activity, state
Typical transportstdio, HTTPJSON-RPC, gRPC, RESTServer-Sent Events, WebSocket
What breaks without itBespoke tool integration per modelBespoke integration per agent pairBespoke streaming format per frontend

None of the three overlaps with the others in practice. A production agent commonly speaks all three at once: MCP underneath to reach its tools, A2A sideways to delegate to a peer agent owned by another team, and AG-UI forward to the chat window or canvas the user is actually looking at.

Framework and Language Adoption

AG-UI shipped first-party integrations across most of the popular agent frameworks within roughly a year of its release, which is the main reason it spread quickly: teams did not need to hand-roll an adapter to try it.

Framework / runtimeIntegration
LangGraphFirst-party AG-UI support
CrewAIFirst-party AG-UI support
MastraFirst-party AG-UI support
Pydantic AIFirst-party AG-UI support
Google ADK, AWS Strands, Microsoft Agent FrameworkSupported as of 2026
LlamaIndex, Agno, AG2First-party integrations
SDK languagesPython, TypeScript, plus community SDKs for Go, Kotlin, Rust, Java, and Dart

CopilotKit remains the most widely used reference frontend for AG-UI, with a useAgent hook that subscribes to an AG-UI stream and exposes it as ordinary React state, but the protocol itself is framework-agnostic: any frontend that can parse the event schema can consume any AG-UI-compliant backend.

Security Considerations

Streaming an agent’s internal activity to a UI widens the surface a compromised or manipulated agent can act on.

  • CUSTOM and RAW events are an escape hatch, treat them as untrusted input. They exist so framework-specific data is not lost, but a frontend that blindly renders their payload as HTML or executes it is exposing itself to injection from anything the agent’s context has been fed, including a document it retrieved or a tool’s output.
  • Tool-call argument streaming can leak intent before validation. Showing TOOL_CALL_ARGS as it streams is good UX, but if those args are rendered directly into a form the user can submit with one click, a prompt-injected instruction that manipulates argument values reaches the user faster than a review step can catch it. Keep a human-in-the-loop confirmation step for any tool call with real-world side effects, regardless of how fast AG-UI can render it.
  • State deltas assume a trusted state owner. STATE_DELTA patches are applied to whatever the client already has, so a client that never validates the resulting object against a schema can end up in an inconsistent or attacker-influenced state after a sequence of patches, particularly in multi-user or multi-agent scenarios where more than one source can emit deltas.
  • Long-lived streams need the same auth as any other endpoint. An SSE or WebSocket connection carrying a full agent run is not exempt from session expiry, per-request authorization, or rate limiting just because it looks like a UI feature rather than an API.

What’s New (2025-2026)

  • Rapid framework consolidation. Within a year of release, AG-UI moved from a CopilotKit-specific integration to first-party support across LangGraph, CrewAI, Mastra, Pydantic AI, Google ADK, AWS Strands, and Microsoft Agent Framework, with community SDKs extending coverage to Go, Kotlin, Rust, Java, and Dart.
  • Positioned as the third leg of the protocol stack. Industry writeups through 2026 increasingly describe MCP, A2A, and AG-UI together as complementary layers, tools, agents, and users, rather than as competing standards, which has reduced the earlier confusion about which protocol solves which problem.
  • Generative UI patterns built on top. Because AG-UI standardizes tool-call and state events, frontends increasingly render agent tool calls as live, interactive components (a map, a chart, an editable form) rather than plain text, an approach CopilotKit brands as “Generative UI” and other adapters have followed.
  • useAgent-style hooks becoming the default integration point. Rather than hand-parsing the event stream, newer frontend SDKs expose an agent’s AG-UI stream as ordinary reactive state, closing most of the gap between “wire protocol” and “component library” for teams building agent-facing UIs.

Practical Guidance

SituationRecommendation
Building a chat UI for an agent, any backend frameworkUse AG-UI instead of a custom SSE schema. First-party support likely already exists for your framework.
Agent updates a large shared object (document, form, canvas) across many turnsRely on STATE_DELTA, not repeated STATE_SNAPSHOT. The savings compound with state size and update count.
Rendering TOOL_CALL_ARGS live in the UIFine for read-only or low-stakes tools. Gate anything with real side effects behind an explicit confirm step, independent of how fast the args stream in.
Frontend needs to show framework-specific data AG-UI has no standard event forUse CUSTOM, but validate and sanitize before rendering, exactly as you would any other untrusted payload.
Deciding between AG-UI, MCP, and A2AThey are not alternatives. Use MCP for tools, A2A for agent-to-agent delegation, AG-UI for the human-facing surface, together if your system needs all three.
Choosing a frontend to pair with an AG-UI backendCopilotKit is the most mature reference implementation, but any AG-UI-compliant client works since the protocol, not the library, is the contract.

AG-UI’s contribution is narrow in the same way MCP’s and A2A’s are: it does not make agents smarter, it makes the wire between an agent and the interface a person actually watches into a shared, documented contract instead of a pile of framework-specific glue code. That is a small promise on paper, but it is the one that determines whether a team building an agent-facing product spends its time on the product or on re-implementing a streaming protocol for the third framework this year.

How to Use: Streaming an order-lookup agent's run to a frontend over AG-UI

python
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from ag_ui.core import (
    EventType, RunStartedEvent, RunFinishedEvent,
    TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent,
    ToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent,
)
from ag_ui.encoder import EventEncoder

# Scenario: a support-chat frontend renders an agent that looks up an
# order status. The frontend needs to show token-by-token text AND a
# "looking up order..." indicator the moment the tool call starts,
# not after it finishes.
app = FastAPI()

@app.post("/agent/run")
async def run_agent(accept: str = "text/event-stream"):
    encoder = EventEncoder(accept=accept)

    async def event_stream():
        yield encoder.encode(RunStartedEvent(
            type=EventType.RUN_STARTED, thread_id="t_1", run_id="r_1"
        ))

        # Tool call streamed as it happens, not returned as one blob.
        yield encoder.encode(ToolCallStartEvent(
            type=EventType.TOOL_CALL_START,
            tool_call_id="call_1", tool_call_name="lookup_order"
        ))
        yield encoder.encode(ToolCallArgsEvent(
            type=EventType.TOOL_CALL_ARGS,
            tool_call_id="call_1", delta='{"order_id": "A-4471"}'
        ))
        yield encoder.encode(ToolCallEndEvent(
            type=EventType.TOOL_CALL_END, tool_call_id="call_1"
        ))

        # Reply text streamed token by token for a typing effect.
        yield encoder.encode(TextMessageStartEvent(
            type=EventType.TEXT_MESSAGE_START, message_id="msg_1", role="assistant"
        ))
        for chunk in ["Order A-4471 ", "shipped yesterday ", "and arrives Thursday."]:
            yield encoder.encode(TextMessageContentEvent(
                type=EventType.TEXT_MESSAGE_CONTENT, message_id="msg_1", delta=chunk
            ))
        yield encoder.encode(TextMessageEndEvent(
            type=EventType.TEXT_MESSAGE_END, message_id="msg_1"
        ))

        yield encoder.encode(RunFinishedEvent(
            type=EventType.RUN_FINISHED, thread_id="t_1", run_id="r_1"
        ))

    return StreamingResponse(event_stream(), media_type=encoder.get_content_type())

Ready to build?

Leverage AI technologies to build your product stack

Superteams can help you build, deploy and launch AI application stacks using open source technologies — from architecture through to production.

Talk to Superteams