SIVARO
MCP (Model Context Protocol)

The A2A Agent Communication Protocol Tutorial: What I Learned Building Multi-Agent Systems

I spent most of 2025 convinced that the agent interoperability problem was a branding problem. The LLM vendors kept telling us to standardize on their agent ...

agentcommunicationprotocoltutorialwhatlearnedbuildingmulti-agent
By Nishaant Dixit
The A2A Agent Communication Protocol Tutorial: What I Learned Building Multi-Agent Systems

The A2A Agent Communication Protocol Tutorial: What I Learned Building Multi-Agent Systems

Free Technical Audit

Expert Review

Get Started →
The A2A Agent Communication Protocol Tutorial: What I Learned Building Multi-Agent Systems

I spent most of 2025 convinced that the agent interoperability problem was a branding problem. The LLM vendors kept telling us to standardize on their agent frameworks, and the open-source community kept fracturing into tool-specific camps.

Turns out it was a protocol problem.

The Agent2Agent (A2A) protocol — announced by Google in April 2025 and donated to the Linux Foundation in June 2025 — is the first real attempt to make agents talk to each other the way HTTP made browsers talk to servers. This a2a agent communication protocol tutorial is what I wish I had when we started wiring SIVARO's production systems to external agents.

By the end of this, you'll know what A2A actually is, when it beats a plain API, and how to implement it without burning your engineering sprint. I'll show you working code, the failure modes we hit, and exactly where the protocol still hurts.

What Is A2A? (And Why It's Not MCP)

Here's the confusion that wastes everyone's first week: MCP (Model Context Protocol) and A2A solve different problems.

Model Context Protocol (MCP) vs. Agent2Agent (A2A) put it cleanly — MCP connects an agent to tools and data. A2A connects agents to other agents. Think of MCP as the agent's hands and A2A as its voice.

The A2A protocol defines how agents discover each other, send tasks, negotiate capabilities, and return results. It runs over HTTP with JSON-RPC. It's not a runtime. It's not a framework. It's a contract.

The core objects you'll work with:

  • Agent Card — a JSON file (usually at /.well-known/agent.json) describing the agent's identity, skills, and endpoints
  • Task — a unit of work with states like submitted, working, input-required, completed, failed
  • Message — what agents exchange within a task (text, structured data, files)
  • Artifact — the output of a task

That's the whole mental model. Everything else is implementation detail.

You can read the deeper architectural comparison in MCP vs A2A: A Guide to AI Agent Communication Protocols from Auth0, but the short version is: MCP is for getting data into your agent. A2A is for getting work done by other agents.

I had to explain this to a client in July when they wanted to replace their entire microservice architecture with A2A. You don't. A2A for agentic workflows, REST for deterministic services.

Why I Moved From Custom Agent APIs to A2A

Here's what pushed me over the edge.

In March 2026, we were building a supply chain agent for a logistics customer. It needed to coordinate with three external systems: a weather data service, a customs broker's internal AI, and a competitor's shipment tracking agent.

The weather service had a REST API. Fine. The customs broker's AI was a GPT wrapper with a documented JSON schema. Fine. The competitor's agent? They told us — and I quote — "just hit our /think endpoint, it accepts a prompt."

That's when I realized: every partner defines "agent API" differently. One expects prompt strings. Another wants structured task objects. Another exposes raw MCP tools. We were building bespoke connectors for every single integration.

That's the problem A2A solves. It's a standard way to say "here's a task, here's what I need, here's how you give it back to me."

The A2A protocol and MCP: When to use which in Elasticsearch makes the distinction practical: "A2A is useful when you're connecting agents that each have their own context and autonomy. MCP is useful when you're connecting an agent to data sources."

I'd add one more: A2A is useful when the agent on the other end is a business partner, not a dependency.

How A2A Works: The Request Flow

Let me walk you through the protocol mechanics. It's simpler than you think.

1. Discovery

Every A2A agent exposes an Agent Card at a well-known URL. Here's a minimal example:

json
{
  "name": "SIVARO-Inventory-Agent",
  "description": "Handles inventory forecasting and stock alerts",
  "url": "https://agents.sivaro.com/inventory",
  "version": "1.0.0",
  "skills": [
    {
      "id": "forecast",
      "name": "Demand Forecasting",
      "description": "Generate 30-day demand forecasts from sales data"
    },
    {
      "id": "reorder",
      "name": "Reorder Suggestions",
      "description": "Suggest purchase orders based on inventory levels"
    }
  ],
  "security": {
    "schemes": ["oauth2"],
    "credentials": {
      "bearer": {
        "format": "opaque"
      }
    }
  }
}

Client agents GET this card, read the skills, and decide if this agent can do what they need. Discovery is just a JSON file. That's it.

2. Task Creation

Once you know an agent exists and what it can do, you send a task:

json
POST https://agents.sivaro.com/inventory/task
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "tasks/send",
  "params": {
    "id": "task-2026-08-26-001",
    "message": {
      "role": "user",
      "parts": [
        {
          "kind": "text",
          "text": "Forecast demand for SKU-4472 for the next 30 days."
        },
        {
          "kind": "file",
          "mimeType": "text/csv",
          "data": "data:image/png;base64,..."
        }
      ]
    },
    "metadata": {
      "customer": "logistics-co-op",
      "priority": "high"
    }
  }
}

