Skip to content

Feature: A2A (Agent-to-Agent) Protocol Support — Remote Agent Discovery, Communication & Interoperability #514

@teknium1

Description

@teknium1

Overview

Google's A2A (Agent-to-Agent) protocol is an open standard (Apache 2.0, under the Linux Foundation) for inter-agent communication — complementary to MCP. While MCP answers "what tools can I use?", A2A answers "who can help me?" It enables agents built on different frameworks to discover each other, negotiate capabilities, and collaborate on tasks over standard HTTP.

This was inspired by an article on building a cybersecurity agent swarm that used A2A + MCP together: MCP as an agent registry, A2A for actual inter-agent task delegation. The architecture demonstrates the complementary power of both protocols — something Hermes is uniquely positioned to support since we already have a production-grade MCP client (tools/mcp_tool.py).

A2A would give Hermes the ability to participate in multi-agent networks — calling remote agents (any framework, any language) and exposing itself as an A2A-discoverable agent for other systems to call.


Research Findings

How A2A Works

A2A uses a three-layer architecture: Protobuf data model → Abstract operations → Protocol bindings (JSON-RPC 2.0 over HTTP, with optional gRPC and SSE streaming).

Core concepts:

  1. Agent Cards — JSON documents served at /.well-known/agent.json. The "business card" of an agent: name, description, URL, skills, capabilities (streaming, push notifications), authentication schemes. This is how agents discover each other.

  2. Tasks — The unit of work. Lifecycle: SUBMITTED → WORKING → {INPUT_REQUIRED, AUTH_REQUIRED} → {COMPLETED, FAILED, CANCELED, REJECTED}. Tasks support multi-turn interactions (agent asks for clarification), streaming results, and long-running async patterns.

  3. Messages & Parts — Communication is via Messages (role: user/agent) containing Parts (text, raw bytes, URLs, or structured JSON data). Results are delivered as Artifacts (also composed of Parts).

  4. JSON-RPC 2.0 — All communication uses standard JSON-RPC over HTTP POST. Methods include message/send, message/stream (SSE), tasks/get, tasks/cancel.

Python SDK (pip install "a2a-sdk[http-server]", Python 3.10+):

Server side (exposing an agent):

from a2a.server.agent_execution import AgentExecutor
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.apps import A2AStarletteApplication
from a2a.server.tasks import InMemoryTaskStore

class MyExecutor(AgentExecutor):
    async def execute(self, context, event_queue):
        updater = TaskUpdater(event_queue, context.task_id, context.context_id)
        updater.start_work()
        result = "Hello from my agent"
        updater.update_status(TaskState.completed,
            message=updater.new_agent_message(parts=[TextPart(text=result)]))

handler = DefaultRequestHandler(agent_executor=MyExecutor(), task_store=InMemoryTaskStore())
app = A2AStarletteApplication(agent_card=card, http_handler=handler)
uvicorn.run(app.build(), host='0.0.0.0', port=9999)

Client side (calling other agents):

from a2a.client import A2AClient

async with httpx.AsyncClient() as hc:
    client = await A2AClient.get_client_from_agent_card_url(hc, 'http://remote:9999')
    response = await client.send_message(request)
    # or streaming:
    async for event in client.send_message_streaming(request):
        print(event)

Key Design Decisions

  • Framework-agnostic: Any A2A agent can talk to any other, regardless of language or framework. This is the key differentiator from local delegation.
  • Opaque execution: Unlike MCP tools (schema-defined inputs/outputs), A2A agents are black boxes — you describe what you need in natural language, and they decide how to accomplish it.
  • Built-in multi-turn: The INPUT_REQUIRED state enables agents to ask clarifying questions mid-task, unlike simple request-response patterns.
  • IBM ACP merger (Aug 2025): IBM's competing Agent Communication Protocol was merged into A2A, making it the de facto standard.

A2A vs MCP — Complementary, Not Competing

