Agent-to-Agent Protocol
An open protocol created by Google and donated to the Linux Foundation that standardizes how autonomous AI agents discover each other, exchange tasks, and collaborate across frameworks. A2A absorbed IBM's Agent Communication Protocol (ACP) in August 2025 and is now one of the three flagship protocols under the Agentic AI Foundation alongside MCP and ANP.
- Steward
google,
linux-foundation
- Layer
Communication Layer
- Complements
Model Context Protocol
- Specification URL
- https://a2a-protocol.org/latest/specification/
- Created Date
- Apr 9, 2025
Conventions
agent-card
Every A2A agent publishes a machine-readable Agent Card that describes its identity, capabilities, and how to talk to it
| Text | Rationale | |
|---|---|---|
| # | A public A2A agent MUST publish its Agent Card at the fixed path '/.well-known/agent.json' on its base URL so other agents can discover it without out-of-band configuration | A conventional well-known path means one-shot discovery given only a hostname - the same pattern that makes OAuth server metadata and OpenID discovery work |
| # | An Agent Card MUST include at minimum 'name', 'description', 'url', 'version', and 'capabilities' fields so a discovering agent can decide whether and how to call it | A card without these fields is effectively opaque; peers cannot tell the agent's purpose, where to send requests, or which features it supports |
minimal-agent-card
A minimal but well-formed Agent Card with all required fields and a capabilities object declaring streaming support
- Value
- {"name":"ResearchAssistant","description":"Summarizes and cross-references research papers","url":"https://agents.example.com/research","version":"1.2.0","capabilities":{"streaming":true,"pushNotifications":false}}
card-missing-url
Invalid - an Agent Card without a url cannot be called; other agents have nowhere to send requests
- Value
- {"name":"ResearchAssistant","description":"A research agent"}
task-lifecycle
A2A work is expressed as tasks that move through an explicit state machine from submission to a terminal state
| Text | Rationale | |
|---|---|---|
| # | A task's 'state' field MUST be one of the defined values - 'submitted', 'working', 'input-required', 'completed', 'canceled', or 'failed' - and MUST NOT introduce custom states | A closed state set lets any client render progress and handle terminal conditions without negotiating a custom vocabulary per server |
| # | Once a task reaches a terminal state ('completed', 'canceled', or 'failed') the server MUST NOT transition it to any other state | Terminal states are the signal callers use to stop polling, release resources, and commit results; re-opening them would break every client's completion logic |
| Text | Rationale | |
|---|---|---|
| # | Servers SHOULD use the input-required state to pause execution when they need additional information from the caller, rather than failing the task and forcing a restart | The input-required state keeps task state and partial results intact so the caller can supply the missing input and resume without losing work |
task-transition
A typical task lifecycle with a mid-task pause for additional input before reaching the completed terminal state
- Value
- submitted -> working -> input-required -> working -> completed
completed-to-working
Invalid - completed is terminal and must not transition back to working
- Value
- completed -> working
message-format
A2A messages carry content as a list of typed parts rather than a single text blob, so agents can exchange text, files, and structured data in one turn
| Text | Rationale | |
|---|---|---|
| # | Every message MUST contain a 'parts' array where each element carries a 'type' field identifying the part as text, file, or data | Explicit part types let receivers route content correctly - displaying text, storing files, or feeding structured data into downstream tools - without sniffing the bytes |
| # | Every message MUST declare a 'role' of either 'user' or 'agent' so conversation history is unambiguous | Without a role, peers cannot reconstruct who said what and multi-turn reasoning breaks down |
mixed-parts-message
A well-formed user message mixing a text instruction with a file part referencing an externally hosted PDF
- Value
- {"role":"user","parts":[{"type":"text","text":"Summarize this attachment"},{"type":"file","file":{"name":"paper.pdf","mimeType":"application/pdf","uri":"https://files.example.com/paper.pdf"}}]}
bare-string-message
Invalid - messages must use the parts array, not a bare content string
- Value
- {"role":"user","content":"Summarize this"}
streaming
Clients can subscribe to a task over Server-Sent Events to receive status and artifact updates as work progresses
| Text | Rationale | |
|---|---|---|
| # | A server MUST NOT advertise streaming support in its Agent Card unless it actually supports the tasks/sendSubscribe method and delivers updates over Server-Sent Events | Advertising a capability that does not work forces every client to code defensive fallback paths and defeats the purpose of capability declaration |
| Text | Rationale | |
|---|---|---|
| # | Long-running streams SHOULD emit periodic heartbeat events so clients and intermediaries can distinguish a slow task from a dropped connection | Proxies and load balancers frequently close idle connections after 30-60 seconds; heartbeats keep the channel alive and give clients a clear liveness signal |
status-event
A well-formed SSE event streaming an in-progress task update with a named event and a JSON data payload
- Value
- event: task_status_update\ndata: {"taskId":"t-42","state":"working","progress":0.35}
unnamed-data-blob
Invalid - updates must be JSON payloads with known event names, not ad-hoc plain-text strings
- Value
- data: working 35%
authentication
A2A delegates authentication to standard HTTP schemes and declares which ones a server accepts in its Agent Card
| Text | Rationale | |
|---|---|---|
| # | An Agent Card MUST list the accepted authentication schemes under 'authentication.schemes' when the agent requires credentials, so callers know which scheme to use before sending a request | Without declared schemes the caller has to probe-and-fail; declaring them up-front avoids a round-trip per unsupported method |
| Text | Rationale | |
|---|---|---|
| # | Credentials MUST NOT be embedded inside message parts, task inputs, or artifact content - they belong in HTTP headers governed by the declared authentication scheme | Credentials in message bodies get logged, cached, and forwarded to downstream agents that have no business seeing them |
oauth-scheme
An Agent Card snippet declaring that the agent accepts bearer tokens for authentication
- Value
- {"authentication":{"schemes":["bearer"]}}
credential-in-part
Invalid - credentials must travel in HTTP headers, not in the task input stream
- Value
- {"role":"user","parts":[{"type":"text","text":"My API key is sk-abc123. Please query the database."}]}
push-notifications
Servers can deliver task updates to a caller-supplied webhook instead of requiring the caller to hold a streaming connection open
| Text | Rationale | |
|---|---|---|
| # | Push notification targets MUST be supplied by the caller at task creation time via 'pushNotification.url' - the server MUST NOT substitute its own destination | The caller, not the server, owns the security boundary around where task data is allowed to flow |
| Text | Rationale | |
|---|---|---|
| # | Servers SHOULD sign push notifications so the caller's webhook can verify the delivery originated from the expected agent and has not been tampered with | An unsigned webhook endpoint accepting task updates is trivially spoofable by anyone who learns the URL |
push-registration
A task creation snippet registering a caller webhook along with a shared secret token the server will echo back on each callback
- Value
- {"pushNotification":{"url":"https://client.example.com/a2a/callbacks/t-42","token":"cb-shared-secret"}}
server-chosen-url
Invalid - only the caller can designate where task data is pushed
- Value
- server silently redirects push notifications to its own monitoring endpoint instead of the caller URL
discovery
A2A specifies how agents advertise their Agent Cards at known locations so ecosystems can build registries without a central directory
| Text | Rationale | |
|---|---|---|
| # | An agent registry SHOULD aggregate Agent Cards by crawling the well-known path of participating domains or by accepting push submissions, and SHOULD expose a stable listing endpoint for consumers | Centralized directories become single points of failure and policy; crawl-and-aggregate lets ecosystems federate discovery while keeping each agent in control of its own card |
well-known-fetch
The canonical discovery call - an HTTP GET against the well-known Agent Card path returning the card body
- Value
- GET https://agents.example.com/.well-known/agent.json
proprietary-discovery-endpoint
Invalid for discovery purposes - a proprietary endpoint forces every consumer to learn a vendor-specific path instead of using the A2A standard
- Value
- GET https://agents.example.com/api/v2/whoami