How the A2A Protocol Server Enables Agent-to-Agent Communication in OmniRoute
The A2A protocol server enables agent-to-agent communication by exposing a JSON-RPC 2.0 endpoint at POST /a2a that manages task lifecycles, streams real-time updates via Server-Sent Events, and orchestrates reusable skills through a stateful task manager.
The A2A protocol server in the OmniRoute repository provides a standardized infrastructure for autonomous agents to exchange messages and coordinate workflows over HTTP. Built on JSON-RPC 2.0, it handles everything from task creation to skill execution, allowing agents to communicate synchronously or via real-time streaming. This article examines the server architecture, request flow, and implementation details based on the source code in diegosouzapw/OmniRoute.
Core Architecture of the A2A Protocol Server
The server architecture revolves around five primary components that handle the complete lifecycle of agent interactions.
Task Manager
Located in src/lib/a2a/taskManager.ts, the Task Manager maintains a state machine (submitted → working → completed/failed/canceled) for each task. It persists task metadata in SQLite, handles TTL-based cleanup of stale tasks, and assigns UUIDs to track request state across the distributed system.
Task Execution Engine
The src/lib/a2a/taskExecution.ts module executes the actual logic for each request. It looks up the requested skill, validates input using Zod schemas, runs the skill in a sandboxed executor, and manages the transition from task creation to result delivery.
Streaming Layer
Implemented in src/lib/a2a/streaming.ts, this component handles Server-Sent Events (SSE) for JSON-RPC streaming. It manages back-pressure, abort signals, and proper JSON-RPC framing for long-running agent interactions.
Skills Registry
Reusable modules in src/lib/a2a/skills/*.ts encapsulate domain logic such as routing, quota management, and provider discovery. Each skill registers a JSON-RPC method name, input schema, and handler function that the execution engine can invoke.
Routing Logger
The src/lib/a2a/routingLogger.ts component records detailed logs of every inter-agent request, providing audit trails and debugging capabilities for production deployments.
Agent-to-Agent Communication Workflow
When one agent communicates with another through the A2A protocol server, the request flows through five distinct stages:
-
Request Validation: The calling agent sends a JSON-RPC payload to
POST /a2a(e.g.,message/sendormessage/stream). The server validates the payload against Zod schemas defined in the skill modules. -
Task Creation:
taskManager.createTask()stores a new task row, assigns a UUID, and returns a task ID to track the operation. -
Skill Execution:
taskExecution.runSkill()looks up the skill implementation, runs it inside a sandboxed executor, and prepares results for delivery. -
Result Propagation: For streaming calls (
message/stream), the server pushes JSON-RPCprogressorresultmessages over an SSE connection. For synchronous calls (message/send), the final JSON-RPC response is returned once the skill completes. -
Task Finalization: The task state updates to
completed,failed, orcanceled. A background TTL cleanup job automatically removes stale tasks from SQLite.
Practical Implementation Examples
Synchronous Message Exchange
Agents can send single-request messages using the message/send method:
POST /a2a HTTP/1.1
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"targetAgent": "router",
"payload": { "query": "list available models" }
},
"id": "req-123"
}
Once the skill finishes, the server returns a standard JSON-RPC response:
{
"jsonrpc": "2.0",
"result": {
"models": ["gpt-4o", "claude-3.5-sonnet", "gemini-1.5-pro"]
},
"id": "req-123"
}
Real-Time Streaming
For continuous interactions, use message/stream with Server-Sent Events:
POST /a2a HTTP/1.1
Accept: text/event-stream
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "message/stream",
"params": {
"targetAgent": "router",
"payload": { "prompt": "Explain the A2A architecture" }
},
"id": "stream-456"
}
The server returns partial results as SSE data frames:
data: {"jsonrpc":"2.0","method":"progress","params":{"chunk":"The A2A"}}
data: {"jsonrpc":"2.0","method":"progress","params":{"chunk":" protocol server"}}
data: {"jsonrpc":"2.0","result":{"answer":"The A2A protocol server ..."},"id":"stream-456"}
Direct Skill Invocation
You can also invoke specific skills directly, such as the smart routing skill:
POST /a2a HTTP/1.1
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "smartRouting",
"params": {
"taskId": "req-789",
"candidateProviders": ["openai", "anthropic", "gemini"]
},
"id": "req-789"
}
Response:
{
"jsonrpc": "2.0",
"result": {
"selectedProvider": "anthropic",
"reason": "lowest estimated cost for 1k tokens"
},
"id": "req-789"
}
Agent Discovery and Capability Negotiation
The A2A server exposes its capabilities via the .well-known/agent.json endpoint. This public metadata describes available skills, endpoint URLs, and supported protocols, allowing other agents to discover and negotiate capabilities automatically without manual configuration.
Summary
- JSON-RPC 2.0 API: The server exposes a single endpoint at
POST /a2asupporting both synchronous and streaming communication patterns. - Stateful Task Management:
src/lib/a2a/taskManager.tsimplements a robust state machine with SQLite persistence and automatic TTL cleanup. - Real-Time Streaming: The
src/lib/a2a/streaming.tsmodule enables SSE-based communication for long-running agent workflows. - Modular Skill System: Skills in
src/lib/a2a/skills/*.tsprovide reusable capabilities like routing and quota management that any agent can invoke. - Observable Communication:
src/lib/a2a/routingLogger.tscaptures detailed audit trails of all inter-agent requests.
Frequently Asked Questions
What protocol does the A2A server use for agent communication?
The A2A protocol server implements JSON-RPC 2.0 over HTTP, providing a standardized request-response format. It supports both standard HTTP POST requests for synchronous calls and Server-Sent Events (SSE) for streaming interactions, as defined in the OmniRoute source code.
How does the server handle real-time streaming between agents?
Real-time streaming uses the message/stream method implemented in src/lib/a2a/streaming.ts. The server maintains an open SSE connection and pushes JSON-RPC progress messages for partial results, followed by a final result message when the skill completes. The implementation handles back-pressure and abort signals to ensure reliable delivery.
What is the purpose of the .well-known/agent.json file?
The .well-known/agent.json file serves as a public capability descriptor that allows other agents to discover the server's available skills, endpoint URLs, and supported protocols automatically. This enables dynamic agent networks where participants can negotiate capabilities without hard-coded configuration.
How does the task manager handle failed or stale tasks?
The Task Manager in src/lib/a2a/taskManager.ts tracks task states through a defined lifecycle (submitted → working → completed/failed/canceled). For failed tasks, it updates the state to failed with error details. Stale tasks are automatically cleaned up via a TTL-based background job that removes old entries from the SQLite database.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →