Back to Blog
Server-Sent Events for Real-Time Agent Communication
SSEserver-sent eventsreal-timeA2A protocolagent streamingwebsockets

Server-Sent Events for Real-Time Agent Communication

Why A2A chose SSE over WebSockets for streaming agent tasks, how SSE works in practice, and a technical guide to implementing streaming in multi-agent systems.

March 23, 2026·Clawshake

Server-Sent Events for Real-Time Agent Communication

When Google's Agent2Agent protocol needed to support long-running tasks and real-time updates between agents, the architects made an interesting choice: Server-Sent Events (SSE) rather than WebSockets. This wasn't a compromise—it was a deliberate design decision rooted in how agents actually communicate. Understanding why reveals a lot about what's different about agent-to-agent communication compared to traditional real-time web applications.


The Problem: Agents Take Time

Most API interactions are synchronous: you send a request, you get a response within milliseconds or a few seconds. This works for simple tool calls—look up a price, search a database, validate an email.

But AI agents operating in the real world do tasks that take *much* longer:

  • A research agent might spend 10 minutes crawling sources and synthesizing findings
  • A procurement agent might initiate a negotiation that plays out over hours
  • A code review agent might analyze a large codebase over 20+ minutes
  • A document processing agent might work through thousands of pages

For these tasks, a simple request/response model doesn't work. The client needs to know: Is the task still running? What's the current status? Are there partial results available? Did something go wrong? Does the agent need additional input?

The A2A protocol handles this through two mechanisms: SSE streaming for connected clients, and push notifications via webhooks for disconnected scenarios.


Why SSE, Not WebSockets?

WebSockets provide full duplex communication—both client and server can send messages at any time. This sounds like it would be ideal for agent communication. So why did A2A choose SSE?

The communication pattern doesn't need bidirectional: In A2A, the interaction model is: client sends a request (one HTTP call), server streams updates back until the task completes. This is inherently asymmetric—the server is pushing to the client, not engaging in a back-and-forth dialogue. SSE is a perfect fit for this pattern.

SSE is HTTP: Server-Sent Events run over regular HTTP/HTTPS. This means they work through every proxy, load balancer, and firewall that knows how to handle HTTP—which is all of them. WebSockets require protocol upgrades that can be blocked by enterprise infrastructure.

Reconnection is built in: SSE includes automatic reconnection logic in the browser and most HTTP clients. If the connection drops, clients automatically reconnect and can resume from where they left off using the Last-Event-ID header.

Simpler server implementation: SSE servers are straightforward HTTP handlers that keep connections open and write to them.

Agent interactions are request-scoped: Each agent task is initiated by a single request. The streaming is scoped to that task.


How A2A Streaming Works

Here's the technical flow for a streaming A2A interaction:

1. Client initiates a streaming request

http
POST /a2a HTTP/1.1
Host: agents.acmecorp.com
Authorization: Bearer <token>
Content-Type: application/json
Accept: text/event-stream

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/stream",
  "params": {
    "message": {
      "role": "user",
      "parts": [{ "type": "text", "text": "Analyze Q4 supplier contracts" }]
    }
  }
}

2. Server responds with SSE stream

http
HTTP/1.1 200 OK
Content-Type: text/event-stream

data: {"result":{"type":"TaskStatusUpdateEvent","status":"submitted"}}

data: {"result":{"type":"TaskStatusUpdateEvent","status":"working","message":"Loading contracts..."}}

data: {"result":{"type":"TaskArtifactUpdateEvent","artifact":{"content":"Found 3 anomalous clauses..."}}}

data: {"result":{"type":"TaskStatusUpdateEvent","status":"completed"}}

3. Client handles events

python
async with client.stream("POST", agent_url, json=payload, headers=headers) as response:
    async for line in response.aiter_lines():
        if line.startswith("data: "):
            event_data = json.loads(line[6:])
            result = event_data.get("result", {})
            event_type = result.get("type")
            
            if event_type == "TaskStatusUpdateEvent":
                status = result["status"]
                if status in ("completed", "failed"):
                    break

Task Lifecycle States

A2A tasks move through a formal state machine:

submitted → working → input-required → (resume) → working
                    → completed ✓
                    → failed ✗
                    → canceled

The input-required state is particularly important for real agent interactions: tasks that need clarification, missing information, or approval from a human before proceeding. This is how agents implement human-in-the-loop workflows without breaking the streaming model.


Push Notifications for Disconnected Clients

Not all clients can maintain a persistent HTTP connection. Serverless functions time out. Mobile clients disconnect. Batch jobs run periodically.

For these scenarios, A2A supports push notifications via webhooks. Instead of streaming, the server sends HTTP POST requests to a URL provided by the client whenever the task state changes.

json
{
  "jsonrpc": "2.0",
  "method": "message/send",
  "params": {
    "message": { "...": "..." },
    "configuration": {
      "pushNotificationConfig": {
        "url": "https://myapp.com/webhooks/agent-updates",
        "token": "webhook-secret-token"
      }
    }
  }
}

The server POSTs task updates to this URL. This enables long-running workflows that span hours or days without requiring persistent connections.


SSE vs Polling vs WebSocket: Decision Guide

ScenarioBest ChoiceReason
Agent task streaming, connected clientSSEUnidirectional, HTTP-native, auto-reconnect
Long task, client may disconnectWebhooks/pushNo persistent connection required
Short synchronous task (<5s)Regular HTTP POSTStreaming adds overhead without benefit
True bidirectional agent chatWebSocketBoth sides need to initiate at any time
Mobile/serverless consumerWebhooksCan't maintain long-lived connections

Implementation Checklist

If you're building an A2A-compatible server that supports streaming:

  1. 1.Advertise streaming support in your Agent Card: "capabilities": {"streaming": true}
  2. 2.Handle `message/stream` method separately from message/send
  3. 3.Keep connections alive: Disable proxy timeouts or send periodic keep-alive events
  4. 4.Use event IDs: Set id: in SSE events to support reconnection via Last-Event-ID
  5. 5.Handle reconnection: Implement tasks/resubscribe so clients can resume after drops
  6. 6.Send keep-alive pings every 15-30 seconds for long-running tasks to prevent proxy timeouts

SSE is simple, proven, and genuinely well-suited to the agent streaming use case. The A2A team made the right call. If you're building real-time agent communication, SSE should be your default choice—and reach for WebSockets only when you genuinely need client-initiated messages mid-stream.