Most intelligent systems built by different development teams or hosted on different platforms can’t interact with one another. This fragmentation forces engineering teams into vendor lock-in and creates complex, tightly coupled integration layers just to pass basic context between workflows. As enterprises deploy specialised agents (code-review assistants, automated HR coordinators, financial-ops bots) built on different stacks, standardized interoperability becomes critical.
This technical guide details the Agent2Agent (A2A) protocol specification. A2A reached v1.0 in 2026 and is governed under the Linux Foundation (which has hosted the project since June 2025). The protocol is supported by Google, Microsoft, Salesforce, ServiceNow, and others.
This guide provides the architectural patterns, data models, and JSON-RPC implementations required to build interoperable, compound AI systems, not agents operating in walled gardens. It bypasses high-level theory to deliver practical guidance for architects and platform engineers implementing agent-to-agent communication at scale.
What is the A2A (Agent2Agent) protocol?
The A2A protocol is an open specification that defines how independent AI agents discover each other, delegate tasks, and exchange data securely across different platforms.
The universal translator for siloed AI agents
A2A enables independent agents, built by different developers, hosted on different networks, to collaborate on complex tasks. It plays a similar role for autonomous agents that SMTP plays for email routing, providing a common, vendor-neutral standard. Its primary goal is to break interoperability barriers within the agentic AI ecosystem. By adopting an open spec, platform engineers prevent vendor lock-in and can route tasks across a federated network of specialized agents.
A2A vs MCP: How are they different?
The distinction between A2A and the Model Context Protocol (MCP) is a frequent point of confusion. The two protocols solve different integration problems:
- A2A facilitates collaboration between autonomous agents over a client-server protocol with fluid roles — any agent can act as the client or the server depending on the interaction. It defines how one reasoning system delegates a complex, multi-step task to another autonomous system, including negotiation, task state tracking, and long-running execution with streaming or webhook updates.
- MCP defines a client-to-tool model where an AI application connects to data sources, tools, and workflows. MCP uses two standard transports: stdio for local subprocesses (no network surface) and Streamable HTTP for remote servers. MCP isn’t designed for negotiating tasks with another autonomous entity, it’s designed for invoking tools.
| Feature | A2A protocol | MCP (Model Context Protocol) |
| Purpose | Task delegation and collaboration between autonomous agents. | Connecting an AI application to external data sources, tools, and workflows. |
| Interaction model | Client-server over HTTP(S) with fluid roles. Any agent can act as client or server depending on the call direction. | Client-to-tool. Local subprocess via stdio, or remote via Streamable HTTP. |
| Primary use case | Multi-agent orchestration and workflow handoffs across teams or vendors. | Giving an LLM access to a database, file system, search engine, or specialised function. |
| Complexity | High. Handles long-running tasks, state tracking, streaming, push notifications, multi-modal artifacts. | Lower. Synchronous request/response over JSON-RPC, with structured outputs. |
The hybrid architecture: Why you need both
In production enterprise systems, agents frequently implement both protocols at once. A scalable compound AI application relies on A2A for high-level orchestration and MCP for concrete tool execution.
Consider a corporate onboarding workflow. An A2A “HR Agent” receives a request to schedule a technical interview and delegates the scheduling task via A2A to a specialized “Calendar Agent.” The Calendar Agent receives the A2A message, understands the intent, and uses MCP locally to call its calendar tool to query open slots. Once it has a slot, the Calendar Agent finalizes the booking and transmits the completed artifact back to the HR Agent over the A2A channel.
The core A2A protocol architecture and components
The A2A architecture is a layered communication framework composed of canonical data models, abstract operations, and explicit transport bindings.
The three-layer specification model
The official A2A specification is explicitly separated into three layers (Section 1.3 of the spec). This separation lets developers swap transport mechanisms without rewriting their core agent logic.
- Layer 1: Canonical data model. The core nouns (AgentCard, AgentSkill, Task, Message, Part, Artifact, Extension). Defined in Protocol Buffers and also published as JSON Schema 2020-12 (a2a.json), auto-generated from the protos.
- Layer 2: Abstract operations. The core verbs (SendMessage, SendStreamingMessage, GetTask, ListTasks, CancelTask, SubscribeToTask), plus the push-notification config family and the Agent Card retrieval operations.
- Layer 3: Protocol bindings. Three concrete bindings ship with v1.0: JSON-RPC 2.0 over HTTPS (the most common deployment), gRPC, and HTTP+JSON/REST. Each interface advertises its binding and protocolVersion in the Agent Card’s supportedInterfaces[].
Agent Cards: The discovery and capability manifest
An Agent Card is a public JSON document served at the well-known URI /.well-known/agent-card.json (per RFC 8615). It’s an agent’s technical manifest, including its name, capabilities, accepted authentication, the URL and binding to reach it at, and the list of skills it offers.
Here is a v1.0-compliant Agent Card for a corporate flight-booking agent:
{
“name”: “Corporate Flight Booking Agent”,
“description”: “Handles domestic and international flight reservations.”,
“version”: “1.0.0”,
“provider”: {
“organization”: “Corp Internal”,
“url”: “https://internal.corp”
},
“supportedInterfaces”: [
{
“url”: “https://flights.internal.corp/a2a”,
“protocolBinding”: “JSONRPC”,
“protocolVersion”: “1.0”
}
],
“capabilities”: {
“streaming”: true,
“pushNotifications”: true,
“extendedAgentCard”: false
},
“defaultInputModes”: [“text/plain”, “application/json”],
“defaultOutputModes”: [“application/json”, “text/plain”],
“skills”: [
{
“id”: “book_flight”,
“name”: “Book a flight”,
“description”: “Books a flight given origin, destination, and exact dates.”,
“tags”: [“travel”, “booking”],
“examples”: [“Book SFO to LAX on July 25 for employee 4432”],
“inputModes”: [“text/plain”, “application/json”],
“outputModes”: [“application/json”]
}
],
“securitySchemes”: {
“corporate_oauth”: {
“type”: “oauth2”,
“flows”: {
“clientCredentials”: {
“tokenUrl”: “https://auth.internal.corp/token”,
“scopes”: {
“flights:read”: “Read internal flight schedules”,
“flights:write”: “Book flights”
}
}
}
}
},
“security”: [
{ “corporate_oauth”: [“flights:read”, “flights:write”] }
],
“signatures”: [
/* AgentCardSignature entries (JWS, RFC 7515) — see security section */
]
}
A few details worth calling out:
- The top-level fields are name, description, version, provider, supportedInterfaces[], capabilities, defaultInputModes/defaultOutputModes, skills[], securitySchemes, security, and signatures.
- Operations the agent can perform live in skills[] (each AgentSkill has id, name, description, tags, examples, inputModes, outputModes).
- The capabilities object holds boolean feature flags (streaming, pushNotifications, extendedAgentCard), not named operations.
- Authentication is declared in securitySchemes (same shape as OpenAPI 3 Security Schemes) and which scheme is required is declared in security (mapping scopes to skills when finer control is needed).
AgentCardSignature: Tamper-evident Agent Cards
In production, the Agent Card itself is an attack surface. Anyone who can rewrite the description or supportedInterfaces[] can redirect callers or smuggle prompt-injection content into the meta-prompt of a victim agent.
The A2A spec defines AgentCardSignature for exactly this: a JSON Web Signature (RFC 7515) computed over a canonicalized form of the card (JSON Canonicalization Scheme, RFC 8785) so signatures are stable across serializers.
The companion a2a-utils package ships utilities like create_agent_card_signer. Clients verify the signatures field against the issuer’s public key (typically held in a curated registry or x5c chain) before trusting the card.
The task lifecycle: From submission to completion
Every interaction in A2A revolves around a strict task state machine. When an agent receives a SendMessage call, it either returns a Message directly (for trivial responses) or instantiates a Task and transitions it through the states defined in Section 4.1.3 of the spec. The full set, with v1.0 prefixed names:
| TaskState (v1.0) | Category | Definition | Typical agent action |
| TASK_STATE_UNSPECIFIED | default | Unknown or indeterminate state | Treat as error; investigate |
| TASK_STATE_SUBMITTED | in-flight | Task accepted but not yet started | Queue for execution |
| TASK_STATE_WORKING | in-flight | Agent is actively processing | Emit TaskStatusUpdateEvent/TaskArtifactUpdateEvent on the SSE stream |
| TASK_STATE_INPUT_REQUIRED | interrupted | Paused awaiting user or agent feedback | Prompt client for additional context; resume with a new SendMessage call |
| TASK_STATE_AUTH_REQUIRED | interrupted | Caller must provide additional credentials | Return a 401-style indication via the task; client refreshes its token and retries |
| TASK_STATE_COMPLETED | terminal | Task finished successfully | Return final artifacts; SSE stream closes |
| TASK_STATE_FAILED | terminal | Unrecoverable error during processing | Emit error code and description; SSE stream closes |
| TASK_STATE_CANCELED | terminal | Task canceled before completion (typically via CancelTask) | Release resources; SSE stream closes |
| TASK_STATE_REJECTED | terminal | Agent refused to perform the task (policy, capability, or quota) | Return a descriptive error; SSE stream closes |
Because AI tasks rely on LLM processing, execution times are highly variable. Polling GetTask works but creates network strain. The A2A spec resolves this with Server-Sent Events:
- Clients call SendStreamingMessage (to submit and stream in one step) or SubscribeToTask (to attach to an existing task)
- The server responds with Content-Type: text/event-stream, and pushes Task/TaskStatusUpdateEvent/TaskArtifactUpdateEvent objects until the task reaches a terminal state
- At that point, the stream closes. In v1.0 the stream-closure mechanism replaces the v0.x final: true boolean.
For very long-running or disconnected scenarios, A2A also defines Push Notifications:
- The server POSTs StreamResponse payloads to a webhook URL the client registered via CreateTaskPushNotificationConfig.
- The webhook authenticates the incoming notification via PushNotificationConfig.authentication (signed JWT, HMAC, or mTLS).
Messages, parts, and multi-modal artifacts
Agents exchange state and contextual data using Message and Artifact objects. A Message carries a role (“user”/”agent” or, in v1.0 ProtoJSON, ROLE_USER/ROLE_AGENT) and an array of Part objects. A Part holds exactly one of: text (string), data (structured JSON), url (URI reference to external content), or raw (inline byte array), and can optionally carry mediaType, filename, and metadata. Artifacts are the concrete deliverables produced during a task. Each has an artifactId, a name, and a parts array.
This design lets an orchestrating agent transmit a single A2A message (containing, say, a text instruction, a JSON parameter block, and a binary PDF attachment, all in one canonical envelope) and lets the receiving agent parse and process each part by its declared mediaType.
How to implement a basic A2A workflow (with code)
Implementing an A2A workflow is a standardized sequence: capability discovery, task submission via JSON-RPC, asynchronous state subscription via SSE, and final artifact retrieval.
Step 1: Discovery and skill parsing
Before transmitting a task, a client must verify that the remote agent advertises the required skill. It does this by GET-ting the Agent Card from /.well-known/agent-card.json and inspecting skills[].
import requests
remote_agent_url = “https://flights.internal.corp”
response = requests.get(f”{remote_agent_url}/.well-known/agent-card.json”)
agent_card = response.json()
# Optional: verify the AgentCardSignature against a trusted issuer key here.
# See a2a.utils.verify_agent_card_signature in the a2a-utils package.
skill_ids = {s[‘id’] for s in agent_card.get(‘skills’, [])}
if ‘book_flight’ not in skill_ids:
raise Exception(‘Target agent does not advertise the book_flight skill.’)
# Pick the interface to call (typically supportedInterfaces[0]).
interface = agent_card[‘supportedInterfaces’][0]
endpoint_url = interface[‘url’] # e.g. https://flights.internal.corp/a2a
binding = interface[‘protocolBinding’] # JSONRPC / GRPC / HTTP_JSON
Step 2: Sending a task with SendMessage
Task initiation uses the JSON-RPC binding. The client POSTs a SendMessage call to the agent’s endpoint URL. Credentials live in HTTP headers (never in the payload), per the spec’s enterprise-readiness guidance.
POST /a2a HTTP/1.1
Host: flights.internal.corp
Authorization: Bearer eyJhbGciOiJFUzI1NiI… # OAuth2 access token
Content-Type: application/json
Accept: application/json, text/event-stream
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “SendMessage”,
“params”: {
“message”: {
“role”: “ROLE_USER”,
“parts”: [
{ “text”: “Book a direct flight from SFO to LAX on July 25th for employee ID 4432.” }
]
},
“configuration”: { “returnImmediately”: true }
}
}
Notes on the shape:
- jsonrpc and id follow JSON-RPC 2.0.
- method maps directly to an A2A abstract operation (SendMessage in v1.0; the legacy v0.x name was message/send).
- params.message is a Message object containing one or more Part entries.
- There is no task_id in the request. The server generates the taskId and returns it in the response.
- configuration.returnImmediately = true tells the server to return the Task immediately so the client can attach an SSE stream while the agent works (in v1.0 the default is blocking; v0.x used a blocking field with inverted polarity).
Step 3: Subscribing to task updates with SSE
Polling GetTask repeatedly is wasteful. Instead, open an SSE stream against the same agent endpoint by calling SubscribeToTask (or use SendStreamingMessage to submit and stream in one round-trip). The server replies with Content-Type: text/event-stream and pushes events until the task reaches a terminal state.
POST /a2a HTTP/1.1
Host: flights.internal.corp
Authorization: Bearer eyJhbGciOiJFUzI1NiI…
Content-Type: application/json
Accept: text/event-stream
{
“jsonrpc”: “2.0”,
“id”: 2,
“method”: “SubscribeToTask”,
“params”: { “taskId”: “task-9876-abcd” }
}
Each SSE event carries a StreamResponse object containing one of: task, statusUpdate, artifactUpdate, or message:
data: { “task”: { “id”: “task-9876-abcd”, “status”: { “state”: “TASK_STATE_SUBMITTED” } } }
data: { “statusUpdate”: { “taskId”: “task-9876-abcd”, “status”: { “state”: “TASK_STATE_WORKING” } } }
data: { “artifactUpdate”: { “taskId”: “task-9876-abcd”, “artifact”: { “artifactId”: “a1”, “name”: “itinerary”, “parts”: […] } } }
data: { “statusUpdate”: { “taskId”: “task-9876-abcd”, “status”: { “state”: “TASK_STATE_COMPLETED” } } }
Notes:
- v1.0 removed the v0.x kind discriminator field. Event type is now determined by the JSON member name (statusUpdate vs artifactUpdate).
- The v0.x final: true boolean is gone too; the stream closes when the task hits a terminal state.
Step 4: Retrieving the final result
If the client missed the artifactUpdate event (e.g. it joined the stream late), it can call GetTask to fetch the completed Task with its full history and artifacts:
POST /a2a HTTP/1.1
Host: flights.internal.corp
Authorization: Bearer eyJhbGciOiJFUzI1NiI…
Content-Type: application/json
{
“jsonrpc”: “2.0”,
“id”: 3,
“method”: “GetTask”,
“params”: { “taskId”: “task-9876-abcd”, “historyLength”: 0 }
}
The server returns the completed Task object, which carries the final multi-modal artifacts (e.g. a confirmation message plus a structured JSON itinerary). Managing long-running streams and webhook connections is operationally non-trivial; platform teams frequently rely on an API gateway (such as Tyk) to handle connection persistence, timeout management, OAuth validation, and observability for these asynchronous AI workloads.
Security architecture in practice: OAuth2, OIDC, API keys, and mTLS
Enterprise A2A security relies on enforcing transport-level encryption, cryptographically verifying the Agent Card, and authenticating every call. The spec aligns its security model with the OpenAPI Specification: agents declare the schemes they accept in the AgentCard’s securitySchemes field, and which scheme is required (with which scopes) in security.
What security schemes does A2A support?
The A2A spec supports the standard OpenAPI security schemes:
| Security scheme | Primary purpose | Enforcement layer | Spec basis |
| OAuth 2.0 | Application-level authorization. Tokens carry scope claims that map to skill IDs. | Application (HTTP) | OpenAPI Security Scheme “oauth2” |
| OpenID Connect (OIDC) | Identity assertion: cryptographically prove who the calling user or agent is. | Application (HTTP) | OpenAPI Security Scheme “openIdConnect” |
| API key | Simple machine-to-machine authentication where granular scoping is not required. | Application (HTTP header) | OpenAPI Security Scheme “apiKey” |
| Mutual TLS (mTLS) | Transport encryption plus bidirectional identity. Both client and server present X.509 certs before any application data flows. | Transport (TLS) | OpenAPI Security Scheme “mutualTLS” |
Example: Configuring an OAuth2 scheme with skill-scoped authorization
Agents declare their security requirements openly in the Agent Card. The block below shows an OAuth2 scheme combined with a security entry that maps scopes to the skill that needs them:
“securitySchemes”: {
“corporate_oauth”: {
“type”: “oauth2”,
“flows”: {
“clientCredentials”: {
“tokenUrl”: “https://auth.internal.corp/token”,
“scopes”: {
“agent:delegate”: “Allow task delegation from another agent”,
“flights:read”: “Read internal flight schedules”,
“flights:write”: “Book flights”
}
}
}
}
},
“security”: [
{ “corporate_oauth”: [“agent:delegate”, “flights:write”] }
]
A client agent reads this block, runs a client-credentials flow against tokenUrl to obtain a JWT, and presents the token in the Authorization header on every SendMessage call. The server validates iss/aud/exp/scope (and caches jti for replay protection) before authorizing the requested skill.
Combining security schemes for defense-in-depth
Production deployments combine mTLS with OAuth2 to create a zero-trust posture. A malicious actor who intercepts an OAuth bearer token still can’t replay it from a different machine, because the TLS handshake will fail without the legitimate client certificate.
For higher assurance without the operational overhead of full mTLS, the spec’s recommended alternatives are sender-constrained tokens: RFC 8705 mTLS-bound tokens or RFC 9449 DPoP. Either makes a stolen token useless without the matching key.
Securing distributed agent interactions also requires observability. A2A’s enterprise-readiness guidance recommends OpenTelemetry with W3C Trace Context headers (traceparent/tracestate) on every A2A call so a single user request can be traced across the entire agent chain.
Deploying an API management platform such as Tyk gives you a central policy enforcement point that terminates mTLS, validates OAuth tokens, propagates trace context, and logs every call, before any malicious request ever reaches the agent server.
Production pitfalls and current protocol limitations
A2A v1.0 establishes a strong baseline for interoperability, but architects must engineer application-layer workarounds for a few specific gaps.
Per-skill body schemas
Each AgentSkill declares accepted media types via inputModes/outputModes (e.g. text/plain, application/json, image/png), but the spec doesn’t standardize per-skill JSON Schema for the body content within a Part. So a client agent can know that a skill accepts application/json, but not exactly what JSON object structure the receiver expects. Two practical mitigations:
- Embed an OpenAPI fragment or JSON Schema inside the skill’s description, or in an Extension declared on the AgentCard (A2A Extensions are formalized in Section 4.6 of the spec).
- Generate the schema from your server-side Pydantic/Protobuf types and publish it in the Agent Card so callers can validate before transmitting.
Authorization downscoping in delegation chains
Authorization creep occurs when a highly privileged orchestrator delegates a sub-task to a less-privileged agent and passes along a broad bearer token. The A2A spec declares how to transmit credentials but is silent on how to downscope them. The pattern that works is OAuth 2.0 Token Exchange (RFC 8693): The orchestrator trades its user-level token for a tightly-scoped, short-lived token meant for the specific downstream skill, presenting that narrow token to the delegated agent rather than the original. Combine with RFC 9396 Rich Authorization Requests for transaction-bound tokens (e.g. “this token authorizes booking exactly this flight for exactly this employee”).
Discovery beyond the well-known URI
Discovery in A2A is layered. The spec defines three strategies in the Agent Discovery topic page:
- Well-known URI: A client knows the domain and GETs /.well-known/agent-card.json. Best for public agents.
- Curated registry: A central registry holds vetted Agent Cards and lets clients query by skill, tag, or provider. Best for enterprise marketplaces. The spec doesn’t yet standardize a registry API, so this remains a build-it-yourself area.
- Direct configuration: Hardcoded URLs or config files. Fine for tightly-coupled internal systems.
Most enterprises end up running an internal registry or developer portal to catalog agent capabilities. AgentCardSignature plays well with this: The registry only accepts cards signed by trusted issuers, which prevents both shadowing attacks and silent rug-pulls.
Frequently asked questions
What is the main difference between A2A and LangChain?
A2A is a standardized communication specification for interoperability between independent, fully built AI agents running on different networks. LangChain is a development framework that engineers use to build a single agent by chaining LLMs, tools, and vector databases. They solve different engineering problems:
- LangChain builds an intelligent application.
- A2A lets that LangChain-built application talk securely to an agent built using CrewAI, the OpenAI Agents SDK, PydanticAI, or any other framework that publishes an Agent Card.
Is the A2A protocol ready for production use?
Yes. A2A v1.0 is the current stable release (2026), governed under the Linux Foundation. The core data models and protocol bindings are stable. Architects should still expect to engineer application-level workarounds for the gaps named above (per-skill body schemas, token downscoping, registry standardisation) but the protocol itself is no longer a moving target.
How does A2A handle errors and retries?
The protocol defines a standard error object in its JSON-RPC binding. If an operation fails, the server returns a specific error code and a descriptive message, either synchronously in the immediate RPC response or asynchronously by transitioning the task to TASK_STATE_FAILED or TASK_STATE_REJECTED. Retry logic and circuit-breaking are deliberately left to the client implementation; the protocol focuses on state and message communication.
Does Tyk support the A2A protocol?
Tyk natively manages, secures, and observes the standard HTTP and gRPC endpoints that power any A2A-compliant agent. By deploying Tyk Gateway in front of A2A servers, platform engineers can enforce rate limiting, validate OAuth 2.0/OIDC tokens, terminate mTLS connections, propagate W3C trace context, and capture detailed telemetry, all without modifying agent code.
Can A2A be used with gRPC instead of HTTP/JSON-RPC?
Yes. A2A’s architecture is strictly layered and the abstract operations and canonical data model are transport-agnostic. The v1.0 spec defines three protocol bindings (JSON-RPC 2.0, gRPC, and HTTP+JSON/REST) and each interface in supportedInterfaces[] advertises its own protocolBinding. JSON-RPC is the most common public deployment; gRPC is highly efficient for low-latency internal microservice communication.
Conclusion
The A2A protocol establishes the open standard for agent-to-agent collaboration. It directly addresses the interoperability bottlenecks that currently isolate enterprise AI systems, replacing tightly-coupled, custom integrations with a predictable, vendor-neutral specification.
As compound AI applications scale in complexity, single-framework approaches will inevitably fail. A2A works in tandem with MCP: A2A manages the high-level orchestration and negotiation between agents, while MCP handles the localized execution of tools and data sources. Production-ready implementations combine signed Agent Cards, OAuth-scoped skill authorization, transport-layer encryption (TLS 1.2+; mTLS or sender-constrained tokens for high assurance), and observability through OpenTelemetry.
Stop dealing with shadow AI and fragmented integrations. Start centralizing your API and AI governance. Explore how Tyk’s MCP Gateway delivers enterprise-grade control, security, and observability for any protocol, including A2A.