A2A's Transport Layer: HTTP, SSE, and JSON-RPC 2.0
A2A is intentionally boring at the wire level.
It reuses HTTP for reachability, JSON-RPC 2.0 for request/response framing, and Server-Sent Events (SSE) (or binding-equivalent streams) for incremental task updates.
The interesting part is the agent data model (tasks, messages, parts, artifacts) carried on top of those standards.
Summary
- A2A separates what agents exchange (canonical task/message model) from how bytes move (protocol bindings such as JSON-RPC over HTTP with optional streaming).
- Insight: You can put A2A behind ordinary gateways, load balancers, auth proxies, and observability stacks instead of inventing a proprietary peer bus.
- Key Concepts: JSON-RPC 2.0, HTTP POST endpoints, SSE streams, push notifications / webhooks, tasks vs messages, capability flags on the Agent Card, optional alternate bindings (verify at build).
- When to Use This Model: Implementing a client/server, choosing sync vs streaming vs push, or comparing A2A transport to MCP transports.
- Limitations/Trade-offs: Spec versions add bindings and rename methods; always pin protocol/SDK versions. SSE needs proxy timeouts tuned. Push needs public or reachable client webhooks.
- Related Topics: Agent Cards, MCP transport choices, cross-cloud interoperability, adoption vs custom handoff.
Foundations
Guiding principle: simple web standards
A2A's design goals call out reuse of familiar enterprise web technology:
| Building block | Role in A2A |
|---|---|
| HTTP(S) | Ubiquitous transport, auth middleware, TLS |
| JSON-RPC 2.0 | Method name + params + correlated id + error object |
| SSE | One-way server → client event stream for task progress |
| Webhooks (push) | Server → client HTTP callbacks when the client cannot keep a stream open |
That is the opposite of a new binary agent bus. It is closer to "JSON APIs done with a shared agent vocabulary."
Canonical model vs binding
Modern A2A specs often describe layers:
Layer 1 Canonical data model (Task, Message, Part, Artifact, ...)
Layer 2 Abstract operations (send message, stream, get task, cancel, ...)
Layer 3 Protocol bindings (JSON-RPC, and possibly others)Implications for implementers:
- Do not hardcode product logic to one SDK-only type if you need multi-language peers.
- Do treat method names as binding-specific while task semantics stay stable.
- Expect official schemas/proto artifacts to be the normative source (verify at build).
Client and server roles on the wire
| Role | Wire behavior |
|---|---|
| A2A client | Discovers Agent Card; sends JSON-RPC requests; optionally opens SSE; may register push URLs |
| A2A server | Serves card; accepts messages; creates/updates tasks; emits stream events or push payloads |
Client Server
| GET agent card |
|------------------------->|
| POST JSON-RPC message |
|------------------------->|
| 200 result: Task |
|<-------------------------|
| GET/POST stream (SSE) |
|------------------------->|
| event: status/artifact |
|<=========================|Mechanics & Interactions
JSON-RPC 2.0 envelope
A typical request shape:
{
"jsonrpc": "2.0",
"id": "corr-123",
"method": "message/send",
"params": {
"message": {
"role": "user",
"messageId": "m-1",
"parts": [{ "kind": "text", "text": "Summarize ticket 4421" }]
}
}
}A typical success response:
{
"jsonrpc": "2.0",
"id": "corr-123",
"result": {
"id": "task-9",
"status": { "state": "working" }
}
}A typical error response:
{
"jsonrpc": "2.0",
"id": "corr-123",
"error": {
"code": -32000,
"message": "Content type not supported"
}
}Rules:
- Correlate with
id(string or number per JSON-RPC rules your stack allows). - Never assume HTTP 200 means application success; check JSON-RPC
error. - Validate
paramsagainst the current schema before hitting production peers.
Synchronous request/response
Use plain POST when:
- The interaction is short
- You can wait for a terminal or intermediate task snapshot
- Streaming is not advertised on the Agent Card
The server may return:
| Result type | Meaning |
|---|---|
| Message | Simple turn completed without long task tracking |
| Task | Work accepted; poll, stream, or wait for push for completion |
Long-running enterprise agents should prefer Task semantics even if the first response is fast.
Streaming with SSE
When the card advertises streaming, clients open a stream for:
- Task status transitions (
working→completed/failed/ ...) - Partial artifact chunks (token-like or section-like updates)
- Intermediate agent messages
SSE characteristics that matter in production:
| Topic | Guidance |
|---|---|
| Proxies | Disable or extend idle timeouts; allow text/event-stream |
| Auth | Same identity as the RPC call (header/cookie/mTLS) |
| Backpressure | Servers should bound event rates; clients should apply read deadlines |
| Reconnect | Decide whether task id allows resuming or requires replay from tasks/get-style reads |
| Payload | Each event often carries a full JSON object in data: lines |
data: {"taskId":"task-9","status":{"state":"working"}}
data: {"taskId":"task-9","artifact":{"parts":[{"kind":"text","text":"..."}]}}Exact event schemas are versioned; verify at build.
Push notifications for disconnected clients
Streaming assumes the client can keep a connection.
Push patterns cover:
- Mobile or serverless clients that cannot hold SSE
- Multi-hour jobs with human-in-the-loop pauses
- Firewalls that kill long streams
Typical flow:
- Client sends a message and includes a push config (webhook URL + auth).
- Server accepts the task.
- Server POSTs signed or authenticated updates to the webhook as state changes.
- Client may still call get-task to reconcile.
Security: webhooks must verify sender authenticity and avoid open relay abuse.
Mapping abstract operations to methods
Names differ by binding and version. Conceptually you need:
| Operation | Purpose |
|---|---|
| Send message | Start or continue work |
| Send streaming message | Same with SSE updates |
| Get task | Fetch current task snapshot |
| Cancel task | Best-effort stop |
| Configure push | Register/update webhook settings |
| Fetch authenticated card | Optional richer discovery after auth |
Prefer official SDKs over hand-rolled method strings once you leave tutorials.
Comparison to MCP transports
| MCP | A2A (common path) | |
|---|---|---|
| Local default | stdio subprocess | Less common; agents are usually network services |
| Remote | Streamable HTTP (legacy HTTP+SSE) | HTTP + JSON-RPC, SSE for streams |
| Primary unit | tools/resources/prompts | tasks/messages/artifacts |
| Opacity | Host sees tool schemas | Peer hides tools; shows skills |
See stdio vs HTTP vs SSE: MCP's Transport Options.
Advanced Considerations & Applications
Timeouts, idempotency, and retries
| Concern | Practice |
|---|---|
| Client RPC timeout | Shorter than stream lifetime; longer than p99 send ack |
| Stream idle timeout | Heartbeats or periodic status events |
| Retries | Safe on pure reads; careful on send unless message ids make retries idempotent |
| Cancel | Always available for runaway cost |
| Exactly-once | Not free; design for at-least-once + dedupe keys |
Gateways and enterprise HTTP path
Because A2A rides HTTP, you can place:
- API gateways for authn/z and WAF
- mTLS sidecars
- OpenTelemetry HTTP instrumentation
- Per-route rate limits on
message/sendequivalents
Do not forget that SSE is not a normal short REST call. Configure the path differently from CRUD APIs.
Optional alternate bindings
Some specification lines describe additional bindings (for example gRPC or HTTP+JSON/REST-style mappings) while keeping the same canonical model.
Guidance:
- Pick one binding per edge unless you run a translating gateway.
- Advertise what you support clearly on the card / docs.
- Interop testing should include at least the JSON-RPC+HTTP profile most peers expect.
Trade-off table: delivery modes
| Mode | Strength | Weakness | Best fit |
|---|---|---|---|
| Sync RPC only | Simple ops | Weak for long jobs | Quick Q&A agents |
| RPC + poll task | Easy through strict proxies | Laggy UX; more load | Enterprise networks that ban streams |
| RPC + SSE | Real-time progress | Proxy/timeout complexity | Interactive multi-minute tasks |
| RPC + push webhook | Works for disconnected clients | Webhook security & reachability | Overnight jobs, mobile backends |
Common Misconceptions
- "A2A is just SSE." SSE is optional streaming; JSON-RPC over HTTP is the core call pattern.
- "JSON-RPC means we inherited Bitcoin-era APIs." Here it is a thin RPC envelope for agent methods.
- "If MCP uses JSON-RPC too, the methods are interchangeable." They are not; different operations and data models.
- "Streaming removes the need for task ids." You still need stable task identity for cancel, resume, and audit.
- "HTTP 200 always means the agent succeeded." Application errors often travel in JSON-RPC
erroror failed task states. - "Push notifications replace auth." They add a second authenticated channel to secure.
FAQs
Must every A2A server support SSE?
No. Advertise streaming only if implemented. Clients should read the Agent Card capabilities first.
Is JSON-RPC required forever?
It is the widely documented binding. Specs may add others; verify the profile your ecosystem standardizes on.
How large can message parts be?
Bound them in your gateway. Prefer file references for large binaries instead of huge inline parts.
Can I load-balance A2A servers?
Yes, like other HTTP services, but sticky sessions or shared task stores may be required for multi-turn tasks and open streams.
Where do I put tracing headers?
Propagate W3C traceparent (or your standard) on RPC and webhook calls so client and remote agent share a trace.
How does this relate to Agent Cards?
Cards tell clients the endpoint URL and whether streaming/push exist before the first RPC. See Agent Cards.
Should I build raw SSE parsing?
Prefer official SDKs. Hand-rolled clients are fine for learning and contract tests.
What fails most often in production?
Idle timeouts on streams, auth mismatch between card and gateway, and clients ignoring non-terminal task states.
Is stdin/stdout a standard A2A transport?
A2A targets networked agents. Local stdio is MCP's comfort zone more than A2A's.
How do cancels work on the wire?
Clients call a cancel operation with the task id; servers best-effort stop work and mark the task canceled.
Do I need WebSockets?
Not for the common A2A profile. SSE covers server → client streaming without full duplex sockets.
Where next?
Hands-on: Agent-to-Agent Protocols Basics. Cross-cloud notes: Cross-Cloud Agent Interoperability.
Related
- Agent-to-Agent Protocols Basics
- Agent Cards: How Agents Advertise Their Capabilities
- What A2A Solves That MCP Doesn't
- Cross-Cloud Agent Interoperability: Azure, Bedrock, and Google Cloud
- The Broader Protocol Stack: MCP, A2A, ACP, and WebMCP
- stdio vs HTTP vs SSE: MCP's Transport Options
- Timeout Strategies for Long-Running Agent Steps
- Agent-to-Agent Protocols Best Practices
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.