Integrating A2A with Existing Agent Frameworks: A Hermes Agent Demonstration
Why A2A matters for agent orchestration
The Agent2Agent Protocol (A2A) is an open standard introduced by Google in April 2025 for inter-agent communication. It defines how an AI agent discovers another agent's capabilities, sends it a task, receives streaming output, and tracks the task through its lifecycle — all over JSON-RPC 2.0, without assuming both agents share a framework, a model provider, or a deployment topology.
A2A fills a gap that MCP does not cover. MCP connects an agent to tools and data sources — it is a tool-calling protocol. A2A connects agents to agents. An agent that needs a specialized capability (pricing logic, catalog search, RFQ processing) can delegate the work to a remote agent that exposes that capability via an A2A endpoint, receive the result as an A2A task, and continue its own workflow. The two protocols are complementary: MCP gives an agent hands; A2A gives it colleagues.
The A2A specification defines three core primitives:
- Agent Card — a JSON document at
/.well-known/agent-card.jsondescribing the agent's identity, capabilities, skills, and service endpoint. This is how agents discover each other. - Task — the unit of work. A client sends a task via
message/send(non-streaming) ormessage/stream(streaming). The task transitions through states:submitted,working,input-required,completed,failed,canceled. - Artifact — structured output produced during task execution, streamed to the client as it becomes available.
For B2B deployments, the value proposition is specific: instead of building one monolithic agent that does everything, you compose specialized agents that each own a domain — and they coordinate through a protocol, not through shared memory or hard-coded function calls.
The integration problem
Most existing agent frameworks do not speak A2A. They have their own native API surfaces, their own task models, their own streaming mechanisms. Hermes Agent (by Nous Research) exposes an OpenAI-compatible API Server at /v1/chat/completions and a runs-based SSE streaming interface at /v1/runs and /v1/runs/{id}/events. OpenClaw serves an OpenResponses-compatible API at POST /v1/responses. LangGraph has its own graph execution model. CrewAI has its own crew dispatch.
None of these frameworks will rewrite their internals to support A2A. Nor should they — their native APIs serve their own ecosystems well. The question is: can they participate in an A2A agent network without changing their code?
The answer is a bridge layer — a component that sits between the A2A protocol and the framework's native API. The bridge implements the A2A JSON-RPC surface on one side (Agent Card, task lifecycle, streaming) and translates to the framework's native calls on the other. The framework does not know it is being called via A2A. The A2A client does not know which framework executes the task.
This article walks through the bridge pattern using Hermes Agent as the demonstration. The same pattern applies to OpenClaw and other frameworks — the bridge handler is the only piece that changes.
The bridge architecture
A working reference implementation of this pattern is the a2a_daemon_engine — an A2A protocol daemon that runs as a gateway module and routes execution to pluggable handlers. The architecture is:
A2A Client (any agent or application)
│
POST /{endpoint_id}/a2a (message/send | message/stream)
│
▼
Gateway (auth, routing, SSE client management)
│
▼
A2A Daemon Executor
│
resolve_agent(uuid) → Database lookup
load_agent_handler() ← data-driven, metadata-based routing
│
├── HermesAgentHandler
│ → POST /v1/runs → GET /v1/runs/{id}/events → SSE streaming
│ → POST /v1/chat/completions (non-streaming)
│
└── [AnyFrameworkHandler] ← same interface, different translation
→ framework's native API
The bridge has three layers:
A2A protocol layer — handles JSON-RPC dispatch, Agent Card serving, task state machine, and the A2A SDK EventQueue. This is framework-agnostic. It is the same for every integration.
Gateway transport layer — handles HTTP, authentication, SSE client lifecycle, and routing. Also framework-agnostic. The gateway owns the wire; the bridge owns the translation.
Framework handler — the only framework-specific code. It implements an
ask_model()interface: accept A2A message parts and context, call the framework's native API, convert the response back to A2A message parts, and (for streaming) forward token deltas to the SSE channel.
Adding a new framework means writing one handler class. Everything else — the protocol surface, the gateway dispatch, the SSE management, the task persistence — is shared infrastructure.
Demonstration: the Hermes Agent bridge
The HermesAgentHandler is the worked example. It bridges A2A task semantics to the Hermes Agent API Server. The handler supports two execution modes:
Non-streaming: message/send with message_response
The handler calls POST /v1/chat/completions on the Hermes API Server — the OpenAI-compatible endpoint. The request carries the converted A2A message parts as a chat completion payload. Hermes processes the request (model inference, tool calls, agent reasoning) and returns a single response. The handler converts the response to an A2A Message with ROLE_AGENT and emits it to the SDK EventQueue.
The client receives a single JSON-RPC response with the full agent text. No intermediate status events, no streaming chunks — the request blocks until Hermes completes.
Streaming: message/send with task_execution and stream: true
The handler calls POST /v1/runs to create a run, then opens an SSE connection to GET /v1/runs/{id}/events. Hermes streams events as they happen: token deltas, reasoning metadata, tool call/result notifications, approval requests, and lifecycle events (run.created, run.completed, run.failed).
The handler runs a drain loop in a background thread. Each message.delta event is pushed to the gateway's SSE manager for real-time delivery to connected clients. The handler accumulates tokens into a single buffer. When run.completed arrives, the accumulated text is emitted as a single A2A Message to the SDK EventQueue, and a COMPLETED status event is pushed to SSE only.
This dual-path design — SSE for real-time chunks, SDK EventQueue for the final accumulated message — is a response to a constraint in the A2A SDK v2, discussed below.
Human-in-the-loop approval across agent boundaries
Hermes supports human-in-the-loop approval gates — when an agent needs permission to execute a sensitive action, it pauses and emits an approval request. The bridge translates this into the A2A INPUT_REQUIRED state, which signals to the calling agent (or human operator) that input is needed. The response comes back through POST /v1/runs/{id}/approval, and the run continues.
This is where the bridge pattern shows its value. A2A defines INPUT_REQUIRED as a first-class task state. Hermes has its own approval mechanism. The bridge maps one to the other, and the calling agent — which may itself be an A2A client running on a completely different framework — sees a standard protocol state transition, not a Hermes-specific detail. An agent delegation chain can include a step that requires human approval (a purchase authorization, a data access decision, a quote approval), and the A2A protocol carries that gate transparently across framework boundaries.
A2A state mapping
A2A defines a task state machine: submitted → working → input-required | completed | failed | canceled. Each framework has its own event vocabulary. The bridge maps between them.
The Hermes event-to-state mapping:
| Hermes SSE Event | A2A Task State | Bridge Action |
|---|---|---|
run.created |
WORKING |
Register run_id for cancel support |
message.delta |
WORKING |
Accumulate token; emit to SSE per-chunk |
reasoning.available |
WORKING |
Reasoning metadata — no token emission |
tool.call / tool.result |
WORKING |
Tool execution metadata only |
approval.required |
INPUT_REQUIRED |
Emit approval chunk; store pending_approval |
run.completed |
COMPLETED |
Set stream event; accumulate final text |
run.failed |
FAILED |
Emit error chunk; set FAILED state |
POST /v1/runs/{id}/stop |
CANCELED |
External cancel via tasks/cancel |
POST /v1/runs/{id}/approval |
(continues run) | Resolved via operation="approval_response" |
This table is the heart of the bridge. Every framework integration produces an equivalent table — the framework's native events on the left, A2A states on the right, bridge actions in the middle. The HERMES_INTEGRATION.md document in the reference implementation records the exact Hermes event format, configuration keys, and end-to-end flow details.
The A2A SDK v2 constraint and the dual-path fix
The A2A SDK v2 (a2a-sdk==1.0.2) imposes two constraints on the on_message_send path that shaped every bridge implementation:
- Single Message only. Emitting multiple
Messageobjects to the SDK EventQueue raisesInvalidAgentResponseError: Multiple Message objects received. - No TaskStatusUpdateEvent. Status events raise
InvalidAgentResponseError: Received TaskStatusUpdateEvent in message mode.
A naive bridge would emit one Message per token delta — the natural streaming pattern. The SDK rejects this. It also rejects status events (WORKING, COMPLETED) on the message/send path.
The fix is a dual-path output channel:
- SSE (gateway-managed): Token chunks are pushed to SSE in real-time. Connected clients see streaming output as it happens. Status events (WORKING, COMPLETED, FAILED) also go to SSE only.
- SDK EventQueue: After the stream completes, a single accumulated
Messagecontaining the full response text is emitted to the SDK EventQueue. This is what the JSON-RPCmessage/sendresponse returns.
The client gets real-time streaming via SSE and a clean single-message JSON-RPC response via the SDK. Both channels work; neither violates the SDK constraints. This pattern is framework-independent — it applies whether the backend is Hermes, OpenClaw, or any future handler.
Applying the same pattern to OpenClaw
OpenClaw's Gateway API exposes a different native surface than Hermes, but the bridge pattern is identical. An OpenClawHandler would implement the same ask_model() interface and translate A2A task semantics into OpenClaw Gateway calls:
- Non-streaming maps to
POST /v1/responseswith the A2A message parts converted to OpenResponses input items. The response is converted back to an A2AMessage. - Streaming maps to
POST /v1/responseswithstream: true, consuming the SSE event stream and forwarding token deltas to the A2A SSE channel. The OpenResponses streaming format usesresponse.output_text.deltafor token chunks andresponse.completedfor finish — different event names, same bridge structure. - Agent selection uses the
modelfield (openclaw/<agentId>) or thex-openclaw-agent-idheader, mapped from A2A agent metadata. - Session continuity leverages OpenClaw's
previous_response_idoruserfield for stable session routing, which maps naturally to A2A task persistence.
The state mapping table for OpenClaw would look like:
| OpenClaw Event | A2A Task State | Bridge Action |
|---|---|---|
response.created |
WORKING |
Register response ID |
response.output_text.delta |
WORKING |
Accumulate token; emit to SSE |
response.completed |
COMPLETED |
Accumulate final text; emit Message |
response.failed |
FAILED |
Emit error; set FAILED |
Same columns, same structure, different event names. The bridge handler is the only piece that changes between frameworks.
Configuration: metadata-driven routing
Agent routing is metadata-driven, not environment-variable-driven. Each agent record in the database carries its handler configuration in a metadata JSON column. For a Hermes-backed agent:
{
"module_name": "a2a_daemon_engine.handlers.a2a_hermes_handler",
"class_name": "HermesAgentHandler",
"hermes_api_url": "http://127.0.0.1:8642",
"hermes_api_key": "hermes-local-key",
"hermes_model": "hermes-agent",
"hermes_timeout": 300.0
}
For an OpenClaw-backed agent, the metadata would point to the OpenClaw handler:
{
"module_name": "a2a_daemon_engine.handlers.a2a_openclaw_handler",
"class_name": "OpenClawHandler",
"openclaw_api_url": "http://127.0.0.1:3000",
"openclaw_api_key": "openclaw-key",
"openclaw_agent_id": "pricing-agent",
"openclaw_timeout": 300.0
}
Config resolution follows a priority chain: agent metadata (DB) → setting dict → Config defaults (env vars). Per-agent overrides win over global defaults. Two agents can point to different frameworks — one to Hermes for reasoning-heavy tasks, another to OpenClaw for workflow-orchestration tasks — and the A2A protocol surface looks identical to the calling agent.
What this enables
The bridge pattern gives you three capabilities that are hard to assemble from scratch:
1. Agent-to-agent delegation with streaming. A2A client agents can send tasks to a Hermes-backed agent and receive real-time token streaming via SSE. The calling agent does not need to know that the remote agent runs Hermes — it sees an A2A endpoint with an Agent Card and a JSON-RPC interface.
2. Human-in-the-loop approval across agent boundaries. Framework-native approval gates (Hermes approval requests, OpenClaw operator approvals) map to A2A INPUT_REQUIRED states. An agent delegation chain can include a step that requires human approval, and the A2A protocol carries that state transition back to the originating agent or operator — regardless of which framework each agent in the chain runs on.
3. Multi-framework routing from one gateway. The same gateway, same executor, same A2A protocol surface serves handlers for different frameworks. Adding a new framework is writing one handler class and inserting one agent record — not a new deployment, not a new protocol implementation.
The reference implementation also supports AWS Lambda dispatch for serverless A2A, experimental gRPC transport with bidirectional streaming, and dual-backend persistence (DynamoDB or PostgreSQL) with multi-tenant isolation via composite partition keys. The a2a_daemon_engine repository and its Hermes integration guide contain the full implementation, configuration reference, and state mapping details.
A mid-market distributor needs quoting agents that talk to catalog agents that talk to inventory agents — each backed by a different framework, each owned by a different team. A2A gives those agents a shared protocol. A bridge layer lets Hermes Agent, OpenClaw, and any other framework participate without rewriting their internals. The a2a_daemon_engine is a working reference implementation of that bridge pattern, demonstrated with Hermes Agent.
Request a scoped build
One-week discovery. You get a system inventory, workflow map, and fixed scope — whether or not you build with us.
Want this built for your systems?
Every document here comes from real production work. If you have a target system and a workflow in mind, we can scope a build in one week.
Request a scoped buildOne-week discovery. You get a system inventory, workflow map, and fixed scope — whether or not you build with us.