How OmniRoute A2A Protocol Enables Agent-to-Agent Communication
OmniRoute implements the A2A (Agent-to-Agent) Protocol v0.3 as a lightweight JSON-RPC 2.0 service that lets autonomous agents discover capabilities, invoke skills, and stream results from one another through a standardized endpoint.
The OmniRoute A2A protocol provides a structured framework for machine-to-machine communication within the diegosouzapw/OmniRoute repository. By exposing agent capabilities via a well-known schema and handling task lifecycles through a state-managed JSON-RPC interface, the protocol transforms isolated AI agents into collaborative networks.
Core Architecture Components
The protocol rests on three foundational pillars that handle routing, persistence, and execution.
JSON-RPC 2.0 Endpoint
All agent communication flows through the canonical entry point at POST /a2a, defined in src/app/a2a/route.ts. This router exposes four primary methods: message/send, message/stream, tasks/get, and tasks/cancel. The endpoint enforces strict JSON-RPC 2.0 compliance, returning standard error codes (-32700 for parse errors, -32600 for invalid requests, -32601 for method not found) when requests deviate from the specification.
Task Manager
Located in src/lib/a2a/taskManager.ts, the task manager tracks the complete lifecycle of every interaction. When an agent submits a request, the manager assigns a UUID, initializes state as submitted, transitions to working during execution, and finalizes as completed, failed, or cancelled. Each task carries a default 5-minute TTL (time-to-live), after which automatic cleanup occurs. The manager exposes getStats() to monitor active streams and state distributions.
Skill Dispatcher
The src/lib/a2a/taskExecution.ts file maintains the A2A_SKILL_HANDLERS registry, mapping skill names to concrete TypeScript handlers. When a task arrives, the dispatcher invokes executeA2ATaskWithState, passing the full A2ATask object to the appropriate handler and capturing returned artifacts and metadata.
Agent Discovery via the Agent Card
Before invoking capabilities, querying agents retrieve the Agent Card by issuing a GET request to /.well-known/agent.json (served from src/app/.well-known/agent.json/route.ts). This JSON document advertises the node’s name, version, available A2A skills, and authentication requirements. The card generates dynamically from package metadata and caches for one hour, ensuring discoverability without manual configuration.
Request Flow and Lifecycle
Every agent-to-agent interaction follows a rigorous seven-step pipeline:
- Authentication – If
OMNIROUTE_API_KEYis configured, theauthenticatefunction validates theAuthorization: Bearer …header. Unauthenticated requests receive an immediate rejection. - Feature Toggle – The
rejectIfA2ADisabledhelper checks thea2aEnabledsetting, returning a-32000error if the endpoint is administratively disabled. - JSON-RPC Parsing – The router validates the
jsonrpc: "2.0"field and method existence. - Task Creation –
taskManager.createTaskinstantiates a task record, persisting the target skill, message array, and optional metadata. - Skill Execution – The handler registered in
A2A_SKILL_HANDLERSexecutes business logic (e.g., routing decisions, quota checks). - State Updates – The manager transitions the task from
workingto terminal states, capturing error traces on failure. - Response Formation – For synchronous calls (
message/send), the server returns a JSON-RPC response object. For streaming (message/stream), it opens an SSE connection viacreateA2AStreamfromsrc/lib/a2a/streaming.ts.
Built-in Skills for Agent Capabilities
OmniRoute ships with six predefined skills located under src/lib/a2a/skills/:
- Smart Routing (
smartRouting.ts) – Selects optimal provider combinations based on prompt characteristics and latency requirements. - Quota Management (
quotaManagement.ts) – Reports per-provider usage statistics and rate limit status. - Provider Discovery (
providerDiscovery.ts) – Enumerates installed providers and their capability matrices. - Cost Analysis (
costAnalysis.ts) – Estimates token costs and pricing for hypothetical requests. - Health Report (
healthReport.ts) – Aggregates circuit-breaker states and provider availability metrics. - List Capabilities (
listCapabilities.ts) – Returns the complete skill catalog for dynamic agent discovery.
Adding custom skills requires creating a new module under src/lib/a2a/skills/ and registering the exported handler in A2A_SKILL_HANDLERS within taskExecution.ts.
Real-time Communication with Streaming
The message/stream method enables real-time agent collaboration through Server-Sent Events (SSE). When invoked, the server maintains an open connection defined in src/lib/a2a/streaming.ts, pushing incremental artifacts (partial text generation, intermediate reasoning) until the task completes. This approach allows agents to consume results progressively rather than blocking until finalization.
Implementing Agent-to-Agent Communication
Synchronous Skill Invocation
To request code generation via the smart-routing skill:
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Write a hello world program in Python"}],
"metadata": {"model": "auto", "combo": "fast-coding"}
}
}'
Streaming Response Handling
For incremental result delivery using Node.js:
import fetch from "node-fetch";
const resp = await fetch("http://localhost:20128/a2a", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_KEY",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "stream-1",
method: "message/stream",
params: {
skill: "smart-routing",
messages: [{ role: "user", content: "Explain quantum computing" }],
},
}),
});
for await (const line of resp.body) {
console.log(line.toString());
}
Task Status Monitoring
Query a specific task's progress:
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"<TASK_UUID>"}}'
Observability and Monitoring
Every smart-routing decision is logged via logRoutingDecision in src/lib/a2a/routingLogger.ts, creating an audit trail for provider selection. The task manager's getStats() method exposes metrics including total tasks processed, active stream counts, and distribution across states (submitted, working, completed, failed). These statistics enable operators to monitor agent-to-agent traffic patterns and detect bottlenecks in skill execution.
Summary
- OmniRoute implements A2A Protocol v0.3 as a JSON-RPC 2.0 service exposed at
POST /a2a. - The Agent Card at
/.well-known/agent.jsonenables automatic capability discovery between agents. - Task lifecycle management in
src/lib/a2a/taskManager.tsprovides UUID assignment, state transitions, and TTL enforcement. - Six built-in skills handle routing, quotas, discovery, costing, health monitoring, and capability listing.
- SSE streaming via
src/lib/a2a/streaming.tssupports real-time, incremental result delivery. - Authentication uses standard Bearer tokens controlled by the
OMNIROUTE_API_KEYenvironment variable.
Frequently Asked Questions
What version of the A2A protocol does OmniRoute implement?
OmniRoute implements A2A Protocol version 0.3, as documented in docs/frameworks/A2A-SERVER.md. This specification defines the JSON-RPC 2.0 message format, required methods, and the Agent Card schema for capability advertisement.
How does OmniRoute secure agent-to-agent communication?
Security relies on Bearer token authentication implemented in src/app/a2a/route.ts. When the OMNIROUTE_API_KEY environment variable is set, the authenticate function rejects requests missing the Authorization: Bearer <token> header. Additionally, the rejectIfA2ADisabled toggle allows administrators to disable the entire A2A surface area.
Can developers add custom skills to the OmniRoute A2A protocol?
Yes. Developers create a new TypeScript module under src/lib/a2a/skills/ implementing the handler signature, then register it in the A2A_SKILL_HANDLERS map within src/lib/a2a/taskExecution.ts. Once registered, the skill becomes discoverable via the list-capabilities method and invocable through message/send or message/stream.
What happens when an A2A task exceeds its time limit?
The task manager enforces a default 5-minute TTL (configurable in src/lib/a2a/taskManager.ts). Tasks exceeding this duration transition to a terminal state and undergo cleanup, preventing resource exhaustion from orphaned or hanging agent requests.
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 →