The receiving agent responds with a task object. Crucially, the response can be synchronous or asynchronous. If your task takes 30 minutes, the agent returns accepted and you poll for updates. If it's fast, you get completed immediately.

3. Polling and Events

For long-running tasks, the protocol gives you two options: polling or webhooks.

python
import requests

async def poll_task(agent_url, task_id, max_attempts=60):
    for attempt in range(max_attempts):
        response = requests.post(agent_url, json={
            "jsonrpc": "2.0",
            "method": "tasks/get",
            "params": {"id": task_id}
        })
        task = response.json()["result"]
        if task["status"] in ("completed", "failed", "canceled"):
            return task
        time.sleep(2)  # Back off properly in production
    raise TimeoutError(f"Task {task_id} did not complete in time")

Webhooks are better for production. You register a callback URL when you send the task, and the agent pings you when state changes. We've run both — webhooks cut our API call volume by 90% in the forecasting pipeline.

4. Streaming Partials

For agents that produce incremental output (think chat responses or long file transformations), A2A supports streaming. You can get artifact updates before the task completes:

javascript
// Partial artifact streaming
const response = await fetch(`${AGENT_URL}/task/${taskId}/stream`, {
  method: 'GET',
  headers: { 'Accept': 'text/event-stream' }
});

const reader = response.body.getReader();
const decoder = new TextDecoder();
while (reader) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value);
  // Parse SSE event, extract artifact delta
  processArtifactChunk(JSON.parse(chunk.split('data: ')[1]));
}

Streaming is where A2A feels genuinely different from a REST API. You're not fetching a resource. You're participating in an ongoing reasoning process.

A2A Protocol vs API for Agents: When to Use What

This is the question I get most from engineering teams. And I'm going to give you the answer that saves you time.

The MCP vs A2A architecture breakdown from StackOne nails the security framing: A2A is designed for cross-organization trust boundaries. Use it when you don't control both ends.

Here's my decision framework:

Use A2A when:

  • The remote agent makes autonomous decisions
  • You need dynamic discovery (agent changes capabilities without you changing code)
  • The workflow involves long-running, stateful tasks
  • You want standard error handling for "agent says I need more info"

Use a plain API when:

  • The service is deterministic (lookup, CRUD, calculation)
  • You control both ends of the wire
  • The contract changes rarely
  • You need strict schema validation with low latency

Last month I killed a six-week project that was trying to wrap a payment service in A2A. Payment processing is a deterministic transaction. A2A added latency, complexity, and ambiguity. A simple POST endpoint was better in every dimension.

