cd /news/ai-agents/agent-series-11-a2a-protocol-how-age… · home topics ai-agents article
[ARTICLE · art-19999] src=dev.to pub= topic=ai-agents verified=true sentiment=· neutral

Agent Series (11): A2A Protocol — How Agents Collaborate with Each Other

Google researchers have developed the A2A (Agent-to-Agent) Protocol, a standard for enabling autonomous AI agents to discover and delegate tasks to one another. The protocol allows agents to publish machine-readable "AgentCards" describing their capabilities, enabling an orchestrator to discover agents by skill tags rather than hardcoding function calls. This replaces rigid, direct-function orchestration with a flexible system where agents pass `Task` objects through a defined lifecycle of submitted, working, and completed states.

read5 min publishedJun 3, 2026

The previous article covered MCP: an Agent connects to tool services via a standard protocol. Tools are passive — they wait to be called, execute, return a result.

But some scenarios require delegating to another Agent with autonomous decision-making, not just a tool:

When these three Agents need to collaborate, how do they communicate? Who knows who exists? How do they hand off work?

This is the problem A2A (Agent-to-Agent) Protocol solves.

MCP:  Agent  ←→  Tools/Data (vertical integration, Agent invokes tools)
A2A:  Agent  ←→  Agent      (horizontal collaboration, Agent delegates to Agent)

Each Agent publishes an AgentCard

describing its capabilities:

from a2a.types import AgentCard, AgentSkill

research_card = AgentCard()
research_card.name = "research-agent"
research_card.description = "Gathers factual background on technical topics"

skill = AgentSkill()
skill.id = "research"
skill.name = "Research"
skill.description = "Collect key facts on a topic"
skill.tags.extend(["research", "facts"])
research_card.skills.append(skill)

Key AgentCard fields: name

, description

, skills

(each skill has tags

for discovery).

Think of it as the Agent equivalent of OpenAPI Spec — a machine-readable capability declaration that humans can also read.

Agents don't pass function calls to each other — they pass Task

objects:

from a2a.types import Task, TaskState, TaskStatus, Message, Part, Role

task = Task()
task.id = str(uuid.uuid4())
task.status.state = TaskState.TASK_STATE_SUBMITTED

msg = Message()
msg.role = Role.ROLE_USER
part = Part(); part.text = "Should I use Python or Go?"
msg.parts.append(part)
task.history.append(msg)

Task

has a lifecycle: SUBMITTED → WORKING → COMPLETED / FAILED

. On completion, the Agent appends the result as a ROLE_AGENT

Message.

AgentRegistry

stores all registered AgentCards and supports discovery by tag:

class AgentRegistry:
    def register(self, card: AgentCard, handler: Callable[[Task], Task]) -> None:
        self._agents[card.name] = AgentEntry(card=card, handler=handler)

    def discover(self, tag: str) -> list[AgentCard]:
        """Return all agents whose skills include the given tag."""
        ...

    def delegate(self, agent_name: str, input_text: str) -> Task:
        """Create a Task and execute it via the registered handler."""
        ...

Without a protocol, the orchestrator calls three Python functions directly:

def direct_orchestrator(question: str) -> str:
    research = research_agent_fn(question)    # hard dependency
    analysis = analysis_agent_fn(research)   # hard dependency
    answer   = writing_agent_fn(analysis)    # hard dependency
    return answer

Real execution output:

→ calling research_agent (direct)
→ calling analysis_agent (direct)
→ calling writing_agent (direct)

Answer: Choose Python if you need rapid development with broad libraries.
        Select Go if performance and concurrency are critical...

Works perfectly. The problem is structural: the orchestrator has three hardcoded function references. Replace any one Agent, and you must edit the orchestrator. If the orchestrator lives in a different service, that means a cross-service code change.

Register three Agents, each with distinct skill tags:

[registry] registered: research-agent
[registry] registered: analysis-agent
[registry] registered: writing-agent

Discovery test:

researchers = registry.discover("research")

writers = registry.discover("writing")

The orchestrator never writes an agent name — it discovers by tag:

def a2a_orchestrator(question: str) -> str:
    researchers = registry.discover("research")
    t1 = registry.delegate(researchers[0].name, question)

    analysts = registry.discover("analysis")
    t2 = registry.delegate(analysts[0].name, task_output(t1))

    writers = registry.discover("writing")
    t3 = registry.delegate(writers[0].name, task_output(t2))
    return task_output(t3)

Real execution output:

→ delegating to research-agent (discovered via tag)
→ delegating to analysis-agent (discovered via tag)
→ delegating to writing-agent (discovered via tag)

Answer: Choose Python for rapid development; Go for high-throughput performance...

The critical difference: the orchestrator code contains no agent names. Register a writing-agent-v2

with the same writing

tag, and the orchestrator discovers and uses it immediately — zero code changes.

A2A's most powerful use case: the LLM reads the AgentCard catalog and decides which Agents to call and in what order.

Show the LLM the agent catalog:

Available agents:
  research-agent: Gathers factual background [skills: Research(research, facts)]
  analysis-agent: Analyzes research notes    [skills: Analysis(analysis, tradeoffs)]
  writing-agent:  Composes technical prose   [skills: Writing(writing, prose)]

LLM outputs an execution plan:

["research-agent", "analysis-agent", "writing-agent"]

Execute the plan:

Executing 3 agents:
  → delegating to research-agent
  → delegating to analysis-agent
  → delegating to writing-agent

Final answer: Choose Python for rapid development; Go for high-throughput...

This is A2A's end state: no pre-configured orchestrator pipeline. The LLM reads the task requirements and the AgentCard descriptions, then plans the collaboration chain at runtime.

Dimension         MCP                          A2A
──────────────────────────────────────────────────────────────────────
Problem solved    Agent ↔ Tool/Data            Agent ↔ Agent
Discovery         list_tools() (tool catalog)  discover() (Agent registry)
Unit of work      Tool call (sync)             Task (async-ready)
Coupling          Agent invokes tool directly  Orchestrator delegates task
Other end         Passive tool service         Autonomous Agent with logic
Cross-service     Tool is an independent proc  Agent is an independent service
Who decides       Agent (tool use)             Orchestrator (delegation)

Complete four-way selection guide:

Scenario                                  Recommended
──────────────────────────────────────────────────────────────────────
Same codebase, call target known          Direct function call
Agent needs external tools                MCP (tools as service)
Agent delegates to specialist Agents      A2A (agents as service)
Cross-org large-scale Agent network       ANP (decentralized discovery)

AgentCard Design

description

: one sentence about what the Agent is good at — the LLM reads it for routing decisionsskill.tags

: use semantically clear tags (research

, analysis

, writing

), not version numbers or internal IDsAgentCard

should be machine-readable and human-readable (think OpenAPI style)Task Design

Task.id

: use UUID for tracking and idempotent retryhistory

(Message chain) for context passing — don't concatenate everything into part.text

ROLE_USER

(input) from ROLE_AGENT

(output) messagesRegistry and Discovery

LLM-Driven Routing

Five core takeaways:

Up next: Agent Evaluation Framework — how to systematically test Agents, which metrics matter, and how to use DeepEval.

Find more useful knowledge and interesting products on my Homepage

── more in #ai-agents 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/agent-series-11-a2a-…] indexed:0 read:5min 2026-06-03 ·