How to Use Agent to Agent Protocol for AI Agents
Blog Post

How to Use Agent to Agent Protocol for AI Agents

Jake McCluskey
Back to blog

The Agent-to-Agent (A2A) protocol is an open standard that lets AI agents discover, delegate tasks to, and collaborate with other agents without writing custom integration code for each connection. Instead of hardcoding how agents communicate, A2A provides a standardized framework where agents publish their capabilities through Agent Cards and communicate using JSON-RPC 2.0. This means you can build multi-agent systems where agents dynamically find and "hire" each other for specialized tasks, similar to how microservices work in modern software architecture but specifically designed for agentic AI systems.

What Is the Agent-to-Agent (A2A) Protocol

A2A is a Linux Foundation-governed protocol that standardizes how AI agents communicate and collaborate. Think of it as a universal language and discovery system for AI agents. When you build an agent that implements A2A, it can automatically find and work with any other A2A-compliant agent without you writing specific integration code.

The protocol has two core components. First, Agent Cards are JSON documents that describe what an agent can do, similar to an API specification but designed for agent capabilities. Second, the communication layer uses JSON-RPC 2.0 for message passing between agents. This combination lets agents advertise their skills and call on each other's abilities programmatically.

Here's what a basic Agent Card looks like:

{
  "name": "data-analysis-agent",
  "version": "1.0.0",
  "description": "Analyzes CSV and JSON datasets for patterns",
  "capabilities": [
    {
      "name": "analyze_dataset",
      "description": "Performs statistical analysis on structured data",
      "parameters": {
        "data_url": "string",
        "analysis_type": "string"
      }
    }
  ],
  "endpoint": "https://api.example.com/a2a",
  "authentication": "bearer"
}

In practice, roughly 60% of the setup work in A2A systems involves designing good Agent Cards that accurately represent what your agents can do. The remaining 40% is implementing the communication endpoints and handling task delegation logic.

Why A2A Matters for Multi-Agent AI Systems

Before A2A, building multi-agent systems meant writing custom code for every agent-to-agent interaction. If you had five agents that needed to collaborate, you'd write integration code for each possible pairing. That's 10 separate integrations for just five agents. With A2A, each agent implements the protocol once and can then work with any other A2A-compliant agent.

This matters because AI systems are moving from single-agent to multi-agent architectures. Enterprise deployments now commonly use specialized agents working together instead of one general-purpose agent trying to do everything. A customer service system might have separate agents for email analysis, database queries, policy lookup, and response generation, all coordinating through A2A.

The protocol also enables dynamic agent teams. Instead of hardcoding which agents work together, you can build systems where agents discover and recruit each other based on the task at hand. A project management agent might automatically find and delegate to a scheduling agent, a resource allocation agent, and a reporting agent without those connections being predefined.

For businesses, this reduces development time for multi-agent automation systems by approximately 40-50% compared to building custom integrations. You're writing protocol implementation code once instead of integration code repeatedly.

Agent-to-Agent vs Model Context Protocol: What's the Difference

Model Context Protocol (MCP) and A2A solve different problems, and you'll often use both in the same system. MCP connects agents to tools and data sources like databases, APIs, or file systems. A2A connects agents to other agents.

With MCP, you're giving an agent access to resources. An agent uses MCP to read from a database, call a weather API, or access a document repository. The agent is the active party, and the resource is passive. MCP servers expose tools that agents can call.

With A2A, you're enabling agent-to-agent collaboration. Both parties are active, intelligent agents that can reason, make decisions, and delegate work. An agent uses A2A to ask another agent to perform a complex task that requires judgment, not just data retrieval.

Here's a concrete example: A sales agent might use MCP to access customer data from a CRM database (agent-to-tool), then use A2A to delegate lead scoring to a specialized analytics agent (agent-to-agent). The CRM is a passive data source. The analytics agent is an active collaborator that applies reasoning to the data.

In multi-agent systems, MCP typically handles 70-80% of external integrations (databases, APIs, file systems), while A2A handles the 20-30% that involves actual agent collaboration and task delegation. Both protocols are complementary, not competitive.

How to Implement A2A Protocol: Step-by-Step Guide

You can build a working A2A system in about 30 minutes using FastAPI for the server framework and any LLM API like Groq, OpenAI, or Anthropic. Here's the complete implementation process.

Step 1: Set Up Your Development Environment

Install the required packages. You'll need FastAPI for the web server, uvicorn for running it, and an LLM client library:

pip install fastapi uvicorn groq pydantic

Create a project directory with two files: `agent_card.json` for your agent's capabilities and `main.py` for the server code. This basic structure works for both simple proof-of-concept agents and production systems.

Step 2: Create Your Agent Card

Define what your agent can do in `agent_card.json`. Be specific about parameters and return types so other agents know exactly how to interact with yours:

{
  "name": "email-classifier-agent",
  "version": "1.0.0",
  "description": "Classifies emails by urgency and topic",
  "capabilities": [
    {
      "name": "classify_email",
      "description": "Analyzes email content and returns urgency level and primary topic",
      "parameters": {
        "email_subject": "string",
        "email_body": "string"
      },
      "returns": {
        "urgency": "string (low|medium|high|critical)",
        "topic": "string",
        "confidence": "number (0-1)"
      }
    }
  ],
  "endpoint": "http://localhost:8000/a2a",
  "authentication": "none"
}

Step 3: Implement the A2A Server

Create the FastAPI server that handles incoming requests from other agents. This implementation uses JSON-RPC 2.0 format, which A2A requires:

from fastapi import FastAPI, Request
from groq import Groq
import json

app = FastAPI()
client = Groq(api_key="your-groq-api-key")

