{"slug": "agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other", "title": "Agent Series (11): A2A Protocol — How Agents Collaborate with Each Other", "summary": "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.", "body_md": "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.\n\nBut some scenarios require delegating to another Agent with autonomous decision-making, not just a tool:\n\nWhen these three Agents need to collaborate, how do they communicate? Who knows who exists? How do they hand off work?\n\nThis is the problem **A2A (Agent-to-Agent) Protocol** solves.\n\n```\nMCP:  Agent  ←→  Tools/Data (vertical integration, Agent invokes tools)\nA2A:  Agent  ←→  Agent      (horizontal collaboration, Agent delegates to Agent)\n```\n\nEach Agent publishes an `AgentCard`\n\ndescribing its capabilities:\n\n``` python\nfrom a2a.types import AgentCard, AgentSkill\n\nresearch_card = AgentCard()\nresearch_card.name = \"research-agent\"\nresearch_card.description = \"Gathers factual background on technical topics\"\n\nskill = AgentSkill()\nskill.id = \"research\"\nskill.name = \"Research\"\nskill.description = \"Collect key facts on a topic\"\nskill.tags.extend([\"research\", \"facts\"])\nresearch_card.skills.append(skill)\n```\n\nKey AgentCard fields: `name`\n\n, `description`\n\n, `skills`\n\n(each skill has `tags`\n\nfor discovery).\n\nThink of it as the Agent equivalent of OpenAPI Spec — a machine-readable capability declaration that humans can also read.\n\nAgents don't pass function calls to each other — they pass `Task`\n\nobjects:\n\n``` python\nfrom a2a.types import Task, TaskState, TaskStatus, Message, Part, Role\n\ntask = Task()\ntask.id = str(uuid.uuid4())\ntask.status.state = TaskState.TASK_STATE_SUBMITTED\n\n# Input: a User-role Message\nmsg = Message()\nmsg.role = Role.ROLE_USER\npart = Part(); part.text = \"Should I use Python or Go?\"\nmsg.parts.append(part)\ntask.history.append(msg)\n```\n\n`Task`\n\nhas a lifecycle: `SUBMITTED → WORKING → COMPLETED / FAILED`\n\n. On completion, the Agent appends the result as a `ROLE_AGENT`\n\nMessage.\n\n`AgentRegistry`\n\nstores all registered AgentCards and supports discovery by tag:\n\n``` python\nclass AgentRegistry:\n    def register(self, card: AgentCard, handler: Callable[[Task], Task]) -> None:\n        self._agents[card.name] = AgentEntry(card=card, handler=handler)\n\n    def discover(self, tag: str) -> list[AgentCard]:\n        \"\"\"Return all agents whose skills include the given tag.\"\"\"\n        ...\n\n    def delegate(self, agent_name: str, input_text: str) -> Task:\n        \"\"\"Create a Task and execute it via the registered handler.\"\"\"\n        ...\n```\n\nWithout a protocol, the orchestrator calls three Python functions directly:\n\n``` php\ndef direct_orchestrator(question: str) -> str:\n    research = research_agent_fn(question)    # hard dependency\n    analysis = analysis_agent_fn(research)   # hard dependency\n    answer   = writing_agent_fn(analysis)    # hard dependency\n    return answer\n```\n\nReal execution output:\n\n```\n→ calling research_agent (direct)\n→ calling analysis_agent (direct)\n→ calling writing_agent (direct)\n\nAnswer: Choose Python if you need rapid development with broad libraries.\n        Select Go if performance and concurrency are critical...\n```\n\nWorks 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.\n\nRegister three Agents, each with distinct skill tags:\n\n```\n[registry] registered: research-agent\n[registry] registered: analysis-agent\n[registry] registered: writing-agent\n```\n\nDiscovery test:\n\n```\nresearchers = registry.discover(\"research\")\n# → Found: research-agent — Gathers factual background on technical topics\n\nwriters = registry.discover(\"writing\")\n# → Found: writing-agent — Composes clear technical prose from analysis output\n```\n\nThe orchestrator never writes an agent name — it discovers by tag:\n\n``` php\ndef a2a_orchestrator(question: str) -> str:\n    researchers = registry.discover(\"research\")\n    t1 = registry.delegate(researchers[0].name, question)\n\n    analysts = registry.discover(\"analysis\")\n    t2 = registry.delegate(analysts[0].name, task_output(t1))\n\n    writers = registry.discover(\"writing\")\n    t3 = registry.delegate(writers[0].name, task_output(t2))\n    return task_output(t3)\n```\n\nReal execution output:\n\n```\n→ delegating to research-agent (discovered via tag)\n→ delegating to analysis-agent (discovered via tag)\n→ delegating to writing-agent (discovered via tag)\n\nAnswer: Choose Python for rapid development; Go for high-throughput performance...\n```\n\n**The critical difference: the orchestrator code contains no agent names.** Register a `writing-agent-v2`\n\nwith the same `writing`\n\ntag, and the orchestrator discovers and uses it immediately — zero code changes.\n\nA2A's most powerful use case: **the LLM reads the AgentCard catalog and decides which Agents to call and in what order**.\n\nShow the LLM the agent catalog:\n\n```\nAvailable agents:\n  research-agent: Gathers factual background [skills: Research(research, facts)]\n  analysis-agent: Analyzes research notes    [skills: Analysis(analysis, tradeoffs)]\n  writing-agent:  Composes technical prose   [skills: Writing(writing, prose)]\n```\n\nLLM outputs an execution plan:\n\n```\n[\"research-agent\", \"analysis-agent\", \"writing-agent\"]\n```\n\nExecute the plan:\n\n```\nExecuting 3 agents:\n  → delegating to research-agent\n  → delegating to analysis-agent\n  → delegating to writing-agent\n\nFinal answer: Choose Python for rapid development; Go for high-throughput...\n```\n\nThis 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**.\n\n```\nDimension         MCP                          A2A\n──────────────────────────────────────────────────────────────────────\nProblem solved    Agent ↔ Tool/Data            Agent ↔ Agent\nDiscovery         list_tools() (tool catalog)  discover() (Agent registry)\nUnit of work      Tool call (sync)             Task (async-ready)\nCoupling          Agent invokes tool directly  Orchestrator delegates task\nOther end         Passive tool service         Autonomous Agent with logic\nCross-service     Tool is an independent proc  Agent is an independent service\nWho decides       Agent (tool use)             Orchestrator (delegation)\n```\n\nComplete four-way selection guide:\n\n```\nScenario                                  Recommended\n──────────────────────────────────────────────────────────────────────\nSame codebase, call target known          Direct function call\nAgent needs external tools                MCP (tools as service)\nAgent delegates to specialist Agents      A2A (agents as service)\nCross-org large-scale Agent network       ANP (decentralized discovery)\n```\n\n**AgentCard Design**\n\n`description`\n\n: one sentence about what the Agent is good at — the LLM reads it for routing decisions`skill.tags`\n\n: use semantically clear tags (`research`\n\n, `analysis`\n\n, `writing`\n\n), not version numbers or internal IDs`AgentCard`\n\nshould be machine-readable and human-readable (think OpenAPI style)**Task Design**\n\n`Task.id`\n\n: use UUID for tracking and idempotent retry`history`\n\n(Message chain) for context passing — don't concatenate everything into `part.text`\n\n`ROLE_USER`\n\n(input) from `ROLE_AGENT`\n\n(output) messages**Registry and Discovery**\n\n**LLM-Driven Routing**\n\nFive core takeaways:\n\nUp next: **Agent Evaluation Framework** — how to systematically test Agents, which metrics matter, and how to use DeepEval.\n\n*Find more useful knowledge and interesting products on my Homepage*", "url": "https://wpnews.pro/news/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other", "canonical_source": "https://dev.to/wonderlab/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other-59o1", "published_at": "2026-06-03 05:35:47+00:00", "updated_at": "2026-06-03 05:41:55.499407+00:00", "lang": "en", "topics": ["ai-agents", "artificial-intelligence", "ai-infrastructure", "ai-research", "ai-tools"], "entities": ["A2A Protocol", "AgentCard", "MCP", "OpenAPI Spec"], "alternates": {"html": "https://wpnews.pro/news/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other", "markdown": "https://wpnews.pro/news/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other.md", "text": "https://wpnews.pro/news/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other.txt", "jsonld": "https://wpnews.pro/news/agent-series-11-a2a-protocol-how-agents-collaborate-with-each-other.jsonld"}}