The a2a protocol for multi agent systems shines when agents negotiate. When they ask for clarification. When they reject tasks. That's the sweet spot.

Multi-Agent Orchestration with A2A

Here's where things get interesting.

We built a multi-agent system for legal contract review. Three specialized agents work in sequence: a jurisdiction agent, a risk agent, and a negotiation agent. Each is a separate A2A server. The orchestrator coordinates them.

python
class ContractOrchestrator:
    def __init__(self, registries):
        self.agents = self._discover_agents(registries)
    
    async def review_contract(self, contract_text):
        # Step 1: Jurisdiction analysis
        jurisdiction_task = await self._send_task(
            self.agents["jurisdiction"],
            {"text": contract_text}
        )
        jurisdiction = await self._wait_for_completion(jurisdiction_task)
        
        # Step 2: Risk assessment (needs jurisdiction context)
        risk_task = await self._send_task(
            self.agents["risk"],
            {
                "text": contract_text,
                "context": {"jurisdiction": jurisdiction["result"]["country"]}
            }
        )
        risk = await self._wait_for_completion(risk_task)
        
        # Step 3: Negotiation prep
        if risk["result"]["score"] > 0.7:
            negotiation_task = await self._send_task(
                self.agents["negotiation"],
                {"risk_report": risk, "fallback_positions": self.customer_rules}
            )
            return await self._wait_for_completion(negotiation_task)
        
        return {"status": "approved", "risk_score": risk["result"]["score"]}

The key insight: A2A lets each agent maintain its own context. The jurisdiction agent doesn't need to know about risk scoring. The risk agent doesn't need to know about negotiation tactics. They only share what the protocol requires — task inputs, status, and artifacts.

That's the a2a protocol for multi agent systems advantage. It preserves agent autonomy while enabling collaboration.

Compare that to the model context protocol approach, which Orca Security's piece on MCP, A2A, and Agent Context Protocols explains: MCP gives agents shared memory and tools. It's one agent with many resources. A2A is many agents with one conversation.

Security Concerns Nobody Tells You About

Let me be honest about the security pain.

A2A's Agent Card is public metadata. If you're building an agent that handles sensitive tasks, you need to think about what your Agent Card reveals. Our Q3 security review flagged this: we had an agent card at /.well-known/agent.json that listed skills like "refund-processing" and "account-data-export". An attacker can enumerate your agents and their capabilities without a single authenticated request.

The protocol spec has security extensions — OAuth 2.0, JWT, mTLS — but they're optional. The default is no security. That's terrifying.

My recommendation: never expose an Agent Card without authentication, even if you think the data within is harmless. The existence of an agent with an admin-bypass skill is itself a signal.

Also: watch for prompt injection through task payloads. We tested this in May. If your agent doesn't separate instructions from data in incoming A2A tasks, a malicious task message can override your system prompt. We spent two weeks hardening our task parsing to reject embedded instructions.

The Human-in-the-Loop Gap

One thing the A2A spec handles surprisingly well: input-required states.

When your agent needs a human decision (approve a transaction, answer "which vendor?"), the task enters input-required status. The protocol explicitly models this. But the implementation quality across agents varies.

Autodesk's construction Tender Agent (which they demoed in 2025) does this well — it sends structured options, not free-text requests. Google's Deep Research agent shows you a list of "this is what I plan to do" before executing. Those are the patterns. A2A just gives you the mechanism.

We added a clarification skill type to our agents in July. Instead of failing when the task is ambiguous, they send an input-required response with a structured question. Huge improvement in success rate — went from 73% to 91% first-pass completion.

A2A in Production: What Breaks

After running A2A in production for 8 months across 12 agents, here's what actually breaks:

1. Agent Cards go stale.

Agents change their skills. Nobody updates the card. Other agents cache it. You get 404s on skills that were listed. Solution: version your Agent Cards and implement a GET /agent-card endpoint with proper cache headers.

2. Task timeouts are not standardized.

The spec doesn't define default timeouts. We had one agent that waited 15 minutes for tasks others expected to complete in 5 seconds. We set timeout metadata on every task now.

3. Error codes are inconsistent.

Some agents return structured TaskNotFound errors. Others return generic JSON-RPC errors. The spec says "SHOULD" not "MUST". We wrote a normalization layer that maps codes across the agents we integrate with.

4. File transfer is inefficient.

If you're passing large files in Message parts as base64, you'll kill your bandwidth. The spec supports URLs, but not all implementations handle them. For files >10MB, we upload to S3, then pass a presigned URL. Half the agents we integrate with can't handle that. We use multipart upload and pray.

The Rust Performance Story (Since You Asked)

The Rust Performance Story (Since You Asked)

We tried implementing a high-throughput A2A agent in Rust. The performance was excellent — 50,000 concurrent tasks per instance with 12ms p95 latency. But the ecosystem is immature. We spent three weeks building infrastructure that Python libraries give you for free.

The Redis blog on MCP vs A2A mentions something relevant: latency budgets in agentic systems are dominated by LLM calls, not protocol overhead. Protocol choice rarely matters for throughput. It matters for correctness and developer experience.

Use Python or TypeScript for A2A agents unless you have a specific reason not to. We moved our production agents from Rust to Python in June. Deployment time dropped 40%. Latency stayed effectively the same.

Implementing A2A in Your Stack: A Working Example

Let me give you a complete, working Python A2A agent using the official SDK.

First, install the SDK:

bash
pip install a2a-sdk

Then create your agent:

python
# agent.py
from a2a import A2AHandler, Task, Message, Artifact
from a2a.types import TaskStatus, Skill
from fastapi import FastAPI
import uvicorn

class AnalyticsAgent(A2AHandler):
    def __init__(self):
        super().__init__(
            agent_card={
                "name": "SIvaro-Analytics-Agent",
                "description": "Runs predictive analytics on customer data",
                "url": "https://agents.sivaro.com/analytics",
                "skills": [
                    Skill(
                        id="churn-prediction",
                        name="Customer Churn Prediction",
                        description="Predict churn probability for a customer segment"
                    )
                ]
            }
        )
    
    async def on_task(self, task: Task) -> Task:
        # Parse the incoming message
        parts = task.message.parts
        text = next(p.text for p in parts if p.text)
        
        if task.metadata.get("skill_id") == "churn-prediction":
            # Actually call your ML model here
            import json
            result = {
                "churn_risk": 0.34,
                "at_risk_customers": ["cust-1001", "cust-1002"],
                "recommended_action": "send_retention_offer"
            }
            
            task.status = TaskStatus.COMPLETED
            task.artifacts = [
                Artifact(
                    name="churn_analysis",
                    mime_type="application/json",
                    data=json.dumps(result)
                )
            ]
            return task
        
        task.status = TaskStatus.FAILED
        task.error = {"code": "SKILL_NOT_FOUND", "message": f"Unknown skill: {task.metadata.get('skill_id')}"}
        return task

app = FastAPI()
handler = AnalyticsAgent()
handler.mount_to_app(app, prefix="/analytics")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

That's it. You now have a production-ready A2A agent. The SDK handles JSON-RPC parsing, response serialization, and task state management.

The client side:

python
# client.py
import asyncio
from a2a import A2AClient

async def main():
    client = A2AClient("http://localhost:8000")
    
    # Discover the agent
    card = await client.get_agent_card()
    print(f"Agent: {card.name}")
    print(f"Skills: {[s.id for s in card.skills]}")
    
    # Send a task
    task = await client.send_task(
        skill_id="churn-prediction",
        message="Predict churn for the Q3 cohort",
        metadata={"segment": "enterprise"}
    )
    
    # Wait for completion
    result = await client.wait_for_task(task.id, timeout=30)
    if result.status == "completed":
        artifact = result.artifacts[0]
        print(f"Result: {artifact.data}")
    else:
        print(f"Failed: {result.error}")

asyncio.run(main())

Run the agent, run the client, and you've built a multi-agent system. One hour of work, including reading this article.

Testing A2A: Start With Contract Tests

A2A's biggest benefit for engineering teams is testability. The protocol gives you a stable contract, so you can write contract tests that catch integration bugs before deployment.

We run Pact-style contract tests for each agent. The consumer (orchestrator) publishes expectations. The provider (agent under test) verifies it meets them. This gave us 94% first-pass integration success on our last multi-agent deployment. Before A2A, that number was below 60%.

python
# test_agent_contract.py
import pytest
from a2a.testing import A2AMockClient

def test_analytics_agent_contract():
    mock = A2AMockClient("http://localhost:8000")
    
    # Verify agent card is well-formed
    card = mock.get_agent_card()
    assert card.name == "SIvaro-Analytics-Agent"
    assert len(card.skills) >= 1
    
    # Send a valid task, expect completion
    task = mock.send_task("churn-prediction", "Test message")
    assert task.status in ("working", "completed")
    
    # Send an invalid task, expect a specific error
    bad_task = mock.send_task("nonexistent-skill", "Test")
    assert bad_task.status == "failed"
    assert "'SKILL_NOT_FOUND'" in str(bad_task.error)

Run these tests in CI. Every time an agent changes, the contract tests catch breaking changes for all consumers. This is where a2a protocol for multi agent systems adoption pays for itself.

Common Pitfalls (Learned The Hard Way)

Pitfall 1: Treating A2A like a remote procedure call.

The A2A protocol encourages stateful, long-running interactions. If you try to use it for request-response, you'll add Latency, complexity, and state-management overhead. Keep RPC for REST.

Pitfall 2: Not handling bidirectional messages.

A2A agents can send tasks back to the caller. That's powerful but dangerous if your orchestration loop isn't idempotent. We hit infinite loops in staging. We now cap nesting depth at 3 and use task IDs for deduplication.

Pitfall 3: Ignoring capability negotiation.

The Agent Card describes what an agent can do, but not how well it can do it. Two agents can claim the same skill with wildly different accuracy. Add your own capability metadata. We extended the Agent Card with a confidence field, and the orchestrator uses it to route tasks.

Pitfall 4: Assuming agents are stateless.

The A2A spec allows stateful agents, and in practice that's the norm. If your agent depends on history, you'll need to handle reconciliation. We added a context ID to every task payload so both sides can track state.

The 2026 Landscape: Where A2A Stands Now

As of August 2026, this is the state of play:

  • Google DeepMind's Project Mariner integrates A2A for browser-based task delegation
  • Elasticsearch's Agent Newsroom uses A2A to coordinate search agents and generative agents — they documented the practical implementation here
  • Salesforce's Agentforce agents support A2A, though they default to MCP for internal tools
  • Microsoft's Semantic Kernel added A2A connectors in their 2026 Q1 release

Adoption is real, but uneven. Financial services are lagging because A2A certification is still evolving. Healthcare is moving cautiously around HIPAA-bound Agent Cards. Logistics and e-commerce — the sectors where agents actually cross organizational boundaries — are the earliest adopters.

MCP remains dominant for tool access. The Auth0 guide to MCP vs A2A called it: MCP won "agent to tool" and A2A is winning "agent to agent". The two are complementary. You'll almost certainly need both.

When A2A Isn't the Right Answer

I'll be honest: A2A was overhyped in 2025. Every blog said "agents will replace APIs". They didn't.

A2A adds meaningful overhead. If the task can be done in one HTTP call and doesn't need agentic reasoning, a plain API is better. Let me repeat that because it's important: the a2a protocol vs api for agents debate ends when you realize they serve different purposes.

