A2A (Agent2Agent Protocol): Building Interoperable Multi-Agent Systems
Understanding how AI agents discover, communicate, delegate tasks, and collaborate across different frameworks and platforms.
1. What Is A2A, and What Problem Does It Solve?
As AI applications become more capable, we are moving beyond simple interactions between a user and a single AI model.
Today, an AI agent can search documentation, query databases, interact with APIs, analyze code, and execute multi-step workflows. However, as these capabilities grow, a new architectural question emerges:
How can independently developed AI agents communicate and work together?
Consider an application that needs to analyze a GitLab merge request. The task may involve several different responsibilities:
- A security agent reviews potential vulnerabilities.
- A testing agent analyzes test coverage and identifies missing cases.
- An architecture agent evaluates design decisions and code structure.
- A review agent combines the findings and prepares a final report.
Each agent might use a different framework, model provider, toolset, or programming language.
Without a standardized communication mechanism, integrating these agents usually requires custom APIs, shared data contracts, and application-specific orchestration logic.
This is where Agent2Agent (A2A) Protocol becomes relevant.
A2A is an open protocol designed to enable communication and collaboration between independent AI agent systems. It provides a common interaction model regardless of the framework, programming language, or vendor used to build an agent.
The protocol was originally developed by Google and contributed to the Linux Foundation. It is designed around interoperability without requiring agents to expose their internal reasoning, memory, tools, or implementation details.
The important distinction is that A2A does not define how an agent thinks or performs its internal tasks. It defines how one agent can communicate with another.
In other words, an agent can be implemented using LangGraph, Google ADK, CrewAI, Semantic Kernel, or a completely custom solution and still communicate with another compatible agent through A2A.
Reference: A2A Protocol — Official Documentation
2. Why Do We Need A2A?
Most agent frameworks provide mechanisms for calling tools, delegating work, or coordinating multiple agents.
However, these mechanisms are generally designed around the framework's own execution model.
For example, an agent built with one framework may have its own task execution logic, state management, and internal tool definitions. Connecting it directly to an agent developed using another framework can require custom integration work.
A2A addresses the interoperability problem.
Instead of requiring agents to understand each other's internal implementations, they communicate through a standardized protocol.
This gives us several important capabilities:
- Discover agents and understand their capabilities.
- Delegate tasks to remote agents.
- Exchange messages and structured data.
- Track the lifecycle of long-running tasks.
- Receive progress updates through streaming.
- Exchange files and generated artifacts.
- Support authentication and authorization across agent boundaries.
A2A is particularly useful when agents belong to different teams, applications, organizations, or technology stacks.
The goal is not to turn every agent into the same implementation. The goal is to allow different implementations to collaborate.
3. A2A vs. MCP vs. ACP
These three protocols are often discussed together, but they solve different architectural problems.
| Technology | Main responsibility |
|---|---|
| MCP | Agent ↔ Tools and external context |
| ACP | Coding Agent ↔ Client Application |
| A2A | Agent ↔ Agent |
Let's consider a practical example.
Imagine a research agent that needs to investigate a topic, retrieve information from internal documentation, and delegate a part of its work to a specialized analysis agent.
In this scenario:
1. MCP provides access to documentation, APIs, databases, and other tools.
2. A2A allows the research agent to communicate with the analysis agent.
3. ACP could be used separately when integrating a coding agent with an editor or another client application.
A simplified architecture:
User
|
v
Main AI Application
|
v
Coordinator Agent
/ \
/ \
A2A A2A
/ \
v v
Research Agent Analysis Agent
| |
MCP MCP
| |
Internal APIs Database / APIs
| |
v v
Results Results
\ /
\ /
Coordinator
|
v
Final Response
Each agent can use its own tools and internal architecture. A2A provides the communication layer between them.
One important point: A2A is not a replacement for an agent framework or an internal sub-agent mechanism. It focuses on communication between independent agent applications. Internal orchestration can still be handled by the framework that runs the agent.
References: A2A and MCP · MCP Documentation · ACP Documentation
4. How Does A2A Work?
To understand A2A, we need to understand a few core concepts.
4.1. Agent Card: Discovering Other Agents
One of the most important concepts in A2A is the Agent Card.
An Agent Card is a JSON document that describes an agent and its capabilities.
It acts as a machine-readable description of what an agent can do and how another agent can communicate with it.
An Agent Card can contain:
- Agent name and description
- Service endpoint
- Supported protocol versions and transports
- Available capabilities
- Supported input and output formats
- Skills and example tasks
- Authentication requirements
By default, an A2A server exposes its Agent Card at:
https://agent.example.com/.well-known/agent-card.json
For example, a research agent might expose the following description:
{
"name": "Research Agent",
"description": "An agent specialized in research and source analysis.",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://research.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": true,
"extensions": []
},
"defaultInputModes": [
"text/plain"
],
"defaultOutputModes": [
"text/plain",
"application/json"
],
"skills": [
{
"id": "market-research",
"name": "Market Research",
"description": "Researches topics and produces structured findings.",
"tags": [
"research",
"analysis"
],
"examples": [
"Research the latest developments in AI agents."
],
"inputModes": [
"text/plain"
],
"outputModes": [
"text/plain",
"application/json"
]
}
]
}
This is a simplified public Agent Card example. Authentication and additional security requirements can also be described through the protocol's security fields.
A client can retrieve this document and understand whether the remote agent is suitable for a particular task.
For example, a coordinator agent looking for an agent capable of analyzing financial documents can inspect registered Agent Cards and evaluate their declared skills.
Agent discovery can happen through:
- Well-known URLs
- Private or public agent registries
- Directly configured Agent Card URLs
An Agent Card describes an agent's advertised capabilities; it does not prove that the agent can successfully perform every advertised task. Capability selection should be combined with authorization, availability checks, and runtime evaluation.
Reference: A2A — Agent Discovery
4.2. Messages and Tasks
A2A distinguishes between a message and a task.
Message
A message represents communication between a client and an agent. It can contain instructions, clarification requests, or additional information.
Task
A task represents a stateful unit of work with its own lifecycle.
For example, when an agent receives a request to analyze a large document, it may need several minutes to complete the work. Instead of treating the interaction as a simple request-response operation, A2A allows the agent to create a task and report its progress.
A task has a lifecycle that can include:
| State | Meaning |
|---|---|
| Submitted | Task has been accepted |
| Working | Agent is processing the task |
| Input Required | Additional information is needed |
| Auth Required | Additional authentication is required |
| Completed | Task finished successfully |
| Failed | Task execution failed |
| Canceled | Task was canceled |
| Rejected | Agent declined the task |
Some states represent interruptions that can be resolved through further interaction, while others represent terminal states.
This is important for long-running workflows because the client does not need to keep a single request open until every operation is complete.
A2A also provides two useful identifiers:
- taskId: Identifies an individual task.
- contextId: Groups related messages and tasks within a broader interaction context.
The task identifier is generated by the agent server. The context identifier helps maintain continuity across multiple related interactions.
4.3. Artifacts: Sharing Task Results
A2A also introduces the concept of Artifacts.
An artifact represents the output produced by an agent while completing a task.
For example:
- A research agent can produce a research report.
- A coding agent can return a generated file.
- An analysis agent can return structured JSON.
- A document processing agent can return an extracted document.
An artifact can contain multiple parts, including text, binary content, URLs, or structured data.
This distinction is important:
Messages are primarily used for communication and interaction, while artifacts represent the actual outputs of a task.
It allows agents to exchange more than just text-based responses.
4.4. Streaming and Asynchronous Communication
Not every agent task finishes immediately.
A research process, document analysis, or code review can take a considerable amount of time.
A2A supports multiple ways of receiving task updates:
Streaming
The client can subscribe to task updates and receive events while the agent is working.
Depending on the protocol binding, streaming is supported through Server-Sent Events (SSE) or gRPC streaming.
Polling
The client can periodically request the current task state.
Push Notifications
For long-running tasks where maintaining a persistent connection is not practical, the agent can send updates to a client-provided webhook.
These mechanisms allow applications to support different execution patterns without inventing custom communication protocols for each agent.
A2A also defines operations for sending messages, retrieving tasks, canceling tasks, subscribing to updates, and managing push notification configurations.
References: A2A Specification · Task Lifecycle · Streaming and Asynchronous Operations
5. How to Integrate A2A?
A2A provides official SDKs for several programming languages, including:
- Python
- JavaScript / TypeScript
- Java
- Go
- C# / .NET
- Rust
For TypeScript applications, the official package is @a2a-js/sdk.
The current SDK implements the A2A v1.0 specification and supports client and server development.
Step 1: Install the package
pnpm add @a2a-js/sdk
The official JavaScript SDK requires Node.js 20 or later.
For this example, we will implement a client that communicates with an existing remote research agent.
Step 2: Connect to an A2A agent
The SDK provides ClientFactory to create a client.
import { ClientFactory } from '@a2a-js/sdk/client';
const factory = new ClientFactory();
const client = await factory.createFromUrl(
'https://research-agent.example.com'
);
The SDK retrieves the Agent Card and selects a compatible transport based on the agent's advertised interfaces and the client's supported transports.
This means we do not need to hardcode every protocol detail into our application.
Step 3: Send a message to the agent
Once the connection is established, we can send a task request.
import {
Role,
type Message,
type SendMessageRequest,
} from '@a2a-js/sdk';
const message: Message = {
messageId: crypto.randomUUID(),
role: Role.ROLE_USER,
parts: [
{
content: {
$case: 'text',
value: 'Research the latest developments in multi-agent systems.',
},
mediaType: 'text/plain',
filename: '',
metadata: undefined,
},
],
taskId: '',
contextId: '',
extensions: [],
metadata: {},
referenceTaskIds: [],
};
const request: SendMessageRequest = {
tenant: '',
message,
configuration: undefined,
metadata: {},
};
const result = await client.sendMessage(request);
console.log(result);
The example uses the SDK's generated TypeScript message types. Depending on the agent's behavior, the response can contain a message or a task.
For production integrations, authentication and request timeouts should be configured as well.
For example:
const result = await client.sendMessage(request, {
serviceParameters: {
Authorization: `Bearer ${process.env.A2A_TOKEN}`,
},
signal: AbortSignal.timeout(60_000),
});
The client can now communicate with a remote agent without needing to know its internal framework or model implementation.
Step 4: Stream task updates
For long-running tasks, we can use sendMessageStream.
const stream = client.sendMessageStream(request);
for await (const event of stream) {
// Handle task events:
// - task
// - statusUpdate
// - artifactUpdate
console.log(event);
}
The actual event union should be handled using the SDK's generated types.
This allows us to display task progress, process generated artifacts, and update the application as the remote agent works.
The SDK also supports task retrieval, cancellation, and push notification configuration.
Step 5: Build your own A2A agent
A2A is not limited to consuming existing agents. We can also expose our own agent through the protocol.
The official JavaScript SDK separates server-side responsibilities into three main components:
AgentExecutor
Contains the agent's business logic. It receives the request context, executes the task, and publishes messages, status updates, and artifacts.
DefaultRequestHandler
Handles protocol-level responsibilities such as message routing, task storage, cancellation, and push notifications.
Transport Adapters
Expose the agent through supported communication protocols.
The SDK provides adapters for JSON-RPC and HTTP+JSON/REST through Express, as well as gRPC for Node.js.
The architecture looks like this:
Incoming A2A Request
|
v
Transport Adapter
|
v
DefaultRequestHandler
|
+----> Task Store
|
+----> Execution Event Bus
|
v
AgentExecutor
|
v
Agent Logic
|
+----> LLM
+----> MCP Tools
+----> Internal APIs
+----> Business Logic
|
v
Messages / Status / Artifacts
|
v
A2A Response
The SDK also supports custom task stores and event buses, which allows us to integrate persistent databases, caches, or message brokers instead of relying exclusively on in-memory implementations.
For production, this separation is particularly useful because protocol handling, task execution, and persistence can evolve independently.
Reference: Official A2A JavaScript SDK
6. Integrating A2A with the Vercel AI SDK
There is another interesting integration worth looking at.
The Vercel AI SDK lists an A2A community provider called a2a-ai-provider.
This package allows an A2A-compatible agent to be consumed through familiar AI SDK functions such as generateText and streamText.
This is conceptually similar to the ACP provider we discussed previously.
Instead of implementing every A2A interaction manually, we can use the community provider as an adapter between the A2A protocol and the AI SDK.
Installation
pnpm add ai a2a-ai-provider
Provider configuration
import { a2a } from 'a2a-ai-provider';
import { generateText } from 'ai';
const result = await generateText({
model: a2a(
'https://research-agent.example.com/.well-known/agent-card.json'
),
prompt: 'Research the latest developments in agentic AI.',
});
console.log(result.text);
Streaming responses
We can also use streamText:
import { a2a } from 'a2a-ai-provider';
import { streamText } from 'ai';
const result = streamText({
model: a2a(
'https://research-agent.example.com/.well-known/agent-card.json'
),
prompt: 'Analyze the current multi-agent ecosystem.',
providerOptions: {
a2a: {
contextId: crypto.randomUUID(),
},
},
});
await result.consumeStream();
console.log(await result.content);
Here, contextId is used to associate related interactions with the same context.
This integration makes it easier to use A2A agents in existing AI SDK applications.
However, there is an important distinction:
The official A2A SDK is the protocol implementation, whereas a2a-ai-provider is a community-maintained adapter.
The community provider's documentation describes it as under active development, and its GitHub repository currently labels it alpha and advises against production workloads.
For that reason, I would use it for experimentation and evaluate the official SDK directly for production-critical integrations until the provider's maturity and compatibility are established.
References: AI SDK — A2A Community Provider · a2a-ai-provider GitHub
7. Supported Communication Protocols
A2A separates its common data model and operations from the transport used to communicate.
The current specification defines three standard protocol bindings:
| Protocol | Description |
|---|---|
| JSON-RPC | JSON-based remote procedure calls |
| HTTP+JSON / REST | Resource-oriented HTTP communication |
| gRPC | RPC communication using Protocol Buffers and gRPC |
An agent can support more than one binding.
For example, a service could expose JSON-RPC and REST interfaces using the same underlying task execution logic.
This separation is valuable because clients and servers can select a compatible transport without changing the fundamental meaning of tasks, messages, and artifacts.
The official JavaScript SDK supports all three transports, with its gRPC integration requiring a Node.js environment and additional dependencies.
A2A also defines protocol versioning. Clients and agents use the A2A-Version header to identify the version they are communicating with.
The current v1.0 specification includes changes from the earlier v0.3 interface, so compatibility and migration should be considered when connecting existing implementations.
Reference: A2A Protocol Specification
8. Advantages of A2A
Interoperability across frameworks
One of A2A's biggest advantages is the ability to connect agents built using different frameworks and programming languages.
We do not have to standardize every team's internal agent implementation just to enable collaboration.
A Python-based research agent can communicate with a TypeScript-based coordinator, while another agent may be implemented using Go or .NET.
This reduces framework-level coupling.
Independent agent implementations
Agents do not need to expose their internal prompts, model configurations, memory, or tools.
They only need to expose their supported capabilities and implement the agreed communication contract.
This allows teams to evolve their internal implementations without forcing all consumers to change their integration code.
Better support for long-running workflows
A2A is designed around stateful tasks.
Instead of implementing custom job tracking, status polling, event streaming, and result retrieval for every remote agent, we can use standardized operations.
This is particularly useful for research, document processing, code analysis, and other workflows that may take several minutes or longer.
More flexible output handling
Agents can exchange text, files, structured data, and other supported content types through message parts and artifacts.
This makes the protocol useful beyond conventional text-based chat applications.
Centralized agent discovery and integration
Agent Cards allow applications to discover what an agent offers and how to communicate with it.
Combined with a curated internal agent registry, this can provide a common discovery layer for enterprise AI platforms.
9. Limitations and Trade-offs
A2A solves a real interoperability problem, but it also introduces architectural complexity.
It does not provide orchestration by itself
A2A defines how agents communicate, not how an entire multi-agent system should be planned and executed.
It does not decide which agent should receive a task, how many agents should participate, or how conflicting results should be resolved.
Those responsibilities remain with an orchestrator or an agent framework.
For example, LangGraph, Google ADK, or a custom coordinator can decide when to delegate a task through A2A.
Distributed execution introduces more failure scenarios
When agents communicate across network boundaries, new failure scenarios appear:
- A remote agent may become unavailable.
- A request may time out while the task continues running.
- A task may fail after partial execution.
- A client may disconnect before receiving the final artifact.
- A downstream agent may return an incomplete or unexpected result.
Therefore, retries, cancellation, idempotency, timeout policies, and persistent task storage need to be considered carefully.
More communication can increase latency and cost
Every agent handoff introduces additional work.
A complex multi-agent workflow may involve several model calls, network requests, and tool executions.
Using more agents does not automatically improve the result.
We should introduce agent delegation only when the additional specialization or parallelism justifies the extra latency, cost, and operational complexity.
Agent compatibility does not guarantee equivalent capabilities
Two agents may both implement A2A but provide very different skills and execution behavior.
A2A standardizes communication, not the quality or accuracy of an agent.
Clients still need to evaluate supported capabilities, input/output formats, and the reliability of the remote service.
Version compatibility requires attention
The transition from earlier protocol versions to v1.0 introduced changes to operation names, Agent Cards, and other protocol details.
The official TypeScript SDK provides an opt-in v0.3 compatibility layer, which can help during staged migration.
However, version compatibility should be tested explicitly rather than assumed.
Security does not come automatically
A2A supports authentication and authorization mechanisms, but the protocol does not automatically make remote agents trustworthy.
The application must decide which agents can communicate, what data can be shared, and which actions are permitted.
Reference: A2A v1.0 Specification · A2A Security Considerations
10. Security and Production Considerations
This is one of the most important parts of adopting A2A in an enterprise environment.
An agent may receive data from another agent, process documents, invoke tools, and return results that influence subsequent decisions.
We should not treat every remote agent as trusted simply because it implements the same protocol.
Some important considerations:
Authentication
A2A relies on established authentication mechanisms such as HTTP authentication, OAuth 2.0, OpenID Connect, API keys, and mutual TLS where appropriate.
The agent can advertise supported authentication schemes through its Agent Card.
Credentials are handled at the transport layer, not embedded directly into protocol messages.
Authorization
Authentication and authorization are different responsibilities.
Knowing who is calling an agent does not automatically mean that the caller should be allowed to invoke every skill.
Authorization should consider:
- Which application or user initiated the task?
- Which agent is allowed to communicate with the target agent?
- Which skills can be invoked?
- Which business operations are permitted?
- Which data can be accessed or shared?
For financial or other sensitive enterprise environments, authorization should be enforced at the skill and business-action levels.
Data isolation
Agents should receive only the information required to complete their tasks.
A research agent should not automatically receive the entire conversation history, database access, or credentials of another agent.
Task and context storage should also respect user and tenant boundaries.
Agent Card validation
Agent Cards describe service identity and capabilities.
For sensitive environments, clients should verify the origin of Agent Cards and validate their signatures when available.
A discovery result should not automatically be treated as a trusted execution target.
Observability and auditing
A multi-agent system needs visibility beyond individual model calls.
We should record:
- Caller and target agent identifiers
- Task and context identifiers
- Correlation and trace identifiers
- Task state transitions
- Request duration and failure reasons
- Artifact metadata and access
- Sensitive action approvals
OpenTelemetry and distributed tracing can help follow a request across multiple agents.
Push notification security
Push notification webhooks need their own security controls.
For example, the receiver should authenticate the notification source, validate the task identifier, and process duplicate notifications safely.
Webhook endpoints should also be protected against SSRF and unauthorized access.
These controls are application and infrastructure responsibilities that must be designed alongside the protocol integration.
Reference: A2A — Enterprise Implementation
11. What Can an AI Chapter Lead Contribute?
I think this is where A2A becomes especially interesting from an engineering leadership perspective.
The value is not simply adding another protocol to the technology stack. It is establishing a reusable architecture for teams that want to build specialized agents independently and allow them to collaborate.
There are several areas where we can contribute.
1. Establish a common agent communication standard
Different teams may build agents using different languages and frameworks.
Instead of forcing everyone to adopt the same framework, we can define a common communication contract based on A2A.
This allows teams to own their agent implementations while maintaining a consistent integration approach.
2. Build an internal agent registry
We can create a centralized catalog of available agents and their capabilities.
For example:
| Agent | Responsibility | Available skills |
|---|---|---|
| Research Agent | Information gathering | Research, source analysis |
| Database Agent | Data analysis | SQL analysis, reporting |
| Code Review Agent | Code quality | Review, recommendations |
| Security Agent | Security analysis | Vulnerability assessment |
| Documentation Agent | Documentation | Search, summarization |
Each agent can publish an Agent Card, while the internal registry handles discovery, ownership, versioning, authorization, and availability.
This would help teams reuse existing capabilities instead of implementing the same functionality repeatedly.
3. Define a reusable orchestration architecture
A2A should be combined with a clear orchestration strategy.
We can establish a reference architecture that separates:
- Agent discovery
- Task delegation
- Agent communication
- Execution state management
- Tool and data access
- Result aggregation
- Human approval
The coordinator should remain responsible for task planning and result aggregation. Specialized agents should focus on their assigned responsibilities.
4. Establish security and governance standards
A2A introduces communication between independently managed systems.
We should define clear standards for agent authentication, authorization, data sharing, tenant isolation, and audit logging.
This is especially important when agents interact with internal systems or execute business operations.
5. Run a practical multi-agent pilot
Rather than introducing A2A across every team immediately, we can start with a well-defined workflow.
A good example would be an automated merge request review process.
Example workflow:
1. A developer submits a merge request.
2. The Coordinator Agent receives the review request.
3. The Security Agent analyzes potential vulnerabilities.
4. The Testing Agent evaluates test coverage and identifies missing cases.
5. The Architecture Agent reviews design and implementation decisions.
6. Each specialist returns findings through A2A.
7. The Coordinator combines the results into a structured review report.
8. A developer reviews the recommendations before any changes are merged.
Each agent can use its own MCP servers to access GitLab, documentation, test tools, or other required services.
This allows us to evaluate the actual value of agent specialization and interoperability.
6. Measure the results
A successful pilot should be evaluated using real measurements.
Some useful metrics:
- Task completion rate
- Agent-to-agent communication latency
- Overall workflow duration
- Failed or timed-out tasks
- Duplicate execution rate
- Token and infrastructure costs
- Human intervention rate
- Quality of the final aggregated result
The objective is to determine whether distributing work across specialized agents produces better results than a single-agent implementation.
7. Create reusable learning resources
A practical reference implementation, architecture documentation, and internal workshops can help developers understand when A2A is useful.
Teams should learn not only how to implement the protocol, but also when not to use it.
Not every internal function needs to become a remote agent. For simple operations within the same runtime, a regular function or framework-native tool call may be more appropriate.
12. When Should We Use A2A?
A2A is most useful when communication between independent agents is an actual architectural requirement.
| Scenario | Recommendation |
|---|---|
| Agents built with different frameworks need to collaborate | A2A |
| Different teams expose independently maintained agents | A2A |
| Remote, long-running agent tasks need tracking | A2A |
| Agents need to exchange structured outputs and files | A2A |
| An agent needs access to APIs and databases | MCP |
| A single agent needs internal tool execution | Native tools or MCP |
| Multiple agents operate within one tightly coupled workflow | Framework-native orchestration may be sufficient |
The important thing is to avoid introducing network boundaries and protocol complexity when a simpler implementation would work.
A2A should solve an interoperability requirement, not become an additional abstraction by default.
Conclusion
A2A addresses an important challenge in the evolution of agentic AI systems: enabling independently developed agents to communicate and collaborate.
As AI applications become more specialized, a single agent will not necessarily be responsible for every task. Different agents may own different capabilities, use different frameworks, and operate in different environments.
A2A provides a standardized way for these systems to discover each other, delegate tasks, exchange information, and track execution.
Combined with MCP, it creates a useful architectural separation: MCP connects agents to tools and external context, while A2A connects agents to other agents.
For an AI Chapter Lead, the opportunity is to establish common integration standards, build an internal agent ecosystem, define security boundaries, and help teams develop reusable capabilities.
My recommendation: Start with one practical multi-agent workflow, implement it using the official A2A SDK, and evaluate the operational benefits against a conventional single-agent or framework-native orchestration approach.
The real question is not how many agents we can connect. It is whether connecting specialized agents makes the system more reliable, maintainable, and effective.
---
References and Further Reading
1. A2A Protocol — Official Website — Overview, architecture, ecosystem, and documentation.
2. A2A Protocol — Specification v1.0 — Protocol operations, data models, task lifecycle, transports, and security.
3. A2A — Agent Discovery — Agent Cards, discovery mechanisms, and capability descriptions.
4. A2A — Life of a Task — Task lifecycle and interaction patterns.
5. A2A — Streaming and Asynchronous Operations — Streaming, task updates, and push notifications.
6. A2A — Enterprise Implementation — Authentication, authorization, observability, and enterprise security.
7. A2A — Official JavaScript SDK — TypeScript client/server implementation, transport adapters, and runnable examples.
8. A2A — Official GitHub Organization — SDKs, samples, and project development.
9. AI SDK — A2A Community Provider — Integrating A2A agents with Vercel AI SDK.
10. A2A AI Provider — Community provider implementation and current maturity status.
11. Model Context Protocol — Complementary agent-to-tool integration protocol.
1 reaction · 1 comment
One extra field turned an A2A error into a TypeError. Fixed in a2a-sdk 1.3.0
---
title: One extra field turned an A2A error into a TypeError. Fixed in a2a-sdk 1.3.0
published: true
description: I fed the A2A Python client seven bad agent replies. A valid error code came back as a TypeError. The fix shipped in a2a-sdk 1.3.0.
tags: python, ai, agents, opensource
cover_image: raw.githubusercontent.com
---
<!-- Images are pushed to Sonica-B/Sonica-B under blog/, so the URLs below resolve. The interactive version of figure 1 is visibility/anatomy/a2a-sorter/a2a-sorter.html. -->
On October 27, 2026, SAP announced general availability of inbound Agent2Agent (A2A) interoperability, so third-party agents can now invoke SAP Agents through its Agent Gateway. Every launch like this means more A2A clients calling agents their authors do not control. Two days before SAP's announcement, I had opened a pull request for that exact situation in the A2A Python SDK. An agent could send a valid A2A error, and the calling code would receive a TypeError. My fix shipped the same day as SAP's news, in a2a-sdk 1.3.0.
A2A is an open protocol that lets AI agents discover each other and exchange messages and tasks over JSON-RPC, REST or gRPC. It joined the Agentic AI Foundation in August 2026, and a2a-sdk is its official Python SDK.
The short version
When an agent sent back an error, the SDK handed the reply to a third-party library before looking at it itself. That library is strict about which fields an error may carry, so one extra field, the kind the A2A spec says to ignore, made it throw a raw TypeError. The SDK's own translation of error codes into named exceptions never got a turn. Version 1.3.0 reads the reply itself first, at one place, and the library only builds requests now.
The concept, animated
!Animated figure. The reply's path through JsonRpcTransport as labelled blocks: agent reply, the json-rpc package's JSONRPC20Response(**body), the SDK's _create_jsonrpc_error(), your code with its except TaskNotFoundError branch, and three bins for TaskNotFoundError, A2AClientError and result. A token labelled {"code": -32001, "reason": "x"} drops into a raw TypeError tray at the json-rpc block. In 1.3.0 that block lifts out, _check_jsonrpc_response() takes its place, and the token reaches the TaskNotFoundError bin; a body that is a list reaches A2AClientError.
Figure 1. The blocks are the real call chain in client/transports/jsonrpc.py, in order. The token is the reply body. The three states: an error object with one extra field before 1.3.0, the same reply in 1.3.0, and a body that is not an object in 1.3.0.
A client talks to agents it does not control, so odd replies are normal. The SDK's promise about them is clear: a failure reaches your code either as A2AClientError or as a typed A2A error such as TaskNotFoundError. The A2A spec (section 5.7) adds that implementations should ignore fields they do not recognise.
Now follow the token in the first state. It carries the error code -32001, which means TaskNotFoundError, plus one extra field, reason, that the spec says to ignore. The first block it reaches is JSONRPC20Response(**body) from the json-rpc package. It validates the envelope before the SDK sees anything, and it builds the error with a constructor that accepts only code, message and data. Any other member is, to that library, a programming mistake. So the token drops into the tray labelled raw TypeError: JSONRPCError.__init__() got an unexpected keyword argument 'reason'. The SDK's _create_jsonrpc_error() block never receives it, and your except TaskNotFoundError: branch never runs.
The library was correct by its own rules. It simply ran first, so its rules won and the SDK's contract lost.
How I found seven of these
I was exploring the client with one question: what reaches my code when an agent answers wrong? No issue existed, so I played the misbehaving agent myself, wrote seven bad response bodies by hand, and fed them to the client through a mock transport. All seven came back as raw Python exceptions.
| What the agent sent | Before | After |
|---|---|---|
| a list, or a string (say, from a proxy) | TypeError | A2AClientError |
| neither result nor error | ValueError | A2AClientError |
| "error": "boom" | TypeError | A2AClientError |
| error object with an extra member | TypeError | TaskNotFoundError |
| error object without message | ValueError | TaskNotFoundError |
| SSE data: that is not JSON | JSONDecodeError | A2AClientError |
| event: error with an extra member | TypeError | TaskNotFoundError |
Following one reply through the code explained the whole table. Nine request methods handed the body straight to the json-rpc validator, and streaming used the same validator on each event. The SDK's own mapping from error codes to exceptions even had a fallback for a missing message, but it could never execute, because the library failed first. Streaming had a second twist: the JSON parse for each event ran in a place where its exception escaped the handler that was supposed to catch it, so a broken data: line leaked a JSONDecodeError.
The fix, in one idea
!Still from the third state: the json-rpc block is lifted out and faded, the SDK's _check_jsonrpc_response() block stands in its place, and a token labelled "a list, not an object" sits in the A2AClientError bin.
Figure 2. In 1.3.0 the json-rpc validator is out of the chain. The SDK reads the envelope itself at one choke point, so an error object becomes the typed A2A error whatever extra fields it carries, and a body that is not even an object becomes A2AClientError.
Check the wire envelope yourself, once, and hand libraries only what they can handle. Every reply already passes through one sending function or the streaming loop, so I put one check there. It asks three plain questions in order: is the body an object; if it has an error, is that an object, and if so, map it to the A2A exception by its code; otherwise, is there a result. Anything that fails those questions becomes A2AClientError with a message that says what was wrong. The streaming loop turns a JSONDecodeError into A2AClientError the same way. A falsy error next to a result still counts as success, as before. The client no longer builds JSONRPC20Response objects at all; requests still use JSONRPC20Request.
Wrapping each of the nine calls in its own try/except would have turned TaskNotFoundError into a generic error and copied the same code nine times. One choke point keeps the typed exceptions.
The tests live in a new class, TestMalformedJsonRpcResponses, with 11 cases. Ten fail on main with the exceptions in the table and pass with the change. The eleventh guards that delete_task_push_notification_config still raises on an error response. The existing tests pass unchanged. I ran everything on Windows 11 with Python 3.10.19: lint.sh passed, the transport tests gave 51 passed, and the wider run gave 2213 passed at 93% total coverage. Three failures and four setup errors fail the same way on main, and two integration suites do not run on Windows, so I left those to Linux CI. All 17 checks passed there, and the coverage bot showed client/transports/jsonrpc.py going from 92.66% to 95.51%.
What changed for people using a2a-sdk
If your client calls agents you do not control, version 1.3.0 behaves differently in three ways:
- An agent that adds its own field to an error object, or leaves out message, now raises the mapped A2A error, so your except TaskNotFoundError: branch runs.
- A malformed body, such as a list, a string, a missing result or a non-object error, raises A2AClientError, so one except clause covers all of them.
- A stream with a broken data: line raises A2AClientError where it used to leak a JSONDecodeError.
One behaviour change is worth knowing. A body with "error": null and no result used to parse as an empty object and now raises A2AClientError. That body breaks JSON-RPC 2.0 anyway. Upgrading is pip install -U a2a-sdk.
The v0.3 compatibility transport still uses the same validator-first pattern on main. I kept this PR to the 1.0 client and offered a fix(compat) follow-up in the PR body.
A thank-you to Iwaniukooo11 and the A2A maintainers
I opened PR #1375 on October 25 with no linked issue, just the table and the tests. On October 26, Mateusz Iwaniuk (@Iwaniukooo11) brought the branch up to date with main, approved it and merged it. Thank you. Getting a first PR to a repo reviewed and merged the day after it opens is a generous welcome.
Thanks also to the A2A maintainers for keeping the door open to outside contributors. Of the 40 merges before mine, 29 came from non-members, with a median of 3.4 days from open to merge. Release 1.3.0 carries fixes from at least three outside contributors, mine among them.
What I took away
Where a third-party validator sits in your call chain decides which exceptions your callers see. The stack trace ended inside json-rpc, but the decision that mattered was where the SDK called it.
When you own an error contract, check the wire envelope yourself at one choke point and hand libraries only what they can handle. As more vendors open their agents to outside callers, the exception a client can catch by name is part of the interface, and that boundary is the kind of problem I like to work on.
I'm Shreya Boyane, an ML engineer who builds agent systems, and I'm open to ML and AI engineering roles. You can find my work on GitHub, more about me at shreyaboyane.dev, and I'm happy to connect on LinkedIn.
<!-- optional disclosure she may keep: An AI coding agent helped me probe the package and draft this fix; I reviewed and verified the change before submitting. -->
@AgenticAIFdn If you want to try it, start with the first lesson in the cookbook, examples/01-exec-demo: a2aproject/a2a-cli
77 views
MCP vs A2A vs AG-UI: The Protocol Stack, Fact-Checked
> Originally published on DevToolHub.
MCP vs A2A vs AG-UI is the comparison chart making the rounds right now, and the framing is right — these are three different, complementary layers, not competing standards. But a couple of its headline numbers are either stale or don't hold up against the protocols' own sources, and it skips the actual news: MCP's July 2026 spec update is a real breaking-change event, not a one-word "stateless" bullet point. Here's what each protocol actually does, fact-checked against their own docs and GitHub repos, plus what the chart got right and what's worth correcting. For the agent concepts underneath all three, see our AI agents explained guide.
The Three Protocols, What Each One Actually Connects
The "vs" in every comparison title is misleading — you don't pick one. Each protocol solves a different connection problem, and a real agentic system typically uses all three at once:
- MCP (Model Context Protocol) — built by Anthropic — connects an agent to tools and data. Anthropic's own docs describe it as a standardized way to connect AI applications to external systems, "like a USB-C port for AI applications." If your agent needs to read a file, query a database, or call an API, this is the layer — see our MCP registry breakdown for how big that ecosystem already is.
- A2A (Agent2Agent Protocol) — built by Google, now governed by the Linux Foundation — connects one agent to another. It's how two independent agents, possibly built by different teams on different frameworks, discover each other and hand off work.
- AG-UI (Agent-User Interaction Protocol) — built by CopilotKit and its open-source community, not Google (a detail the viral chart gets right, worth double-checking since AG-UI's name invites confusion with Google's A2A) — connects an agent to the human watching it work. It's an event-based protocol for streaming an agent's state and output into a real UI, not a chat log.
None of these three compete for the same job. A production agent that reads your CRM (MCP), delegates a sub-task to a specialist agent (A2A), and shows a human the live progress bar while it works (AG-UI) is using all three simultaneously.
MCP's July 2026 Spec Update Was Bigger Than "Stateless"
The chart's MCP row says "Stateless spec, July 2026" and leaves it there. That's the right headline, but it's doing a lot of work in one line — per MCP's own 2026-07-28 changelog, this is the largest rewrite since launch: protocol-level sessions and the Mcp-Session-Id header are gone, the initialize handshake is gone, and Roots, Sampling, and Logging are now formally deprecated with a twelve-month removal window. We already covered the full breaking-change list and a real server-migration checklist in MCP Server Authentication: What the 2026-07-28 Spec Broke — if you're migrating an MCP server, that's the piece to work from.
What's actually new to check, and what the chart doesn't carry at all: per Anthropic's own announcement, MCP is at 400M monthly SDK downloads as of that July 28 update — a real, sourced number that's meaningfully below the chart's "close to 500M" claim, and the real reason for the stateless move is architectural: "MCP moves from a bidirectional stateful protocol to a request/response model. Servers can now deploy on serverless and edge infrastructure" — something the old session-pinned design couldn't do cleanly.
A2A's Real Status — v1.0, Signed Agent Cards, and a Number Worth Correcting
The chart says A2A hit "v1.0 with signed Agent Cards, March 2026." The version and date both check out — A2A's GitHub releases show v1.0.0 published March 12, 2026, and the live spec's section 8.4 ("Agent Card Signing") does define canonicalization, signature format, and signature verification requirements so agents can cryptographically sign their Agent Cards for authenticity. Worth knowing if you're building against it today: v1.0 isn't the current patch anymore — v1.0.1 shipped May 28, 2026 with HTTP binding and TaskStatus fixes.
Governance checks out too: Google originally built A2A and donated it to the Linux Foundation, and as of August 20, 2026 it formally joined the Agentic AI Foundation (AAIF) — the same body that already hosts MCP — putting both protocols under one neutral governance structure instead of two separate vendor-led ones. A2A's Technical Steering Committee includes representatives from AWS, Cisco, Google, IBM Research, Microsoft, Salesforce, SAP, and ServiceNow.
The one number worth double-checking: the chart lists A2A at "150+ organizations behind it." I couldn't find a primary source attributing that specific figure to A2A alone. What I could verify, directly from the Linux Foundation's own press release, is AAIF's total membership — the body that now governs both A2A and MCP — which states it "grows to 247 member organizations with the addition of 57 new members over the past three months." That's a materially different, larger number than "150+," just measuring the whole foundation rather than one protocol's backers specifically. If the chart's figure is from an earlier AAIF member count, it's stale now.
AG-UI's Real Adoption — 16.7k Stars and a Fourth Integration the Chart Skips
AG-UI's chart row says "15K+ GitHub stars." Checking the ag-ui-protocol/ag-ui repo directly, it's actually at 16.7k stars — the chart's number is accurate but already a bit behind. More interesting: the chart lists three 1st-party framework integrations (Google ADK, Microsoft Agent Framework, AWS AgentCore). The repo documents a fourth — AWS Strands Agents is also officially supported, alongside AWS Bedrock AgentCore, not instead of it. If you're picking an agent framework specifically because it has AG-UI support baked in, Strands is a real option the chart doesn't mention.
Where ACP Went
The chart's footer note — "Where's ACP? IBM merged it into A2A in August 2025" — checks out exactly. IBM Research's Agent Communication Protocol, built to power its BeeAI platform, overlapped heavily with A2A (both covered agent-to-agent discovery and messaging). The original ACP repository itself confirms it: archived August 27, 2025, with a banner reading "ACP is now part of A2A under the Linux Foundation!" and a link to the migration guide. If you see ACP referenced in older material, it's a dead end now — the real destination is A2A.
Do You Need All Three? A Practical Decision Guide
- Your agent needs to read or write something external — a CRM, your docs, a GitHub repo, a database: that's MCP. One server, any MCP-compatible client can use it — our best MCP servers by category guide is a starting point.
- Your agent needs to hand off a task to a different, independently-built agent — possibly on a different framework, possibly a different vendor: that's A2A. The Agent Card is how the other side learns what your agent can do before the handoff.
- A person needs to watch your agent work and approve or steer its steps in real time, not just read a final answer: that's AG-UI. It's the layer between the agent's internal state and whatever UI framework renders it.
- You're building a single-tool integration with no multi-agent handoff and no live UI — you may genuinely only need MCP. Don't add A2A or AG-UI to a system that doesn't have another agent or a human-in-the-loop UI to connect to.
Frequently Asked Questions
Q: Are MCP, A2A, and AG-UI competing standards?
A: No. They connect different things — MCP connects an agent to tools and data, A2A connects one agent to another, AG-UI connects an agent to the person watching it. A single system can use all three at once.
Q: What actually changed when MCP went stateless in July 2026?
A: Sessions, the initialize handshake, and three features (Roots, Sampling, Logging) all changed at once — see our full MCP server authentication breaking-change checklist for the complete list. The number worth knowing that isn't in that piece: Anthropic's own blog puts MCP at 400M monthly SDK downloads as of this update.
Q: Who governs MCP and A2A now?
A: Both are hosted projects under the Agentic AI Foundation (AAIF), part of the Linux Foundation. A2A joined AAIF on August 20, 2026, after Google donated it; MCP was already a founding AAIF project. AAIF's own press release puts total membership at 247 organizations.
Q: Does AG-UI only work with Google, Microsoft, and AWS AgentCore?
A: Those three are 1st-party supported, but so is AWS Strands Agents — a fourth framework with official AG-UI support that's easy to miss if you're only going by popular comparison charts.
Q: What happened to ACP (IBM's Agent Communication Protocol)?
A: It merged into A2A. The original repository was archived August 27, 2025, with a notice directing developers to A2A's migration guide.
Quick Summary:
- MCP, A2A, and AG-UI are complementary layers (tools, agent-to-agent, agent-to-human), not competing alternatives
- MCP's July 28, 2026 spec update (sessions, the initialize handshake, Roots/Sampling/Logging all changed) is covered in full in our MCP server authentication piece
- Per Anthropic's own blog, MCP is at 400M monthly SDK downloads as of that update — not "close to 500M"
- A2A hit v1.0.0 in March 2026 (now on patch v1.0.1) with real signed Agent Cards; AAIF's own total membership is 247 organizations, a different number than "150+"
- AG-UI is at 16.7k GitHub stars with four 1st-party framework integrations (Google ADK, Microsoft Agent Framework, AWS Bedrock AgentCore, and AWS Strands Agents)
- ACP (IBM) merged into A2A in August 2025 and is a dead end if you see it referenced
The real answer to MCP vs A2A vs AG-UI is: pick protocols by the connection you actually need, not by which chart you saw first — and if you're touching an MCP integration built before July 2026, check it against the real breaking-change list above before assuming "stateless" just means a minor version bump.
A2A Protocol Authentication Security: Agent Cards and Signed Requests
Originally published on robatdasorvi.com
The lockout check fired and returned True. The caller skipped one action, and the loop kept running. For three and a half days after X suspended my account, the bot carried on as if nothing had happened. The check was right; the caller treated a suspension like one failed action. I keep thinking about that gap, because A2A protocol authentication security runs into the same problem: a component returns a verdict, and whatever calls it decides whether anyone listens. A2A (Agent2Agent) is the open protocol for agents built by different teams to discover and call each other. In it, that gap sits between verifying who a remote agent is and deciding whether to act on what it sends.
Is an A2A Agent Card something I can trust?
Not by default. An Agent Card is a JSON document a remote agent publishes to describe itself and its endpoints. Reading one tells you what the agent claims. It doesn't tell you who stands behind the claim.
The card is served at the well-known path /.well-known/agent-card.json on the agent's own domain. Signed cards arrived in v0.3.0, whose release notes list "Add signatures to the AgentCard." Per the A2A specification, signatures are JWS over the card after it is canonicalized with the JSON Canonicalization Scheme (RFC 8785), with the signatures field itself excluded. Signing is optional for the publisher, and a client SHOULD verify at least one signature before trusting a card.
Why not sign the card and move on?
Because a valid signature answers one narrow question: did the holder of this key sign these exact bytes? It doesn't tell you whether that key belongs to an organization you chose to deal with, whether the key is still trusted, or whether the agent behind it does what the card says.
The weak point is rarely the cryptography. A signed document is only as trustworthy as the list of keys you decided to accept, and that list rarely gets a security review. A signature tells you the card wasn't altered after signing. It says nothing about whether the signer should have been allowed to publish it in the first place.
The field isn't ready for this. A 2026 study by Tam Nguyen, Moses Ndebugre and Dheeraj Arremsetty scored 16 AI security frameworks against 193 threat items and found that "No reviewed framework achieves majority coverage of any single category" (arXiv:2603.09002). That paper doesn't discuss Agent Cards, so I'm citing it only for the coverage gap. The earlier Habler et al. threat model names Agent Card spoofing directly: a forged card hosted on a malicious domain enables task hijacking and impersonation (arXiv:2504.16902).
How do you verify an A2A Agent Card signature?
Fetch the card over TLS from the domain you expect, canonicalize it with RFC 8785 after removing the signatures field, and verify each JWS against a key from a trust store you control. Reject the card if any check fails. A passing check shows the key is trusted, not that the agent behaves.
1. Fetch the card over TLS from the expected domain. Use the well-known path /.well-known/agent-card.json on that domain, and don't follow redirects to hosts you did not expect.
2. Remove signatures and canonicalize. Apply RFC 8785 (JSON Canonicalization Scheme) to the remaining content. The spec excludes signatures from signed content to avoid circular dependencies, so verify exactly that byte sequence.
3. Verify each JWS against a key from a trust store you control. Never take the key only from a URL embedded in the card. The spec requires alg, typ and kid in the protected header, but an attacker can name their own key, so the header proves nothing about who holds it.
4. Reject an unknown kid, an expired key or a missing signature, and fail closed. A card without a valid signature is unverified. Logging a warning and continuing is not a passing result.
5. Cache with a TTL, and re-verify when the card changes. A cached "verified" flag must not outlive the key that produced it.
6. Record every rejection, and alert when the rejection count stays at zero for a long time. A verifier that never rejects may not be wired in, or may be checking against rules that no longer match reality. My bot's lockout check failed the second way: during the second suspension it matched none of its known selectors, so it never fired.
What do static API keys and allowlists cost in A2A?
A hardcoded allowlist of agent URLs plus one long-lived API key per partner fails silently: stale entries keep routing calls, and a leaked key works until someone notices. Its costs never show up on a dashboard, which is why people keep choosing it.
Look at the credential habits. Astrix Security's report, published October 15, 2025, examined about 5,200 open-source MCP server implementations (5,205 GitHub repositories). It found that 53% rely on static secrets, mostly API keys and personal access tokens. Only 8.5% use OAuth, and 79% of API keys are passed through plain environment variables (Astrix Security, 2025). Those are MCP servers, not A2A agents, but the habit carries over. A static secret is cheap on day one and expensive on the day it leaks.
The hidden cost I know firsthand is quieter than a leak. For about six weeks, four hardcoded target accounts that no longer existed soaked up 88% of my bot's daily comment attempts. Every run logged success. Nothing raised an error. A hardcoded list is cheap to write and expensive to be wrong about, because it fails silently. A trusted-agent allowlist fails the same way. The partner moves its domain, the entry stays, and every call to the old address either fails softly or, in the bad version, reaches whoever now controls that address.
The single-integration model has the same problem at a much larger scale. Google Threat Intelligence Group reported that beginning as early as August 8, 2025, through at least August 18, 2025, an actor used OAuth tokens tied to the Salesloft Drift integration to export data from numerous corporate Salesforce instances (GTIG, August 2025). GTIG described the victims as numerous Salesforce instances. One integration, trusted once, reached numerous tenants. Nothing was forged. The tokens were real, and so was the integration. The convenient trust was the vulnerability.
Has anyone exploited agent-to-agent trust in production?
I have found no public report of an A2A-protocol trust exploit in the wild. The closest documented case is ServiceNow's Now Assist, which uses ServiceNow's own agent-discovery mechanism, not A2A, and its lessons still carry over. AppOmni found CVE-2025-12420, rated CVSS 9.3, in Now Assist AI Agents and the Virtual Agent API. ServiceNow patched it and said it was unaware of the issue being exploited in the wild against customer instances (NVD). AppOmni's BodySnatcher write-up says an unauthenticated attacker could impersonate any ServiceNow user with only their email address, because the providers shipped with the same static client secret across all ServiceNow instances and required only the email address to link an external entity to a ServiceNow account. The attacker could then run AI agents as that user (AppOmni, BodySnatcher).
AppOmni's second finding is not a CVE but default behavior, and it matters more. In AppOmni's November 19, 2025 write-up, agents deployed to the same virtual agent were "automatically grouped into the same team by default." Published agents were "marked as being discoverable by default." And agents ran with "the privilege of the user who started the interaction," not the user who planted the malicious content (AppOmni). Nobody had to break a signature. The trust came from shipped defaults.
Neither finding proves a wild campaign. But when the dangerous configuration is the default, "rare misconfiguration" stops working as a defense.
What does a request-time check require?
Every request needs a credential the server can verify, sent over TLS, with a scope tied to that specific call. The A2A security scheme types include API key, HTTP auth, OAuth 2.0, OpenID Connect and mutual TLS, so a card can declare what a caller must present (A2A specification). Declaring a requirement is not the same as enforcing it. The A2A spec signs the Agent Card. Authenticating each request is left to whichever security scheme the card declares, so a signed card does not mean signed requests. If you need integrity on the message body itself, HTTP Message Signatures (RFC 9421, rfc-editor.org) is one option, but it sits outside A2A and both sides must agree to use it.
Mutual TLS looks like the grown-up default. For agent calls across organizations, though, it just moves the stale-entry problem into certificate issuance and rotation for every partner. You get the same allowlist rot, with more paperwork and a better audit log. Per-call authorization matters most, where the server checks the caller and the action together. I wrote about that in Policy as Code for AI Agent Tool Calls: Authorizing Actions with OPA or Cedar. The ServiceNow defaults are also why the scope of a delegated identity deserves the care described in Least Privilege for AI Agents: Scoping Tools, Keys and Filesystems.
What's still unresolved?
I don't know who should own the trust list for a multi-party A2A network. An operator-maintained list can rot the way my bot's hardcoded target list did. A federated model, where you trust an issuer rather than a URL, moves the failure into the issuer's key management, and I have no data on how often issuers rotate keys badly. Feature flags can disable a remote agent without a redeploy, as I described in Feature Flags for AI Agent Tools: Disabling One Capability in Production Without a Redeploy. But a kill switch only helps if something notices the agent has gone bad.
Nothing here is measured against a live A2A deployment. I haven't verified a signature check against a real remote card. The question I'm left with is the one the lockout check taught me: when the next quiet failure happens, will the code around it be listening?
---
Note: I could not finish verifying change 15. The A2A spec page was truncated before the signing and authentication sections, so the sentence "Authenticating each request is left to whichever security scheme the card declares" is not confirmed against the spec text. The BodySnatcher wording in change 9 is confirmed against the AppOmni post, which uses "exact same secret across all ServiceNow instances" rather than "hardcoded platform-wide secret," so I used the post's own phrasing. Change 8 used option (b), so no 700 figure is cited.
@stevekrouse haven't used it but this is a thing a2aproject/a2a
29 views
Controlling Access to A2A Agents with agentgateway
At Qodo, we've publicly described how PR-Agent became an A2A agent for MOSAICO, a research project coordinating software-engineering agents. Other agents can delegate code reviews and receive results. When you expose an agent this way, how do you control who can call it and verify that rejected requests never reach it?
This walkthrough puts a sample agent behind agentgateway, checks caller credentials, and records which requests reach the backend!
What does A2A make possible for a code-review agent?
Agent2Agent, or A2A, defines how independently deployed agents exchange messages, tasks, and results. An agent card advertises an agent's capabilities and connection details. A client can discover a reviewer, send work, and receive its response without sharing the reviewer's implementation.
Qodo's public PR-Agent integration illustrates that boundary. MOSAICO can delegate a review to PR-Agent and receive review output. That establishes a useful interaction, but operating an endpoint also requires decisions about callers, credentials, and the resources each request may access.
For the access tests, a deterministic Hello World agent is enough. It returns a greeting instead of reviewing code. That keeps model behavior out of the experiment and makes backend execution easy to observe.
Where does agentgateway sit in the request path?
Agentgateway's A2A example places a proxy between the client and the agent. The tested setup uses agentgateway 1.6.0, Python 3.13.4, A2A Python SDK 1.1.0, and the A2A 1.0 JSON-RPC binding. Its executor comes from this pinned upstream sample.
Requests go to the gateway on port 18760; the sample backend listens on 127.0.0.1:18761. Request logging and an executor-entry record let the test compare what the gateway returned with what the agent actually received and ran.
!Client requests pass through JWT validation and a reviewer-role check in agentgateway. Invalid credentials return 401; rejected roles return 403; accepted requests reach the sample agent.
The tested request path. The backend remains reachable directly from the local machine; the diagram doesn't imply network isolation.
Fetching the agent card through the gateway returned a supportedInterfaces URL pointing back to port 18760. That helps clients continue through the proxy. It doesn't prevent someone who knows a reachable backend address from calling it directly.
How do you define what can reach the agent?
The route combines JWT authentication with an authorization rule. A JSON Web Token carries signed claims about its issuer, intended audience, and caller. The gateway checks the signature against a JSON Web Key Set, or JWKS, containing the issuer's public key.
Here's the route portion of the tested configuration:
binds:
- port: 18760
listeners:
- routes:
- policies:
a2a: {}
jwtAuth:
mode: strict
issuer: https://issuer.example.test
audiences: [a2a-demo]
jwks:
file: ./jwks.json
authorization:
rules:
- require: 'jwt.role == "reviewer"'
backends:
- host: 127.0.0.1:18761
The issuer here is a synthetic test identity, not a service to sign into. The runner creates an RSA key and short-lived tokens locally. In this release, the JWKS file path resolves from the process's working directory.
strict requires a valid token. The issuer and audience constrain which tokens qualify; a separate rule requires the signed role claim to equal reviewer. That role is this demo's convention, not an A2A-defined permission.
Keep those checks explicit. Agentgateway's default optional authentication mode permits requests without a token. A configuration that validates credentials when present can behave differently from one that requires credentials on every request.
How do you prove a policy rejects the right requests?
Each test sends a real SendMessage request with a unique message ID. The same identifier connects the request to the backend's records. Accepted cases must return the greeting without a JSON-RPC error; an HTTP 200 alone doesn't establish successful A2A execution.
These were the observed outcomes with the configuration above:
| Caller credentials | HTTP response | Backend requests / executor calls |
|---|---:|---:|
| Valid token, reviewer role | 200 | 1 / 1 |
| No token | 401 | 0 / 0 |
| Wrong audience | 401 | 0 / 0 |
| Wrong role | 403 | 0 / 0 |
| Missing role claim | 403 | 0 / 0 |
| Invalid signature or expired token, tested separately | 401 each | 0 / 0 each |
A valid request after the rejection cases also completed. Removing only the authorization rule let a valid token with the wrong role reach the executor. That control checks whether the role rule actually explains the rejection.
One configuration change exposed a useful failure case. Replacing the requirement with this rule changed the result for a valid token missing role:
authorization:
rules:
- deny: 'jwt.role != "reviewer"'
That request returned 200 and executed once. With require, it returned 403 and never reached the backend.
The documented evaluation rules explain why: accessing a missing claim makes the expression fail. A failed requirement rejects the request. A failed deny expression doesn't match; with this single deny rule and no allow rules, the request proceeds.
JWT authentication stayed strict in both configurations. A missing token still returned 401. The finding concerns a missing application claim in an otherwise valid token, under these exact policies.
What permissions belong inside the agent?
The gateway established which tested requests could reach this endpoint. It didn't decide whether a caller may read a particular repository, retrieve a private diff, or write a change. A real reviewer needs those checks where it accesses the repository and invokes its tools.
The bypass control matters too: calling this local backend directly without a token returned 200 and executed once. A deployment needs to restrict direct backend access or enforce equivalent checks there. Rewriting the agent card isn't a network access control.
This reproduction used synthetic credentials, HTTP on a development machine, and a greeting executor. It didn't validate production TLS, identity-provider integration, streaming recovery, or repository permissions. The gateway's standalone listener binds a wildcard address; the backend and administrative endpoints were configured on loopback.
For your own agent, start with an allowed request and specific rejected requests. Check both the response and backend execution. Then remove or change the relevant policy in an isolated test to verify which check caused the rejection. That gives you evidence for the boundary you've configured—and a clear account of what still needs protection.
3 reactions
Week 42: New A2A partners and Community Integrations
Hi All, sharing a few A2A Updates.
This week we have made some progress in our A2A v1.1 protocol and also added a new list of partners.
Partners Added @ https://a2a-protocol.org/latest/partners/
- Agent Commons: A free, public home for independent AI agents offering directory profiles, restart-proof inboxes, and shared knowledge bases, accessible via an open JSON-RPC message/send endpoint.
- DEUSPROOF: A public, immutable notary service that acts as a certificate of birth for AI agents, providing ownership verification, Ed25519 signing, and cryptographic time-stamping.
- Neuronto: A registry and endpoint provider that serves a conformant A2A agent card and endpoint for agent discovery and communication.
Community Integrations Added @ https://a2a-protocol.org/latest/community/
- Quilzo: A community-integrated framework featuring built-in A2A 1.0 support for agent cards, governance extensions, and secure JSON-RPC task execution.
- CUGA Integration: Added CUGA (IBM Research's open-source agent harness) to A2A community integrations, allowing CUGA agents to be served over A2A and delegate tasks to remote agents.
1 point
The example has been replaced (PR #2319 approved), and the Python SDK will warn on the affected cards. Aligning the bytes waits for the next major release.
I did not argue it. I measured it.
A2A#2122
Not a claim about the work. The work.
Mentions
A2A (Agent2Agent Protocol): Building Interoperable Multi-Agent Systems Understanding how AI agents discover, communicate, delegate tasks, and collaborate across different frameworks and platforms. 1. What Is A2A, and What Problem Does It Solve? As AI applications become more capable, we are moving beyond simple interactions between a user and a single AI model. Today, an AI agent can search documentation, query databases, interact with APIs, analyze code, and execute multi-step workflows. However, as these capabilities grow, a new architectural question emerges: How can independently developed AI agents communicate and work together? Consider an application that needs to analyze a GitLab merge request. The task may involve several different responsibilities: - A security agent reviews potential vulnerabilities. - A testing agent analyzes test coverage and identifies missing cases. - An architecture agent evaluates design decisions and code structure. - A review agent combines the findings and prepares a final report. Each agent might use a different framework, model provider, toolset, or programming language. Without a standardized communication mechanism, integrating these agents usually requires custom APIs, shared data contracts, and application-specific orchestration logic. This is where Agent2Agent (A2A) Protocol becomes relevant. A2A is an open protocol designed to enable communication and collaboration between independent AI agent systems. It provides a common interaction model regardless of the framework, programming language, or vendor used to build an agent. The protocol was originally developed by Google and contributed to the Linux Foundation. It is designed around interoperability without requiring agents to expose their internal reasoning, memory, tools, or implementation details. The important distinction is that A2A does not define how an agent thinks or performs its internal tasks. It defines how one agent can communicate with another. In other words, an agent can be implemented using LangGraph, Google ADK, CrewAI, Semantic Kernel, or a completely custom solution and still communicate with another compatible agent through A2A. Reference: A2A Protocol — Official Documentation 2. Why Do We Need A2A? Most agent frameworks provide mechanisms for calling tools, delegating work, or coordinating multiple agents. However, these mechanisms are generally designed around the framework's own execution model. For example, an agent built with one framework may have its own task execution logic, state management, and internal tool definitions. Connecting it directly to an agent developed using another framework can require custom integration work. A2A addresses the interoperability problem. Instead of requiring agents to understand each other's internal implementations, they communicate through a standardized protocol. This gives us several important capabilities: - Discover agents and understand their capabilities. - Delegate tasks to remote agents. - Exchange messages and structured data. - Track the lifecycle of long-running tasks. - Receive progress updates through streaming. - Exchange files and generated artifacts. - Support authentication and authorization across agent boundaries. A2A is particularly useful when agents belong to different teams, applications, organizations, or technology stacks. The goal is not to turn every agent into the same implementation. The goal is to allow different implementations to collaborate. 3. A2A vs. MCP vs. ACP These three protocols are often discussed together, but they solve different architectural problems. | Technology | Main responsibility | |---|---| | MCP | Agent ↔ Tools and external context | | ACP | Coding Agent ↔ Client Application | | A2A | Agent ↔ Agent | Let's consider a practical example. Imagine a research agent that needs to investigate a topic, retrieve information from internal documentation, and delegate a part of its work to a specialized analysis agent. In this scenario: 1. MCP provides access to documentation, APIs, databases, and other tools. 2. A2A allows the research agent to communicate with the analysis agent. 3. ACP could be used separately when integrating a coding agent with an editor or another client application. A simplified architecture: User | v Main AI Application | v Coordinator Agent / \ / \ A2A A2A / \ v v Research Agent Analysis Agent | | MCP MCP | | Internal APIs Database / APIs | | v v Results Results \ / \ / Coordinator | v Final Response Each agent can use its own tools and internal architecture. A2A provides the communication layer between them. One important point: A2A is not a replacement for an agent framework or an internal sub-agent mechanism. It focuses on communication between independent agent applications. Internal orchestration can still be handled by the framework that runs the agent. References: A2A and MCP · MCP Documentation · ACP Documentation 4. How Does A2A Work? To understand A2A, we need to understand a few core concepts. 4.1. Agent Card: Discovering Other Agents One of the most important concepts in A2A is the Agent Card. An Agent Card is a JSON document that describes an agent and its capabilities. It acts as a machine-readable description of what an agent can do and how another agent can communicate with it. An Agent Card can contain: - Agent name and description - Service endpoint - Supported protocol versions and transports - Available capabilities - Supported input and output formats - Skills and example tasks - Authentication requirements By default, an A2A server exposes its Agent Card at: https://agent.example.com/.well-known/agent-card.json For example, a research agent might expose the following description: { "name": "Research Agent", "description": "An agent specialized in research and source analysis.", "version": "1.0.0", "supportedInterfaces": [ { "url": "https://research.example.com/a2a", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" } ], "capabilities": { "streaming": true, "pushNotifications": true, "extensions": [] }, "defaultInputModes": [ "text/plain" ], "defaultOutputModes": [ "text/plain", "application/json" ], "skills": [ { "id": "market-research", "name": "Market Research", "description": "Researches topics and produces structured findings.", "tags": [ "research", "analysis" ], "examples": [ "Research the latest developments in AI agents." ], "inputModes": [ "text/plain" ], "outputModes": [ "text/plain", "application/json" ] } ] } This is a simplified public Agent Card example. Authentication and additional security requirements can also be described through the protocol's security fields. A client can retrieve this document and understand whether the remote agent is suitable for a particular task. For example, a coordinator agent looking for an agent capable of analyzing financial documents can inspect registered Agent Cards and evaluate their declared skills. Agent discovery can happen through: - Well-known URLs - Private or public agent registries - Directly configured Agent Card URLs An Agent Card describes an agent's advertised capabilities; it does not prove that the agent can successfully perform every advertised task. Capability selection should be combined with authorization, availability checks, and runtime evaluation. Reference: A2A — Agent Discovery 4.2. Messages and Tasks A2A distinguishes between a message and a task. Message A message represents communication between a client and an agent. It can contain instructions, clarification requests, or additional information. Task A task represents a stateful unit of work with its own lifecycle. For example, when an agent receives a request to analyze a large document, it may need several minutes to complete the work. Instead of treating the interaction as a simple request-response operation, A2A allows the agent to create a task and report its progress. A task has a lifecycle that can include: | State | Meaning | |---|---| | Submitted | Task has been accepted | | Working | Agent is processing the task | | Input Required | Additional information is needed | | Auth Required | Additional authentication is required | | Completed | Task finished successfully | | Failed | Task execution failed | | Canceled | Task was canceled | | Rejected | Agent declined the task | Some states represent interruptions that can be resolved through further interaction, while others represent terminal states. This is important for long-running workflows because the client does not need to keep a single request open until every operation is complete. A2A also provides two useful identifiers: - taskId: Identifies an individual task. - contextId: Groups related messages and tasks within a broader interaction context. The task identifier is generated by the agent server. The context identifier helps maintain continuity across multiple related interactions. 4.3. Artifacts: Sharing Task Results A2A also introduces the concept of Artifacts. An artifact represents the output produced by an agent while completing a task. For example: - A research agent can produce a research report. - A coding agent can return a generated file. - An analysis agent can return structured JSON. - A document processing agent can return an extracted document. An artifact can contain multiple parts, including text, binary content, URLs, or structured data. This distinction is important: Messages are primarily used for communication and interaction, while artifacts represent the actual outputs of a task. It allows agents to exchange more than just text-based responses. 4.4. Streaming and Asynchronous Communication Not every agent task finishes immediately. A research process, document analysis, or code review can take a considerable amount of time. A2A supports multiple ways of receiving task updates: Streaming The client can subscribe to task updates and receive events while the agent is working. Depending on the protocol binding, streaming is supported through Server-Sent Events (SSE) or gRPC streaming. Polling The client can periodically request the current task state. Push Notifications For long-running tasks where maintaining a persistent connection is not practical, the agent can send updates to a client-provided webhook. These mechanisms allow applications to support different execution patterns without inventing custom communication protocols for each agent. A2A also defines operations for sending messages, retrieving tasks, canceling tasks, subscribing to updates, and managing push notification configurations. References: A2A Specification · Task Lifecycle · Streaming and Asynchronous Operations 5. How to Integrate A2A? A2A provides official SDKs for several programming languages, including: - Python - JavaScript / TypeScript - Java - Go - C# / .NET - Rust For TypeScript applications, the official package is @a2a-js/sdk. The current SDK implements the A2A v1.0 specification and supports client and server development. Step 1: Install the package pnpm add @a2a-js/sdk The official JavaScript SDK requires Node.js 20 or later. For this example, we will implement a client that communicates with an existing remote research agent. Step 2: Connect to an A2A agent The SDK provides ClientFactory to create a client. import { ClientFactory } from '@a2a-js/sdk/client'; const factory = new ClientFactory(); const client = await factory.createFromUrl( 'https://research-agent.example.com' ); The SDK retrieves the Agent Card and selects a compatible transport based on the agent's advertised interfaces and the client's supported transports. This means we do not need to hardcode every protocol detail into our application. Step 3: Send a message to the agent Once the connection is established, we can send a task request. import { Role, type Message, type SendMessageRequest, } from '@a2a-js/sdk'; const message: Message = { messageId: crypto.randomUUID(), role: Role.ROLE_USER, parts: [ { content: { $case: 'text', value: 'Research the latest developments in multi-agent systems.', }, mediaType: 'text/plain', filename: '', metadata: undefined, }, ], taskId: '', contextId: '', extensions: [], metadata: {}, referenceTaskIds: [], }; const request: SendMessageRequest = { tenant: '', message, configuration: undefined, metadata: {}, }; const result = await client.sendMessage(request); console.log(result); The example uses the SDK's generated TypeScript message types. Depending on the agent's behavior, the response can contain a message or a task. For production integrations, authentication and request timeouts should be configured as well. For example: const result = await client.sendMessage(request, { serviceParameters: { Authorization: `Bearer ${process.env.A2A_TOKEN}`, }, signal: AbortSignal.timeout(60_000), }); The client can now communicate with a remote agent without needing to know its internal framework or model implementation. Step 4: Stream task updates For long-running tasks, we can use sendMessageStream. const stream = client.sendMessageStream(request); for await (const event of stream) { // Handle task events: // - task // - statusUpdate // - artifactUpdate console.log(event); } The actual event union should be handled using the SDK's generated types. This allows us to display task progress, process generated artifacts, and update the application as the remote agent works. The SDK also supports task retrieval, cancellation, and push notification configuration. Step 5: Build your own A2A agent A2A is not limited to consuming existing agents. We can also expose our own agent through the protocol. The official JavaScript SDK separates server-side responsibilities into three main components: AgentExecutor Contains the agent's business logic. It receives the request context, executes the task, and publishes messages, status updates, and artifacts. DefaultRequestHandler Handles protocol-level responsibilities such as message routing, task storage, cancellation, and push notifications. Transport Adapters Expose the agent through supported communication protocols. The SDK provides adapters for JSON-RPC and HTTP+JSON/REST through Express, as well as gRPC for Node.js. The architecture looks like this: Incoming A2A Request | v Transport Adapter | v DefaultRequestHandler | +----> Task Store | +----> Execution Event Bus | v AgentExecutor | v Agent Logic | +----> LLM +----> MCP Tools +----> Internal APIs +----> Business Logic | v Messages / Status / Artifacts | v A2A Response The SDK also supports custom task stores and event buses, which allows us to integrate persistent databases, caches, or message brokers instead of relying exclusively on in-memory implementations. For production, this separation is particularly useful because protocol handling, task execution, and persistence can evolve independently. Reference: Official A2A JavaScript SDK 6. Integrating A2A with the Vercel AI SDK There is another interesting integration worth looking at. The Vercel AI SDK lists an A2A community provider called a2a-ai-provider. This package allows an A2A-compatible agent to be consumed through familiar AI SDK functions such as generateText and streamText. This is conceptually similar to the ACP provider we discussed previously. Instead of implementing every A2A interaction manually, we can use the community provider as an adapter between the A2A protocol and the AI SDK. Installation pnpm add ai a2a-ai-provider Provider configuration import { a2a } from 'a2a-ai-provider'; import { generateText } from 'ai'; const result = await generateText({ model: a2a( 'https://research-agent.example.com/.well-known/agent-card.json' ), prompt: 'Research the latest developments in agentic AI.', }); console.log(result.text); Streaming responses We can also use streamText: import { a2a } from 'a2a-ai-provider'; import { streamText } from 'ai'; const result = streamText({ model: a2a( 'https://research-agent.example.com/.well-known/agent-card.json' ), prompt: 'Analyze the current multi-agent ecosystem.', providerOptions: { a2a: { contextId: crypto.randomUUID(), }, }, }); await result.consumeStream(); console.log(await result.content); Here, contextId is used to associate related interactions with the same context. This integration makes it easier to use A2A agents in existing AI SDK applications. However, there is an important distinction: The official A2A SDK is the protocol implementation, whereas a2a-ai-provider is a community-maintained adapter. The community provider's documentation describes it as under active development, and its GitHub repository currently labels it alpha and advises against production workloads. For that reason, I would use it for experimentation and evaluate the official SDK directly for production-critical integrations until the provider's maturity and compatibility are established. References: AI SDK — A2A Community Provider · a2a-ai-provider GitHub 7. Supported Communication Protocols A2A separates its common data model and operations from the transport used to communicate. The current specification defines three standard protocol bindings: | Protocol | Description | |---|---| | JSON-RPC | JSON-based remote procedure calls | | HTTP+JSON / REST | Resource-oriented HTTP communication | | gRPC | RPC communication using Protocol Buffers and gRPC | An agent can support more than one binding. For example, a service could expose JSON-RPC and REST interfaces using the same underlying task execution logic. This separation is valuable because clients and servers can select a compatible transport without changing the fundamental meaning of tasks, messages, and artifacts. The official JavaScript SDK supports all three transports, with its gRPC integration requiring a Node.js environment and additional dependencies. A2A also defines protocol versioning. Clients and agents use the A2A-Version header to identify the version they are communicating with. The current v1.0 specification includes changes from the earlier v0.3 interface, so compatibility and migration should be considered when connecting existing implementations. Reference: A2A Protocol Specification 8. Advantages of A2A Interoperability across frameworks One of A2A's biggest advantages is the ability to connect agents built using different frameworks and programming languages. We do not have to standardize every team's internal agent implementation just to enable collaboration. A Python-based research agent can communicate with a TypeScript-based coordinator, while another agent may be implemented using Go or .NET. This reduces framework-level coupling. Independent agent implementations Agents do not need to expose their internal prompts, model configurations, memory, or tools. They only need to expose their supported capabilities and implement the agreed communication contract. This allows teams to evolve their internal implementations without forcing all consumers to change their integration code. Better support for long-running workflows A2A is designed around stateful tasks. Instead of implementing custom job tracking, status polling, event streaming, and result retrieval for every remote agent, we can use standardized operations. This is particularly useful for research, document processing, code analysis, and other workflows that may take several minutes or longer. More flexible output handling Agents can exchange text, files, structured data, and other supported content types through message parts and artifacts. This makes the protocol useful beyond conventional text-based chat applications. Centralized agent discovery and integration Agent Cards allow applications to discover what an agent offers and how to communicate with it. Combined with a curated internal agent registry, this can provide a common discovery layer for enterprise AI platforms. 9. Limitations and Trade-offs A2A solves a real interoperability problem, but it also introduces architectural complexity. It does not provide orchestration by itself A2A defines how agents communicate, not how an entire multi-agent system should be planned and executed. It does not decide which agent should receive a task, how many agents should participate, or how conflicting results should be resolved. Those responsibilities remain with an orchestrator or an agent framework. For example, LangGraph, Google ADK, or a custom coordinator can decide when to delegate a task through A2A. Distributed execution introduces more failure scenarios When agents communicate across network boundaries, new failure scenarios appear: - A remote agent may become unavailable. - A request may time out while the task continues running. - A task may fail after partial execution. - A client may disconnect before receiving the final artifact. - A downstream agent may return an incomplete or unexpected result. Therefore, retries, cancellation, idempotency, timeout policies, and persistent task storage need to be considered carefully. More communication can increase latency and cost Every agent handoff introduces additional work. A complex multi-agent workflow may involve several model calls, network requests, and tool executions. Using more agents does not automatically improve the result. We should introduce agent delegation only when the additional specialization or parallelism justifies the extra latency, cost, and operational complexity. Agent compatibility does not guarantee equivalent capabilities Two agents may both implement A2A but provide very different skills and execution behavior. A2A standardizes communication, not the quality or accuracy of an agent. Clients still need to evaluate supported capabilities, input/output formats, and the reliability of the remote service. Version compatibility requires attention The transition from earlier protocol versions to v1.0 introduced changes to operation names, Agent Cards, and other protocol details. The official TypeScript SDK provides an opt-in v0.3 compatibility layer, which can help during staged migration. However, version compatibility should be tested explicitly rather than assumed. Security does not come automatically A2A supports authentication and authorization mechanisms, but the protocol does not automatically make remote agents trustworthy. The application must decide which agents can communicate, what data can be shared, and which actions are permitted. Reference: A2A v1.0 Specification · A2A Security Considerations 10. Security and Production Considerations This is one of the most important parts of adopting A2A in an enterprise environment. An agent may receive data from another agent, process documents, invoke tools, and return results that influence subsequent decisions. We should not treat every remote agent as trusted simply because it implements the same protocol. Some important considerations: Authentication A2A relies on established authentication mechanisms such as HTTP authentication, OAuth 2.0, OpenID Connect, API keys, and mutual TLS where appropriate. The agent can advertise supported authentication schemes through its Agent Card. Credentials are handled at the transport layer, not embedded directly into protocol messages. Authorization Authentication and authorization are different responsibilities. Knowing who is calling an agent does not automatically mean that the caller should be allowed to invoke every skill. Authorization should consider: - Which application or user initiated the task? - Which agent is allowed to communicate with the target agent? - Which skills can be invoked? - Which business operations are permitted? - Which data can be accessed or shared? For financial or other sensitive enterprise environments, authorization should be enforced at the skill and business-action levels. Data isolation Agents should receive only the information required to complete their tasks. A research agent should not automatically receive the entire conversation history, database access, or credentials of another agent. Task and context storage should also respect user and tenant boundaries. Agent Card validation Agent Cards describe service identity and capabilities. For sensitive environments, clients should verify the origin of Agent Cards and validate their signatures when available. A discovery result should not automatically be treated as a trusted execution target. Observability and auditing A multi-agent system needs visibility beyond individual model calls. We should record: - Caller and target agent identifiers - Task and context identifiers - Correlation and trace identifiers - Task state transitions - Request duration and failure reasons - Artifact metadata and access - Sensitive action approvals OpenTelemetry and distributed tracing can help follow a request across multiple agents. Push notification security Push notification webhooks need their own security controls. For example, the receiver should authenticate the notification source, validate the task identifier, and process duplicate notifications safely. Webhook endpoints should also be protected against SSRF and unauthorized access. These controls are application and infrastructure responsibilities that must be designed alongside the protocol integration. Reference: A2A — Enterprise Implementation 11. What Can an AI Chapter Lead Contribute? I think this is where A2A becomes especially interesting from an engineering leadership perspective. The value is not simply adding another protocol to the technology stack. It is establishing a reusable architecture for teams that want to build specialized agents independently and allow them to collaborate. There are several areas where we can contribute. 1. Establish a common agent communication standard Different teams may build agents using different languages and frameworks. Instead of forcing everyone to adopt the same framework, we can define a common communication contract based on A2A. This allows teams to own their agent implementations while maintaining a consistent integration approach. 2. Build an internal agent registry We can create a centralized catalog of available agents and their capabilities. For example: | Agent | Responsibility | Available skills | |---|---|---| | Research Agent | Information gathering | Research, source analysis | | Database Agent | Data analysis | SQL analysis, reporting | | Code Review Agent | Code quality | Review, recommendations | | Security Agent | Security analysis | Vulnerability assessment | | Documentation Agent | Documentation | Search, summarization | Each agent can publish an Agent Card, while the internal registry handles discovery, ownership, versioning, authorization, and availability. This would help teams reuse existing capabilities instead of implementing the same functionality repeatedly. 3. Define a reusable orchestration architecture A2A should be combined with a clear orchestration strategy. We can establish a reference architecture that separates: - Agent discovery - Task delegation - Agent communication - Execution state management - Tool and data access - Result aggregation - Human approval The coordinator should remain responsible for task planning and result aggregation. Specialized agents should focus on their assigned responsibilities. 4. Establish security and governance standards A2A introduces communication between independently managed systems. We should define clear standards for agent authentication, authorization, data sharing, tenant isolation, and audit logging. This is especially important when agents interact with internal systems or execute business operations. 5. Run a practical multi-agent pilot Rather than introducing A2A across every team immediately, we can start with a well-defined workflow. A good example would be an automated merge request review process. Example workflow: 1. A developer submits a merge request. 2. The Coordinator Agent receives the review request. 3. The Security Agent analyzes potential vulnerabilities. 4. The Testing Agent evaluates test coverage and identifies missing cases. 5. The Architecture Agent reviews design and implementation decisions. 6. Each specialist returns findings through A2A. 7. The Coordinator combines the results into a structured review report. 8. A developer reviews the recommendations before any changes are merged. Each agent can use its own MCP servers to access GitLab, documentation, test tools, or other required services. This allows us to evaluate the actual value of agent specialization and interoperability. 6. Measure the results A successful pilot should be evaluated using real measurements. Some useful metrics: - Task completion rate - Agent-to-agent communication latency - Overall workflow duration - Failed or timed-out tasks - Duplicate execution rate - Token and infrastructure costs - Human intervention rate - Quality of the final aggregated result The objective is to determine whether distributing work across specialized agents produces better results than a single-agent implementation. 7. Create reusable learning resources A practical reference implementation, architecture documentation, and internal workshops can help developers understand when A2A is useful. Teams should learn not only how to implement the protocol, but also when not to use it. Not every internal function needs to become a remote agent. For simple operations within the same runtime, a regular function or framework-native tool call may be more appropriate. 12. When Should We Use A2A? A2A is most useful when communication between independent agents is an actual architectural requirement. | Scenario | Recommendation | |---|---| | Agents built with different frameworks need to collaborate | A2A | | Different teams expose independently maintained agents | A2A | | Remote, long-running agent tasks need tracking | A2A | | Agents need to exchange structured outputs and files | A2A | | An agent needs access to APIs and databases | MCP | | A single agent needs internal tool execution | Native tools or MCP | | Multiple agents operate within one tightly coupled workflow | Framework-native orchestration may be sufficient | The important thing is to avoid introducing network boundaries and protocol complexity when a simpler implementation would work. A2A should solve an interoperability requirement, not become an additional abstraction by default. Conclusion A2A addresses an important challenge in the evolution of agentic AI systems: enabling independently developed agents to communicate and collaborate. As AI applications become more specialized, a single agent will not necessarily be responsible for every task. Different agents may own different capabilities, use different frameworks, and operate in different environments. A2A provides a standardized way for these systems to discover each other, delegate tasks, exchange information, and track execution. Combined with MCP, it creates a useful architectural separation: MCP connects agents to tools and external context, while A2A connects agents to other agents. For an AI Chapter Lead, the opportunity is to establish common integration standards, build an internal agent ecosystem, define security boundaries, and help teams develop reusable capabilities. My recommendation: Start with one practical multi-agent workflow, implement it using the official A2A SDK, and evaluate the operational benefits against a conventional single-agent or framework-native orchestration approach. The real question is not how many agents we can connect. It is whether connecting specialized agents makes the system more reliable, maintainable, and effective. --- References and Further Reading 1. A2A Protocol — Official Website — Overview, architecture, ecosystem, and documentation. 2. A2A Protocol — Specification v1.0 — Protocol operations, data models, task lifecycle, transports, and security. 3. A2A — Agent Discovery — Agent Cards, discovery mechanisms, and capability descriptions. 4. A2A — Life of a Task — Task lifecycle and interaction patterns. 5. A2A — Streaming and Asynchronous Operations — Streaming, task updates, and push notifications. 6. A2A — Enterprise Implementation — Authentication, authorization, observability, and enterprise security. 7. A2A — Official JavaScript SDK — TypeScript client/server implementation, transport adapters, and runnable examples. 8. A2A — Official GitHub Organization — SDKs, samples, and project development. 9. AI SDK — A2A Community Provider — Integrating A2A agents with Vercel AI SDK. 10. A2A AI Provider — Community provider implementation and current maturity status. 11. Model Context Protocol — Complementary agent-to-tool integration protocol.
One extra field turned an A2A error into a TypeError. Fixed in a2a-sdk 1.3.0 --- title: One extra field turned an A2A error into a TypeError. Fixed in a2a-sdk 1.3.0 published: true description: I fed the A2A Python client seven bad agent replies. A valid error code came back as a TypeError. The fix shipped in a2a-sdk 1.3.0. tags: python, ai, agents, opensource cover_image: raw.githubusercontent.com --- <!-- Images are pushed to Sonica-B/Sonica-B under blog/, so the URLs below resolve. The interactive version of figure 1 is visibility/anatomy/a2a-sorter/a2a-sorter.html. --> On October 27, 2026, SAP announced general availability of inbound Agent2Agent (A2A) interoperability, so third-party agents can now invoke SAP Agents through its Agent Gateway. Every launch like this means more A2A clients calling agents their authors do not control. Two days before SAP's announcement, I had opened a pull request for that exact situation in the A2A Python SDK. An agent could send a valid A2A error, and the calling code would receive a TypeError. My fix shipped the same day as SAP's news, in a2a-sdk 1.3.0. A2A is an open protocol that lets AI agents discover each other and exchange messages and tasks over JSON-RPC, REST or gRPC. It joined the Agentic AI Foundation in August 2026, and a2a-sdk is its official Python SDK. The short version When an agent sent back an error, the SDK handed the reply to a third-party library before looking at it itself. That library is strict about which fields an error may carry, so one extra field, the kind the A2A spec says to ignore, made it throw a raw TypeError. The SDK's own translation of error codes into named exceptions never got a turn. Version 1.3.0 reads the reply itself first, at one place, and the library only builds requests now. The concept, animated !Animated figure. The reply's path through JsonRpcTransport as labelled blocks: agent reply, the json-rpc package's JSONRPC20Response(**body), the SDK's _create_jsonrpc_error(), your code with its except TaskNotFoundError branch, and three bins for TaskNotFoundError, A2AClientError and result. A token labelled {"code": -32001, "reason": "x"} drops into a raw TypeError tray at the json-rpc block. In 1.3.0 that block lifts out, _check_jsonrpc_response() takes its place, and the token reaches the TaskNotFoundError bin; a body that is a list reaches A2AClientError. Figure 1. The blocks are the real call chain in client/transports/jsonrpc.py, in order. The token is the reply body. The three states: an error object with one extra field before 1.3.0, the same reply in 1.3.0, and a body that is not an object in 1.3.0. A client talks to agents it does not control, so odd replies are normal. The SDK's promise about them is clear: a failure reaches your code either as A2AClientError or as a typed A2A error such as TaskNotFoundError. The A2A spec (section 5.7) adds that implementations should ignore fields they do not recognise. Now follow the token in the first state. It carries the error code -32001, which means TaskNotFoundError, plus one extra field, reason, that the spec says to ignore. The first block it reaches is JSONRPC20Response(**body) from the json-rpc package. It validates the envelope before the SDK sees anything, and it builds the error with a constructor that accepts only code, message and data. Any other member is, to that library, a programming mistake. So the token drops into the tray labelled raw TypeError: JSONRPCError.__init__() got an unexpected keyword argument 'reason'. The SDK's _create_jsonrpc_error() block never receives it, and your except TaskNotFoundError: branch never runs. The library was correct by its own rules. It simply ran first, so its rules won and the SDK's contract lost. How I found seven of these I was exploring the client with one question: what reaches my code when an agent answers wrong? No issue existed, so I played the misbehaving agent myself, wrote seven bad response bodies by hand, and fed them to the client through a mock transport. All seven came back as raw Python exceptions. | What the agent sent | Before | After | |---|---|---| | a list, or a string (say, from a proxy) | TypeError | A2AClientError | | neither result nor error | ValueError | A2AClientError | | "error": "boom" | TypeError | A2AClientError | | error object with an extra member | TypeError | TaskNotFoundError | | error object without message | ValueError | TaskNotFoundError | | SSE data: that is not JSON | JSONDecodeError | A2AClientError | | event: error with an extra member | TypeError | TaskNotFoundError | Following one reply through the code explained the whole table. Nine request methods handed the body straight to the json-rpc validator, and streaming used the same validator on each event. The SDK's own mapping from error codes to exceptions even had a fallback for a missing message, but it could never execute, because the library failed first. Streaming had a second twist: the JSON parse for each event ran in a place where its exception escaped the handler that was supposed to catch it, so a broken data: line leaked a JSONDecodeError. The fix, in one idea !Still from the third state: the json-rpc block is lifted out and faded, the SDK's _check_jsonrpc_response() block stands in its place, and a token labelled "a list, not an object" sits in the A2AClientError bin. Figure 2. In 1.3.0 the json-rpc validator is out of the chain. The SDK reads the envelope itself at one choke point, so an error object becomes the typed A2A error whatever extra fields it carries, and a body that is not even an object becomes A2AClientError. Check the wire envelope yourself, once, and hand libraries only what they can handle. Every reply already passes through one sending function or the streaming loop, so I put one check there. It asks three plain questions in order: is the body an object; if it has an error, is that an object, and if so, map it to the A2A exception by its code; otherwise, is there a result. Anything that fails those questions becomes A2AClientError with a message that says what was wrong. The streaming loop turns a JSONDecodeError into A2AClientError the same way. A falsy error next to a result still counts as success, as before. The client no longer builds JSONRPC20Response objects at all; requests still use JSONRPC20Request. Wrapping each of the nine calls in its own try/except would have turned TaskNotFoundError into a generic error and copied the same code nine times. One choke point keeps the typed exceptions. The tests live in a new class, TestMalformedJsonRpcResponses, with 11 cases. Ten fail on main with the exceptions in the table and pass with the change. The eleventh guards that delete_task_push_notification_config still raises on an error response. The existing tests pass unchanged. I ran everything on Windows 11 with Python 3.10.19: lint.sh passed, the transport tests gave 51 passed, and the wider run gave 2213 passed at 93% total coverage. Three failures and four setup errors fail the same way on main, and two integration suites do not run on Windows, so I left those to Linux CI. All 17 checks passed there, and the coverage bot showed client/transports/jsonrpc.py going from 92.66% to 95.51%. What changed for people using a2a-sdk If your client calls agents you do not control, version 1.3.0 behaves differently in three ways: - An agent that adds its own field to an error object, or leaves out message, now raises the mapped A2A error, so your except TaskNotFoundError: branch runs. - A malformed body, such as a list, a string, a missing result or a non-object error, raises A2AClientError, so one except clause covers all of them. - A stream with a broken data: line raises A2AClientError where it used to leak a JSONDecodeError. One behaviour change is worth knowing. A body with "error": null and no result used to parse as an empty object and now raises A2AClientError. That body breaks JSON-RPC 2.0 anyway. Upgrading is pip install -U a2a-sdk. The v0.3 compatibility transport still uses the same validator-first pattern on main. I kept this PR to the 1.0 client and offered a fix(compat) follow-up in the PR body. A thank-you to Iwaniukooo11 and the A2A maintainers I opened PR #1375 on October 25 with no linked issue, just the table and the tests. On October 26, Mateusz Iwaniuk (@Iwaniukooo11) brought the branch up to date with main, approved it and merged it. Thank you. Getting a first PR to a repo reviewed and merged the day after it opens is a generous welcome. Thanks also to the A2A maintainers for keeping the door open to outside contributors. Of the 40 merges before mine, 29 came from non-members, with a median of 3.4 days from open to merge. Release 1.3.0 carries fixes from at least three outside contributors, mine among them. What I took away Where a third-party validator sits in your call chain decides which exceptions your callers see. The stack trace ended inside json-rpc, but the decision that mattered was where the SDK called it. When you own an error contract, check the wire envelope yourself at one choke point and hand libraries only what they can handle. As more vendors open their agents to outside callers, the exception a client can catch by name is part of the interface, and that boundary is the kind of problem I like to work on. I'm Shreya Boyane, an ML engineer who builds agent systems, and I'm open to ML and AI engineering roles. You can find my work on GitHub, more about me at shreyaboyane.dev, and I'm happy to connect on LinkedIn. <!-- optional disclosure she may keep: An AI coding agent helped me probe the package and draft this fix; I reviewed and verified the change before submitting. -->
@AgenticAIFdn If you want to try it, start with the first lesson in the cookbook, examples/01-exec-demo: a2aproject/a2a-cli
MCP vs A2A vs AG-UI: The Protocol Stack, Fact-Checked > Originally published on DevToolHub. MCP vs A2A vs AG-UI is the comparison chart making the rounds right now, and the framing is right — these are three different, complementary layers, not competing standards. But a couple of its headline numbers are either stale or don't hold up against the protocols' own sources, and it skips the actual news: MCP's July 2026 spec update is a real breaking-change event, not a one-word "stateless" bullet point. Here's what each protocol actually does, fact-checked against their own docs and GitHub repos, plus what the chart got right and what's worth correcting. For the agent concepts underneath all three, see our AI agents explained guide. The Three Protocols, What Each One Actually Connects The "vs" in every comparison title is misleading — you don't pick one. Each protocol solves a different connection problem, and a real agentic system typically uses all three at once: - MCP (Model Context Protocol) — built by Anthropic — connects an agent to tools and data. Anthropic's own docs describe it as a standardized way to connect AI applications to external systems, "like a USB-C port for AI applications." If your agent needs to read a file, query a database, or call an API, this is the layer — see our MCP registry breakdown for how big that ecosystem already is. - A2A (Agent2Agent Protocol) — built by Google, now governed by the Linux Foundation — connects one agent to another. It's how two independent agents, possibly built by different teams on different frameworks, discover each other and hand off work. - AG-UI (Agent-User Interaction Protocol) — built by CopilotKit and its open-source community, not Google (a detail the viral chart gets right, worth double-checking since AG-UI's name invites confusion with Google's A2A) — connects an agent to the human watching it work. It's an event-based protocol for streaming an agent's state and output into a real UI, not a chat log. None of these three compete for the same job. A production agent that reads your CRM (MCP), delegates a sub-task to a specialist agent (A2A), and shows a human the live progress bar while it works (AG-UI) is using all three simultaneously. MCP's July 2026 Spec Update Was Bigger Than "Stateless" The chart's MCP row says "Stateless spec, July 2026" and leaves it there. That's the right headline, but it's doing a lot of work in one line — per MCP's own 2026-07-28 changelog, this is the largest rewrite since launch: protocol-level sessions and the Mcp-Session-Id header are gone, the initialize handshake is gone, and Roots, Sampling, and Logging are now formally deprecated with a twelve-month removal window. We already covered the full breaking-change list and a real server-migration checklist in MCP Server Authentication: What the 2026-07-28 Spec Broke — if you're migrating an MCP server, that's the piece to work from. What's actually new to check, and what the chart doesn't carry at all: per Anthropic's own announcement, MCP is at 400M monthly SDK downloads as of that July 28 update — a real, sourced number that's meaningfully below the chart's "close to 500M" claim, and the real reason for the stateless move is architectural: "MCP moves from a bidirectional stateful protocol to a request/response model. Servers can now deploy on serverless and edge infrastructure" — something the old session-pinned design couldn't do cleanly. A2A's Real Status — v1.0, Signed Agent Cards, and a Number Worth Correcting The chart says A2A hit "v1.0 with signed Agent Cards, March 2026." The version and date both check out — A2A's GitHub releases show v1.0.0 published March 12, 2026, and the live spec's section 8.4 ("Agent Card Signing") does define canonicalization, signature format, and signature verification requirements so agents can cryptographically sign their Agent Cards for authenticity. Worth knowing if you're building against it today: v1.0 isn't the current patch anymore — v1.0.1 shipped May 28, 2026 with HTTP binding and TaskStatus fixes. Governance checks out too: Google originally built A2A and donated it to the Linux Foundation, and as of August 20, 2026 it formally joined the Agentic AI Foundation (AAIF) — the same body that already hosts MCP — putting both protocols under one neutral governance structure instead of two separate vendor-led ones. A2A's Technical Steering Committee includes representatives from AWS, Cisco, Google, IBM Research, Microsoft, Salesforce, SAP, and ServiceNow. The one number worth double-checking: the chart lists A2A at "150+ organizations behind it." I couldn't find a primary source attributing that specific figure to A2A alone. What I could verify, directly from the Linux Foundation's own press release, is AAIF's total membership — the body that now governs both A2A and MCP — which states it "grows to 247 member organizations with the addition of 57 new members over the past three months." That's a materially different, larger number than "150+," just measuring the whole foundation rather than one protocol's backers specifically. If the chart's figure is from an earlier AAIF member count, it's stale now. AG-UI's Real Adoption — 16.7k Stars and a Fourth Integration the Chart Skips AG-UI's chart row says "15K+ GitHub stars." Checking the ag-ui-protocol/ag-ui repo directly, it's actually at 16.7k stars — the chart's number is accurate but already a bit behind. More interesting: the chart lists three 1st-party framework integrations (Google ADK, Microsoft Agent Framework, AWS AgentCore). The repo documents a fourth — AWS Strands Agents is also officially supported, alongside AWS Bedrock AgentCore, not instead of it. If you're picking an agent framework specifically because it has AG-UI support baked in, Strands is a real option the chart doesn't mention. Where ACP Went The chart's footer note — "Where's ACP? IBM merged it into A2A in August 2025" — checks out exactly. IBM Research's Agent Communication Protocol, built to power its BeeAI platform, overlapped heavily with A2A (both covered agent-to-agent discovery and messaging). The original ACP repository itself confirms it: archived August 27, 2025, with a banner reading "ACP is now part of A2A under the Linux Foundation!" and a link to the migration guide. If you see ACP referenced in older material, it's a dead end now — the real destination is A2A. Do You Need All Three? A Practical Decision Guide - Your agent needs to read or write something external — a CRM, your docs, a GitHub repo, a database: that's MCP. One server, any MCP-compatible client can use it — our best MCP servers by category guide is a starting point. - Your agent needs to hand off a task to a different, independently-built agent — possibly on a different framework, possibly a different vendor: that's A2A. The Agent Card is how the other side learns what your agent can do before the handoff. - A person needs to watch your agent work and approve or steer its steps in real time, not just read a final answer: that's AG-UI. It's the layer between the agent's internal state and whatever UI framework renders it. - You're building a single-tool integration with no multi-agent handoff and no live UI — you may genuinely only need MCP. Don't add A2A or AG-UI to a system that doesn't have another agent or a human-in-the-loop UI to connect to. Frequently Asked Questions Q: Are MCP, A2A, and AG-UI competing standards? A: No. They connect different things — MCP connects an agent to tools and data, A2A connects one agent to another, AG-UI connects an agent to the person watching it. A single system can use all three at once. Q: What actually changed when MCP went stateless in July 2026? A: Sessions, the initialize handshake, and three features (Roots, Sampling, Logging) all changed at once — see our full MCP server authentication breaking-change checklist for the complete list. The number worth knowing that isn't in that piece: Anthropic's own blog puts MCP at 400M monthly SDK downloads as of this update. Q: Who governs MCP and A2A now? A: Both are hosted projects under the Agentic AI Foundation (AAIF), part of the Linux Foundation. A2A joined AAIF on August 20, 2026, after Google donated it; MCP was already a founding AAIF project. AAIF's own press release puts total membership at 247 organizations. Q: Does AG-UI only work with Google, Microsoft, and AWS AgentCore? A: Those three are 1st-party supported, but so is AWS Strands Agents — a fourth framework with official AG-UI support that's easy to miss if you're only going by popular comparison charts. Q: What happened to ACP (IBM's Agent Communication Protocol)? A: It merged into A2A. The original repository was archived August 27, 2025, with a notice directing developers to A2A's migration guide. Quick Summary: - MCP, A2A, and AG-UI are complementary layers (tools, agent-to-agent, agent-to-human), not competing alternatives - MCP's July 28, 2026 spec update (sessions, the initialize handshake, Roots/Sampling/Logging all changed) is covered in full in our MCP server authentication piece - Per Anthropic's own blog, MCP is at 400M monthly SDK downloads as of that update — not "close to 500M" - A2A hit v1.0.0 in March 2026 (now on patch v1.0.1) with real signed Agent Cards; AAIF's own total membership is 247 organizations, a different number than "150+" - AG-UI is at 16.7k GitHub stars with four 1st-party framework integrations (Google ADK, Microsoft Agent Framework, AWS Bedrock AgentCore, and AWS Strands Agents) - ACP (IBM) merged into A2A in August 2025 and is a dead end if you see it referenced The real answer to MCP vs A2A vs AG-UI is: pick protocols by the connection you actually need, not by which chart you saw first — and if you're touching an MCP integration built before July 2026, check it against the real breaking-change list above before assuming "stateless" just means a minor version bump.
A2A Protocol Authentication Security: Agent Cards and Signed Requests Originally published on robatdasorvi.com The lockout check fired and returned True. The caller skipped one action, and the loop kept running. For three and a half days after X suspended my account, the bot carried on as if nothing had happened. The check was right; the caller treated a suspension like one failed action. I keep thinking about that gap, because A2A protocol authentication security runs into the same problem: a component returns a verdict, and whatever calls it decides whether anyone listens. A2A (Agent2Agent) is the open protocol for agents built by different teams to discover and call each other. In it, that gap sits between verifying who a remote agent is and deciding whether to act on what it sends. Is an A2A Agent Card something I can trust? Not by default. An Agent Card is a JSON document a remote agent publishes to describe itself and its endpoints. Reading one tells you what the agent claims. It doesn't tell you who stands behind the claim. The card is served at the well-known path /.well-known/agent-card.json on the agent's own domain. Signed cards arrived in v0.3.0, whose release notes list "Add signatures to the AgentCard." Per the A2A specification, signatures are JWS over the card after it is canonicalized with the JSON Canonicalization Scheme (RFC 8785), with the signatures field itself excluded. Signing is optional for the publisher, and a client SHOULD verify at least one signature before trusting a card. Why not sign the card and move on? Because a valid signature answers one narrow question: did the holder of this key sign these exact bytes? It doesn't tell you whether that key belongs to an organization you chose to deal with, whether the key is still trusted, or whether the agent behind it does what the card says. The weak point is rarely the cryptography. A signed document is only as trustworthy as the list of keys you decided to accept, and that list rarely gets a security review. A signature tells you the card wasn't altered after signing. It says nothing about whether the signer should have been allowed to publish it in the first place. The field isn't ready for this. A 2026 study by Tam Nguyen, Moses Ndebugre and Dheeraj Arremsetty scored 16 AI security frameworks against 193 threat items and found that "No reviewed framework achieves majority coverage of any single category" (arXiv:2603.09002). That paper doesn't discuss Agent Cards, so I'm citing it only for the coverage gap. The earlier Habler et al. threat model names Agent Card spoofing directly: a forged card hosted on a malicious domain enables task hijacking and impersonation (arXiv:2504.16902). How do you verify an A2A Agent Card signature? Fetch the card over TLS from the domain you expect, canonicalize it with RFC 8785 after removing the signatures field, and verify each JWS against a key from a trust store you control. Reject the card if any check fails. A passing check shows the key is trusted, not that the agent behaves. 1. Fetch the card over TLS from the expected domain. Use the well-known path /.well-known/agent-card.json on that domain, and don't follow redirects to hosts you did not expect. 2. Remove signatures and canonicalize. Apply RFC 8785 (JSON Canonicalization Scheme) to the remaining content. The spec excludes signatures from signed content to avoid circular dependencies, so verify exactly that byte sequence. 3. Verify each JWS against a key from a trust store you control. Never take the key only from a URL embedded in the card. The spec requires alg, typ and kid in the protected header, but an attacker can name their own key, so the header proves nothing about who holds it. 4. Reject an unknown kid, an expired key or a missing signature, and fail closed. A card without a valid signature is unverified. Logging a warning and continuing is not a passing result. 5. Cache with a TTL, and re-verify when the card changes. A cached "verified" flag must not outlive the key that produced it. 6. Record every rejection, and alert when the rejection count stays at zero for a long time. A verifier that never rejects may not be wired in, or may be checking against rules that no longer match reality. My bot's lockout check failed the second way: during the second suspension it matched none of its known selectors, so it never fired. What do static API keys and allowlists cost in A2A? A hardcoded allowlist of agent URLs plus one long-lived API key per partner fails silently: stale entries keep routing calls, and a leaked key works until someone notices. Its costs never show up on a dashboard, which is why people keep choosing it. Look at the credential habits. Astrix Security's report, published October 15, 2025, examined about 5,200 open-source MCP server implementations (5,205 GitHub repositories). It found that 53% rely on static secrets, mostly API keys and personal access tokens. Only 8.5% use OAuth, and 79% of API keys are passed through plain environment variables (Astrix Security, 2025). Those are MCP servers, not A2A agents, but the habit carries over. A static secret is cheap on day one and expensive on the day it leaks. The hidden cost I know firsthand is quieter than a leak. For about six weeks, four hardcoded target accounts that no longer existed soaked up 88% of my bot's daily comment attempts. Every run logged success. Nothing raised an error. A hardcoded list is cheap to write and expensive to be wrong about, because it fails silently. A trusted-agent allowlist fails the same way. The partner moves its domain, the entry stays, and every call to the old address either fails softly or, in the bad version, reaches whoever now controls that address. The single-integration model has the same problem at a much larger scale. Google Threat Intelligence Group reported that beginning as early as August 8, 2025, through at least August 18, 2025, an actor used OAuth tokens tied to the Salesloft Drift integration to export data from numerous corporate Salesforce instances (GTIG, August 2025). GTIG described the victims as numerous Salesforce instances. One integration, trusted once, reached numerous tenants. Nothing was forged. The tokens were real, and so was the integration. The convenient trust was the vulnerability. Has anyone exploited agent-to-agent trust in production? I have found no public report of an A2A-protocol trust exploit in the wild. The closest documented case is ServiceNow's Now Assist, which uses ServiceNow's own agent-discovery mechanism, not A2A, and its lessons still carry over. AppOmni found CVE-2025-12420, rated CVSS 9.3, in Now Assist AI Agents and the Virtual Agent API. ServiceNow patched it and said it was unaware of the issue being exploited in the wild against customer instances (NVD). AppOmni's BodySnatcher write-up says an unauthenticated attacker could impersonate any ServiceNow user with only their email address, because the providers shipped with the same static client secret across all ServiceNow instances and required only the email address to link an external entity to a ServiceNow account. The attacker could then run AI agents as that user (AppOmni, BodySnatcher). AppOmni's second finding is not a CVE but default behavior, and it matters more. In AppOmni's November 19, 2025 write-up, agents deployed to the same virtual agent were "automatically grouped into the same team by default." Published agents were "marked as being discoverable by default." And agents ran with "the privilege of the user who started the interaction," not the user who planted the malicious content (AppOmni). Nobody had to break a signature. The trust came from shipped defaults. Neither finding proves a wild campaign. But when the dangerous configuration is the default, "rare misconfiguration" stops working as a defense. What does a request-time check require? Every request needs a credential the server can verify, sent over TLS, with a scope tied to that specific call. The A2A security scheme types include API key, HTTP auth, OAuth 2.0, OpenID Connect and mutual TLS, so a card can declare what a caller must present (A2A specification). Declaring a requirement is not the same as enforcing it. The A2A spec signs the Agent Card. Authenticating each request is left to whichever security scheme the card declares, so a signed card does not mean signed requests. If you need integrity on the message body itself, HTTP Message Signatures (RFC 9421, rfc-editor.org) is one option, but it sits outside A2A and both sides must agree to use it. Mutual TLS looks like the grown-up default. For agent calls across organizations, though, it just moves the stale-entry problem into certificate issuance and rotation for every partner. You get the same allowlist rot, with more paperwork and a better audit log. Per-call authorization matters most, where the server checks the caller and the action together. I wrote about that in Policy as Code for AI Agent Tool Calls: Authorizing Actions with OPA or Cedar. The ServiceNow defaults are also why the scope of a delegated identity deserves the care described in Least Privilege for AI Agents: Scoping Tools, Keys and Filesystems. What's still unresolved? I don't know who should own the trust list for a multi-party A2A network. An operator-maintained list can rot the way my bot's hardcoded target list did. A federated model, where you trust an issuer rather than a URL, moves the failure into the issuer's key management, and I have no data on how often issuers rotate keys badly. Feature flags can disable a remote agent without a redeploy, as I described in Feature Flags for AI Agent Tools: Disabling One Capability in Production Without a Redeploy. But a kill switch only helps if something notices the agent has gone bad. Nothing here is measured against a live A2A deployment. I haven't verified a signature check against a real remote card. The question I'm left with is the one the lockout check taught me: when the next quiet failure happens, will the code around it be listening? --- Note: I could not finish verifying change 15. The A2A spec page was truncated before the signing and authentication sections, so the sentence "Authenticating each request is left to whichever security scheme the card declares" is not confirmed against the spec text. The BodySnatcher wording in change 9 is confirmed against the AppOmni post, which uses "exact same secret across all ServiceNow instances" rather than "hardcoded platform-wide secret," so I used the post's own phrasing. Change 8 used option (b), so no 700 figure is cited.
@stevekrouse haven't used it but this is a thing a2aproject/a2a
Controlling Access to A2A Agents with agentgateway At Qodo, we've publicly described how PR-Agent became an A2A agent for MOSAICO, a research project coordinating software-engineering agents. Other agents can delegate code reviews and receive results. When you expose an agent this way, how do you control who can call it and verify that rejected requests never reach it? This walkthrough puts a sample agent behind agentgateway, checks caller credentials, and records which requests reach the backend! What does A2A make possible for a code-review agent? Agent2Agent, or A2A, defines how independently deployed agents exchange messages, tasks, and results. An agent card advertises an agent's capabilities and connection details. A client can discover a reviewer, send work, and receive its response without sharing the reviewer's implementation. Qodo's public PR-Agent integration illustrates that boundary. MOSAICO can delegate a review to PR-Agent and receive review output. That establishes a useful interaction, but operating an endpoint also requires decisions about callers, credentials, and the resources each request may access. For the access tests, a deterministic Hello World agent is enough. It returns a greeting instead of reviewing code. That keeps model behavior out of the experiment and makes backend execution easy to observe. Where does agentgateway sit in the request path? Agentgateway's A2A example places a proxy between the client and the agent. The tested setup uses agentgateway 1.6.0, Python 3.13.4, A2A Python SDK 1.1.0, and the A2A 1.0 JSON-RPC binding. Its executor comes from this pinned upstream sample. Requests go to the gateway on port 18760; the sample backend listens on 127.0.0.1:18761. Request logging and an executor-entry record let the test compare what the gateway returned with what the agent actually received and ran. !Client requests pass through JWT validation and a reviewer-role check in agentgateway. Invalid credentials return 401; rejected roles return 403; accepted requests reach the sample agent. The tested request path. The backend remains reachable directly from the local machine; the diagram doesn't imply network isolation. Fetching the agent card through the gateway returned a supportedInterfaces URL pointing back to port 18760. That helps clients continue through the proxy. It doesn't prevent someone who knows a reachable backend address from calling it directly. How do you define what can reach the agent? The route combines JWT authentication with an authorization rule. A JSON Web Token carries signed claims about its issuer, intended audience, and caller. The gateway checks the signature against a JSON Web Key Set, or JWKS, containing the issuer's public key. Here's the route portion of the tested configuration: binds: - port: 18760 listeners: - routes: - policies: a2a: {} jwtAuth: mode: strict issuer: https://issuer.example.test audiences: [a2a-demo] jwks: file: ./jwks.json authorization: rules: - require: 'jwt.role == "reviewer"' backends: - host: 127.0.0.1:18761 The issuer here is a synthetic test identity, not a service to sign into. The runner creates an RSA key and short-lived tokens locally. In this release, the JWKS file path resolves from the process's working directory. strict requires a valid token. The issuer and audience constrain which tokens qualify; a separate rule requires the signed role claim to equal reviewer. That role is this demo's convention, not an A2A-defined permission. Keep those checks explicit. Agentgateway's default optional authentication mode permits requests without a token. A configuration that validates credentials when present can behave differently from one that requires credentials on every request. How do you prove a policy rejects the right requests? Each test sends a real SendMessage request with a unique message ID. The same identifier connects the request to the backend's records. Accepted cases must return the greeting without a JSON-RPC error; an HTTP 200 alone doesn't establish successful A2A execution. These were the observed outcomes with the configuration above: | Caller credentials | HTTP response | Backend requests / executor calls | |---|---:|---:| | Valid token, reviewer role | 200 | 1 / 1 | | No token | 401 | 0 / 0 | | Wrong audience | 401 | 0 / 0 | | Wrong role | 403 | 0 / 0 | | Missing role claim | 403 | 0 / 0 | | Invalid signature or expired token, tested separately | 401 each | 0 / 0 each | A valid request after the rejection cases also completed. Removing only the authorization rule let a valid token with the wrong role reach the executor. That control checks whether the role rule actually explains the rejection. One configuration change exposed a useful failure case. Replacing the requirement with this rule changed the result for a valid token missing role: authorization: rules: - deny: 'jwt.role != "reviewer"' That request returned 200 and executed once. With require, it returned 403 and never reached the backend. The documented evaluation rules explain why: accessing a missing claim makes the expression fail. A failed requirement rejects the request. A failed deny expression doesn't match; with this single deny rule and no allow rules, the request proceeds. JWT authentication stayed strict in both configurations. A missing token still returned 401. The finding concerns a missing application claim in an otherwise valid token, under these exact policies. What permissions belong inside the agent? The gateway established which tested requests could reach this endpoint. It didn't decide whether a caller may read a particular repository, retrieve a private diff, or write a change. A real reviewer needs those checks where it accesses the repository and invokes its tools. The bypass control matters too: calling this local backend directly without a token returned 200 and executed once. A deployment needs to restrict direct backend access or enforce equivalent checks there. Rewriting the agent card isn't a network access control. This reproduction used synthetic credentials, HTTP on a development machine, and a greeting executor. It didn't validate production TLS, identity-provider integration, streaming recovery, or repository permissions. The gateway's standalone listener binds a wildcard address; the backend and administrative endpoints were configured on loopback. For your own agent, start with an allowed request and specific rejected requests. Check both the response and backend execution. Then remove or change the relevant policy in an isolated test to verify which check caused the rejection. That gives you evidence for the boundary you've configured—and a clear account of what still needs protection.
Week 42: New A2A partners and Community Integrations Hi All, sharing a few A2A Updates. This week we have made some progress in our A2A v1.1 protocol and also added a new list of partners. Partners Added @ https://a2a-protocol.org/latest/partners/ - Agent Commons: A free, public home for independent AI agents offering directory profiles, restart-proof inboxes, and shared knowledge bases, accessible via an open JSON-RPC message/send endpoint. - DEUSPROOF: A public, immutable notary service that acts as a certificate of birth for AI agents, providing ownership verification, Ed25519 signing, and cryptographic time-stamping. - Neuronto: A registry and endpoint provider that serves a conformant A2A agent card and endpoint for agent discovery and communication. Community Integrations Added @ https://a2a-protocol.org/latest/community/ - Quilzo: A community-integrated framework featuring built-in A2A 1.0 support for agent cards, governance extensions, and secure JSON-RPC task execution. - CUGA Integration: Added CUGA (IBM Research's open-source agent harness) to A2A community integrations, allowing CUGA agents to be served over A2A and delegate tasks to remote agents.
The example has been replaced (PR #2319 approved), and the Python SDK will warn on the affected cards. Aligning the bytes waits for the next major release. I did not argue it. I measured it. A2A#2122 Not a claim about the work. The work.