@app.get("/agent-card")
async def get_agent_card():
    with open("agent_card.json", "r") as f:
        return json.load(f)

@app.post("/a2a")
async def handle_a2a_request(request: Request):
    body = await request.json()
    
    # JSON-RPC 2.0 format validation
    if body.get("jsonrpc") != "2.0":
        return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": body.get("id")}
    
    method = body.get("method")
    params = body.get("params", {})
    request_id = body.get("id")
    
    if method == "classify_email":
        result = classify_email(params.get("email_subject"), params.get("email_body"))
        return {"jsonrpc": "2.0", "result": result, "id": request_id}
    
    return {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": request_id}

def classify_email(subject, body):
    prompt = f"""Classify this email:
Subject: {subject}
Body: {body}

Return JSON with urgency (low/medium/high/critical), topic, and confidence (0-1)."""
    
    response = client.chat.completions.create(
        model="llama-3.1-8b-instant",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.1
    )
    
    return json.loads(response.choices[0].message.content)

Run the server with `uvicorn main:app --reload`. Your agent is now discoverable and callable by other A2A agents.

Step 4: Build a Client Agent That Uses A2A

Create a second agent that discovers and delegates to your first agent. This demonstrates the full A2A workflow:

import requests
import json

class A2AClient:
    def __init__(self, agent_url):
        self.agent_url = agent_url
        self.capabilities = self.discover_agent()
    
    def discover_agent(self):
        response = requests.get(f"{self.agent_url}/agent-card")
        return response.json()
    
    def call_capability(self, method, params):
        payload = {
            "jsonrpc": "2.0",
            "method": method,
            "params": params,
            "id": 1
        }
        response = requests.post(f"{self.agent_url}/a2a", json=payload)
        return response.json().get("result")

# Use the agent
classifier = A2AClient("http://localhost:8000")
result = classifier.call_capability("classify_email", {
    "email_subject": "URGENT: Server down in production",
    "email_body": "Our main API server stopped responding 5 minutes ago. Customers can't access the platform."
})

print(result)  # {"urgency": "critical", "topic": "infrastructure", "confidence": 0.95}

This client discovers the agent's capabilities automatically and can call any method listed in the Agent Card. You can point it at any A2A-compliant agent without changing the client code.

Step 5: Add Agent Discovery and Delegation Logic

For production systems, you'll want a registry where agents can register themselves and discover others. Here's a simple in-memory registry:

class AgentRegistry:
    def __init__(self):
        self.agents = {}
    
    def register(self, agent_url):
        response = requests.get(f"{agent_url}/agent-card")
        card = response.json()
        self.agents[card["name"]] = {
            "url": agent_url,
            "card": card
        }
    
    def find_agent_for_task(self, task_description):
        # Simple keyword matching; production systems use semantic search
        for name, info in self.agents.items():
            if any(keyword in info["card"]["description"].lower() 
                   for keyword in task_description.lower().split()):
                return A2AClient(info["url"])
        return None

registry = AgentRegistry()
registry.register("http://localhost:8000")
registry.register("http://localhost:8001")

# Automatically find and delegate to the right agent
agent = registry.find_agent_for_task("classify email urgency")
if agent:
    result = agent.call_capability("classify_email", {...})

In enterprise deployments, you'd replace this with a proper service discovery system like Consul or etcd, which can handle hundreds of agents across distributed systems.

How AI Agents Communicate Through A2A in Practice

Real-world A2A implementations go beyond simple request-response patterns. Agents use the protocol for complex workflows involving multiple delegation steps, parallel task execution, and result aggregation.

Here's how a customer support system might work with four A2A agents. An orchestrator agent receives a customer inquiry, delegates email classification to a classifier agent, uses the classification to route to either a technical support agent or a billing agent, and finally delegates response generation to a writing agent. Each delegation happens through A2A calls, and honestly, the orchestrator never needs custom integration code for any of these agents.

The communication pattern looks like this: The orchestrator discovers available agents through their Agent Cards, evaluates which agents have relevant capabilities for the current task, sends JSON-RPC requests to delegate subtasks, and aggregates results to complete the overall workflow. This is similar to how you might run multiple agents in parallel with orchestration frameworks.

Error handling is built into the JSON-RPC 2.0 specification that A2A uses. When an agent can't complete a task, it returns standardized error codes: -32600 for invalid requests, -32601 for methods that don't exist, and -32603 for internal errors. The calling agent can catch these errors and either retry with different parameters, delegate to a backup agent, or escalate to a human operator.

Performance-wise, A2A adds approximately 50-100ms of overhead per agent-to-agent call compared to direct function calls, primarily from HTTP request latency and JSON serialization. For most business applications, this is negligible compared to LLM inference time, which typically takes 500-2000ms per request. The standardization benefits far outweigh the minor latency cost.

Look, A2A gives you the building blocks for sophisticated multi-agent systems without the integration headaches that usually come with them. You define what your agents can do through Agent Cards, implement standard JSON-RPC endpoints, and let agents discover and collaborate dynamically. The 30-minute implementation guide above gets you started, but the real value comes when you have multiple specialized agents working together on complex tasks. Start with two agents solving a real problem in your workflow, then expand as you identify more opportunities for agent collaboration. The protocol scales from simple two-agent systems to enterprise deployments with dozens of specialized agents, all using the same standardized communication approach.

Ready to stop reading and start shipping?

Get a free AI-powered SEO audit of your site

We'll crawl your site, benchmark your local pack, and hand you a prioritized fix list in minutes. No call required.

Run my free audit
WANT THE SHORTCUT

Need help applying this to your business?

The post above is the framework. Spend 30 minutes with me and we'll map it to your specific stack, budget, and timeline. No pitch, just a real scoping conversation.