Akshay Parkhi's Weblog

Subscribe

HTTP vs AG-UI: What Actually Changes in Your React Code

5th April 2026

A question that comes up once you understand how AG-UI works: isn’t this just HTTP streaming with a defined event format? Could you achieve the same thing with the HTTP protocol if you defined the same output structure?

The short answer: yes. And that’s the point.

The Proof

Here’s the same agent output using HTTP streaming with your own format vs AG-UI:

HTTP streaming (you define the format):
  POST /invocations → yield {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hi"}
                            ↑ YOU define this format

AGUI:
  POST /invocations → yield {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hi"}
                            ↑ ag-ui-strands defines this format for you

Same wire format. Same SSE. Same bytes on the wire. Both are POST → text/event-stream with JSON payloads. AG-UI doesn’t introduce a new transport, a new connection type, or any networking magic. It’s HTTP streaming all the way down.

What AG-UI Actually Is

AG-UI is three things:

  1. A naming convention — "let’s all call it TEXT_MESSAGE_CONTENT instead of chunk or delta or token"
  2. A libraryag-ui-strands auto-generates those events from Strands agent internals (intercepts tool calls, extracts state) so you don’t write the yield statements manually
  3. An ecosystem agreement — if your agent emits these 12 event types, any AG-UI-compatible frontend works with it

It’s not a transport protocol. It’s a convention protocol — the same way REST, GraphQL, and JSON-RPC are convention protocols.

“Protocol”Is it a transport?What is it really?
HTTPYesApplication-layer transport
RESTNoConventions on top of HTTP
GraphQLNoQuery language on top of HTTP POST
JSON-RPCNoMessage format on top of HTTP
AG-UINoEvent format on top of HTTP SSE or WebSocket

AG-UI is to agent streaming what REST is to web APIs: “if you follow these conventions, my client will understand you.”

What You’d Build Yourself with HTTP

If you chose the HTTP protocol and wanted the same UI experience as AG-UI, you’d write approximately this:

@app.entrypoint
async def handler(payload):
    # YOU manually emit lifecycle events:
    yield {"type": "RUN_STARTED", ...}

    # YOU intercept every agent event and categorize it:
    for event in agent.stream(msg):
        if event is text:
            yield {"type": "TEXT_MESSAGE_CONTENT", ...}
        elif event is tool_start:
            yield {"type": "TOOL_CALL_START", ...}
        elif event is tool_args:
            yield {"type": "TOOL_CALL_ARGS", ...}
        elif event is tool_end:
            # YOU extract state from tool args:
            if tool_name == "update_document":
                state = extract_state(tool_args)
                yield {"type": "STATE_SNAPSHOT", ...}

    yield {"type": "RUN_FINISHED", ...}

With AG-UI (ag-ui-strands), this is automatic:

agui_agent = StrandsAgent(agent=agent, config=StrandsAgentConfig(
    tool_behaviors={"update_document": ToolBehavior(state_from_args=...)}
))

# One line — all 12 event types emitted automatically
async for event in agui_agent.run(input):
    yield event

~100 lines of manual event mapping vs ~5 lines of config. Both produce identical wire output.

The Real Value: Interoperability

Without AG-UI, every framework invents its own streaming format:

Your agent:    {"chunk": "Hello"}
LangGraph:     {"event": "on_chat_model_stream", "data": {"chunk": ...}}
OpenAI:        {"choices": [{"delta": {"content": "Hello"}}]}
Bedrock:       {"contentBlockDelta": {"delta": {"text": "Hello"}}}

Frontend: needs 4 different parsers

With AG-UI:

Your agent:    {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hello"}
LangGraph:     {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hello"}
Strands:       {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hello"}
CrewAI:        {"type": "TEXT_MESSAGE_CONTENT", "delta": "Hello"}

Frontend: one parser works for all

Nine frameworks have adopted the same event format:

FrameworkAG-UI Adapter
LangGraphag-ui-langgraph
CrewAIag-ui-crewai
AWS Strandsag-ui-strands
Google ADKag-ui-adk
Mastraag-ui-mastra
Pydantic AIag-ui-pydantic-ai
LlamaIndexag-ui-llamaindex
AG2 (AutoGen)ag-ui-ag2
Microsoft AFag-ui-microsoft-af

Build a frontend that parses AG-UI events and it works with all nine. Invent your own HTTP streaming format and it works with only yours.

The Honest Verdict

What AG-UI gives youCan you build this with HTTP?
12 typed events (TEXT_MESSAGE_*, TOOL_CALL_*, STATE_SNAPSHOT)Yes — define the same JSON yourself
Auto-extraction of state from tool callsYes — write the extraction logic yourself
Tool call interception and streamingYes — intercept agent events manually
WebSocket transport optionYes — add a /ws endpoint yourself
Frontend interop with other frameworksNo — your custom format won’t match LangGraph’s or CrewAI’s
ag-ui-strands doing it all in ~5 linesNo — you write ~100 lines of event mapping
CopilotKit React components out of the boxNo — CopilotKit expects AG-UI events

When You Should NOT Use AG-UI

  • One agent, one frontend, one team — define your own JSON format. It’s simpler and you control everything.
  • Backend-only agent (no UI) — use HTTP or A2A. AG-UI is designed for humans watching screens.
  • Simple request/response, no streaming needed — HTTP returning JSON is fine.
  • Internal tool with no framework migration plans — the interop benefit doesn’t apply.

When AG-UI Actually Helps

  • You might switch frameworks — today Strands, tomorrow LangGraph. The frontend stays the same.
  • Multiple agents, one UI — your UI talks to 3 different agent backends, all speaking AG-UI.
  • You use CopilotKit — AG-UI was created by CopilotKit. Their React components (@copilotkit/react-core) parse AG-UI events natively. You get a full agent UI for free.
  • You want the ecosystemAG-UI Dojo has live demos for every framework. You can compare how Strands vs LangGraph vs CrewAI handle the same interactions.
  • You don’t want to write event mapping codeag-ui-strands handles tool interception, state extraction, message grouping, and lifecycle events automatically.

It’s Not Just AG-UI — All Four AgentCore Protocols Are HTTP

This observation extends beyond AG-UI. We inspected the actual AgentCore SDK source code for all four protocols. Here’s what each one produces:

ALL FOUR PROTOCOLS ON AGENTCORE:

                HTTP          MCP           A2A           AGUI
                ────          ───           ───           ────
App base:       Starlette     Starlette     Starlette     Starlette
Container:      port 8080     port 8080     port 8080     port 8080
Network:        TCP+TLS       TCP+TLS       TCP+TLS       TCP+TLS
Transport:      HTTP POST     HTTP POST     HTTP POST     HTTP POST
Streaming:      SSE           SSE           SSE           SSE
Wire format:    data:{}\n\n   data:{}\n\n   data:{}\n\n   data:{}\n\n

Same Starlette app. Same port. Same TLS. Same SSE framing. The only difference is what JSON sits inside the data: line:

HTTP:  {"anything": "you define"}

MCP:   {"jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {"name": "search", "arguments": {"q": "..."}}}

A2A:   {"jsonrpc": "2.0", "id": 1, "result":
        {"id": "task-1", "status": {"state": "working",
         "message": {"parts": [{"text": "Searching..."}]}}}}

AGUI:  {"type": "TEXT_MESSAGE_CONTENT", "messageId": "abc",
        "delta": "Hello"}

MCP and A2A are even more similar to each other than to AGUI — both use the JSON-RPC envelope ({"jsonrpc": "2.0", "method": "...", "params": {...}}). The only difference between them is the method names: MCP uses tools/list and tools/call; A2A uses tasks/send and tasks/get.

What serverProtocol Actually Does

We checked what happens when you set the protocol in AgentCore’s starter toolkit:

ProtocolConfiguration(server_protocol="HTTP").to_aws_dict()  → {"serverProtocol": "HTTP"}
ProtocolConfiguration(server_protocol="MCP").to_aws_dict()   → {"serverProtocol": "MCP"}
ProtocolConfiguration(server_protocol="A2A").to_aws_dict()   → {"serverProtocol": "A2A"}
ProtocolConfiguration(server_protocol="AGUI").to_aws_dict()  → {"serverProtocol": "AGUI"}

It’s a label. AgentCore doesn’t parse your events, doesn’t validate the format, and doesn’t change routing based on the protocol value. It proxies POST /invocations to your container and streams back whatever bytes you return. The label shows up in the console and CloudWatch for observability — that’s it.

You could set serverProtocol: HTTP and manually emit JSON-RPC tasks/send responses — it would work as an A2A agent. You could set serverProtocol: HTTP and yield AG-UI events — it would work as an AGUI frontend. The label doesn’t enforce anything.

Four JSON Vocabularies, Not Four Transports

The four “protocols” are really four JSON vocabularies, each designed for a different conversation:

HTTP:  "I define my own language."
       → No vocabulary constraints. You speak however you want.

MCP:   "I speak JSON-RPC with tool/resource/prompt vocabulary."
       → Designed for: AI system asking "what tools do you have?"
       → The Strands Agent brain is NOT used — raw tools exposed.

A2A:   "I speak JSON-RPC with task lifecycle vocabulary."
       → Designed for: Agent A asking Agent B "do this job."
       → The Strands Agent brain IS used — wrapped as a task worker.

AGUI:  "I speak 12 typed events for human UI."
       → Designed for: browser rendering streaming text + tool cards + state.
       → The Strands Agent brain IS used — events auto-generated by library.

The infrastructure is identical. The JSON is different. The audience is different. Each “protocol” is really a library plus a convention that saves you from reinventing the JSON format and parsing logic yourself.

The Bottom Line

AG-UI is not magic. It’s HTTP streaming with a defined event format. MCP is HTTP with JSON-RPC and tool vocabulary. A2A is HTTP with JSON-RPC and task vocabulary. You could build any of them yourself with the HTTP protocol and the right JSON output.

The value proposition is the same as REST, GraphQL, or JSON itself: everyone agreed on the format, so everything interoperates. Whether that’s worth it depends on whether you care about framework interoperability. If you’re building one agent with one frontend, HTTP streaming with your own format is perfectly fine. If you’re building a platform that connects to multiple agent frameworks, the shared vocabulary saves you from writing separate parsers for each one.

The protocol label isn’t about technical complexity — it’s about ecosystem agreement. And right now, nine major frameworks have agreed on AG-UI, the MCP ecosystem is growing rapidly, and A2A has Google and AWS behind it. The conventions are winning not because they do something HTTP can’t, but because they do something HTTP alone doesn’t: make everyone speak the same language.