We operate a hybrid architecture. CRUD endpoints for data. REST for deterministic services. A2A for cross-agent negotiation and autonomous workflows. The line should always be clear, and your engineering team should know which side of the line they're on.

Your First 30 Days with A2A

Here's a concrete plan:

Week 1: Pick one non-critical workflow. Implement a single A2A agent. Run a client that sends a task. Get end-to-end working.

Week 2: Add security. Implement OAuth2 or mTLS. Ensure your Agent Card is not exposed publicly.

Week 3: Write the contract tests. Set up CI. Pressure-test failure modes — timeouts, malformed tasks, task rejection.

Week 4: Add the second agent. Wire the orchestrator. Measure success rates. Track failures. Fix.

Don't try to transform your entire architecture in a quarter. One workflow, one agent, one integration. Then expand.

The One Thing Most Teams Get Wrong

They treat A2A as purely technical. It's not.

A2A is a business negotiation protocol. When two agents talk, they're representing two organizations. The task payload contains the customer data. The agent card reveals what your service can do. The successful task result is a business outcome, not a code artifact.

Sit down with your product team. Define what skills your agents actually own. A vague "data-analysis" skill will cause integration chaos. A precise "revenue-forecast" skill with explicit input requirements will integrate cleanly. This is a product decision, not a protocol decision.

Building the Multi-Agent Future

We're building a world where agents talk to agents with the same reliability that servers talk to servers. A2A is a step in the right direction, but it's not the end.

The protocol still lacks native support for:

  • Multiparty orchestration (currently optimized for agent pairs)
  • Standardized payment/billing between agents
  • Reliability guarantees across organizations
  • A formal trust and reputation layer

That's where innovation needs to happen. The pieces after A2A might be agent marketplaces and protocol-level trust certificates.

If you're building agent infrastructure right now, start with A2A. Not because it's perfect, but because it never claims to be. It's the practical, deployable, 80% solution that lets you ship today — and change the protocol later without rewriting your entire system.

We chose A2A at SIVARO because it's the only protocol with the boring parts figured out: JSON-RPC over HTTP, long-running task states, error handling, partial artifacts, and capability discovery. The boring parts are what survive contact with production.

That, more than anything, is why I wrote this a2a agent communication protocol tutorial. The technology works. You're one afternoon away from a working multi-agent system.


FAQ

FAQ

Q: What is the difference between A2A and MCP?

A: A2A connects agents to agents. MCP connects agents to tools and data. The TrueFoundry comparison of MCP vs A2A makes this clear: MCP is about input/data, A2A is about task execution and delegation.

Q: Do I need A2A if I already have REST APIs?

A: No, for deterministic services. Yes, for autonomous agents that negotiate, ask for clarification, and make decisions. Use this guide to pick the right tool for each integration.

Q: How secure is A2A?

A: Optional security features mean it's as secure as you make it. You must implement OAuth2, mTLS, or similar. The StackOne analysis has a good security comparison between MCP and A2A.

Q: Can A2A agents handle long-running tasks?

A: Yes, A2A has working and input-required states, plus streaming partials. The protocol is designed for asynchronous work.

Q: What's the best language for implementing A2A?

A: Python or TypeScript. The official SDKs are solid. We ran production A2A in Rust, but the maturity gap made it not worth it for most use cases.

Q: How does A2A handle agent versioning?

A: The Agent Card includes a version field, and task metadata can carry version context. But graceful degradation across versions is still in development.

Q: Is A2A production-ready in 2026?

A: Yes, for straightforward agent-to-agent workflows. For complex orchestration across many agents, expect to build supporting infrastructure like we did.


Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Part of our MCP (Model Context Protocol) series — see every guide in this cluster. Fighting this in production? Explore AI Product Development.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with AI systems?

Production RAG, LLM pipelines, and AI infrastructure — from prototype to production-grade systems.

Explore AI Product Development