Aspect MCP A2A
Purpose Agent-to-Tool Agent-to-Agent
Discovery Tool schemas Agent Cards
Interaction Deterministic function calls Natural language tasks
Statefulness Stateless per call Stateful task lifecycle
Framework coupling Client must understand schema Framework-agnostic

Current State in Hermes Agent

What we have:

  • MCP client (tools/mcp_tool.py, 1047 lines) — Full MCP client with stdio + HTTP transports, auto-discovery, auto-reconnect. Production-ready.
  • Local delegation (tools/delegate_tool.py, 560 lines) — Spawns child AIAgent instances in-process. Up to 3 concurrent, max depth 2. Local only.
  • Cross-CLI delegation skills (claude-code, codex, hermes-agent) — Run external agent CLIs via terminal.

What's missing:

  • No A2A protocol support — zero references in codebase
  • No remote agent communication over network
  • No agent discovery mechanism for external agents
  • Local delegation is in-process only — can't span machines or frameworks
  • No way for external systems to call Hermes as an agent

Related issues:


Implementation Plan

Skill vs. Tool Classification

This should be a tool because:

  1. It requires end-to-end HTTP client integration with agent card resolution, JSON-RPC protocol handling, and task state management
  2. It needs custom processing logic: SSE streaming, multi-turn task lifecycle, auth negotiation — not expressible as "instructions + shell commands"
  3. It handles streaming data (SSE events) that can't go through the terminal
  4. It parallels the MCP tool architecture (persistent connections, background event loop, auto-discovery)

What We'd Need

  • New file: tools/a2a_tool.py — A2A client tools
  • New dependency: a2a-sdk[http-server] (pulls in httpx, starlette, uvicorn, protobuf)
  • Config section: a2a_agents in ~/.hermes/config.yaml
  • Optional: a2a_server.py for exposing Hermes as A2A agent
  • Test file: tests/tools/test_a2a_tool.py

Phased Rollout

Phase 1: A2A Client — Call Remote Agents

  • Implement a2a_discover tool: fetch and display an Agent Card from any URL
  • Implement a2a_call tool: send a message to a remote A2A agent, get the result
  • Support sync mode (poll for completion) and streaming mode (SSE)
  • Agent URL resolution from config (a2a_agents in config.yaml) or direct URL
  • Agent card caching (avoid re-fetching every call)
  • Register in a2a toolset, add to _HERMES_CORE_TOOLS
  • Architecture: mirror mcp_tool.py patterns (background asyncio loop, thread-safe, credential handling)

Phase 2: A2A Server — Expose Hermes as an Agent

Phase 3: Multi-Agent Orchestration Patterns


Pros & Cons

Pros

  • Framework interoperability: Hermes could collaborate with agents built in LangChain, CrewAI, Google ADK, AutoGen, or any A2A-compliant framework — not just other Hermes instances
  • Network-spanning delegation: Unlike local delegate_tool, A2A works across machines, clouds, and organizations
  • Industry standard: A2A is the dominant agent protocol (Linux Foundation, IBM merger, 100+ adopters). Supporting it positions Hermes well
  • Complementary to MCP: We already have MCP client; adding A2A completes the picture (tools + agents)
  • Minimal dependency: a2a-sdk is lightweight (httpx, protobuf). Server mode adds starlette/uvicorn but those are optional

Cons / Risks

  • Pre-1.0 SDK: v0.3.24 as of March 2026. Breaking changes possible before GA (expected mid-2026). Mitigation: pin version, track spec changes
  • Complexity: Another protocol to maintain alongside MCP. Mitigation: modular architecture, shared patterns with mcp_tool.py
  • Security surface: Network-exposed agent endpoints. Mitigation: auth required by default, HTTPS enforcement, input sanitization
  • Unsigned Agent Cards: Cards can be spoofed (JWS signing is optional, not enforced). Mitigation: only connect to configured/trusted agents
  • Overlap with delegate_tool: Users may be confused about when to use local delegation vs A2A. Mitigation: clear documentation, skill-based routing

Open Questions


References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions