How the A2A v0.3 Protocol Facilitates Agent-to-Agent Communication in OmniRoute
The A2A v0.3 protocol in OmniRoute enables autonomous agents to discover, invoke, and stream results from one another through a lightweight JSON-RPC 2.0 service built on a task manager, skill dispatcher, and streaming interface.
OmniRoute implements the A2A (Agent-to-Agent) Protocol version 0.3 to provide standardized agent-to-agent communication across distributed systems. This protocol allows external agents to leverage OmniRoute’s smart routing, quota management, and provider discovery capabilities through a clean, standards-based interface defined in docs/frameworks/A2A-SERVER.md.
Core Architecture of the A2A v0.3 Protocol
The protocol architecture rests on three primary components that handle request ingestion, lifecycle management, and business logic execution.
JSON-RPC 2.0 Endpoint
All A2A interactions flow through the canonical entry point defined in src/app/a2a/route.ts. This Next.js route handler exposes the POST /a2a endpoint and implements the JSON-RPC 2.0 specification. The supported methods include:
message/send– Synchronous request/response patternmessage/stream– Server-Sent Events (SSE) for real-time updatestasks/get– Retrieve task status and artifactstasks/cancel– Abort an in-flight task
The router validates the "jsonrpc": "2.0" header and returns standard JSON-RPC error codes (-32700, -32600, -32601, -32602) for malformed requests.
Task Manager
Located in src/lib/a2a/taskManager.ts, the task manager maintains the full lifecycle state machine for every request. It assigns a UUID to each task, enforces a default 5-minute TTL, and tracks transitions from submitted → working → completed or failed. The createTask function initializes the record, while getStats exposes operational metrics including active stream counts and state distributions.
Skill Dispatcher
The dispatcher in src/lib/a2a/taskExecution.ts maps skill names to concrete handlers via the A2A_SKILL_HANDLERS registry. When a request specifies a skill (e.g., "smart-routing"), the dispatcher invokes executeA2ATaskWithState, which runs the handler and manages state transitions through the task manager.
Agent Discovery via the Agent Card
Before invoking capabilities, A2A clients discover what an OmniRoute node offers by requesting /.well-known/agent.json. This Agent Card—generated in src/app/.well-known/agent.json/route.ts—contains the node’s name, version, available skills, and authentication requirements. The response is cached for one hour and provides the canonical manifest for automated agent negotiation.
Request Lifecycle and Method Handlers
Every A2A request passes through a rigorous validation and execution pipeline:
- Authentication – If
OMNIROUTE_API_KEYis configured, theauthenticatefunction requires a matchingAuthorization: Bearer <token>header. - Feature Toggle – The
rejectIfA2ADisabledhelper checks thea2aEnabledsetting, returning error code-32000if the endpoint is disabled. - Task Creation – Valid requests trigger
taskManager.createTask, persisting the skill name, message array, and metadata. - Skill Execution – The appropriate handler from
A2A_SKILL_HANDLERSreceives theA2ATaskobject and returns{ artifacts, metadata }. - State Updates – The manager updates the task to
working, then finalizes tocompletedorfaileddepending on handler success. - Response Formation – Synchronous calls return a JSON-RPC response object, while streaming calls initiate SSE via
createA2AStreamfromsrc/lib/a2a/streaming.ts.
Built-in Skills for Agent Capabilities
OmniRoute ships with six native skills located in src/lib/a2a/skills/, each exportable for the A2A_SKILL_HANDLERS registry:
- Smart Routing (
smartRouting.ts) – Selects optimal provider combinations for prompt execution. - Quota Management (
quotaManagement.ts) – Reports per-provider usage and limits. - Provider Discovery (
providerDiscovery.ts) – Lists installed providers and their capabilities. - Cost Analysis (
costAnalysis.ts) – Estimates token and monetary costs for requests. - Health Report (
healthReport.ts) – Summarizes circuit-breaker status and provider health. - List Capabilities (
listCapabilities.ts) – Returns the full skill catalog for dynamic discovery.
New capabilities are added by creating a TypeScript module in the skills directory and registering it in src/lib/a2a/taskExecution.ts.
Real-time Communication with SSE Streaming
The message/stream method enables progressive result delivery through Server-Sent Events. When invoked, the server establishes an SSE connection using headers defined in SSE_HEADERS (located in src/lib/a2a/streaming.ts) and pushes incremental chunks as the skill handler produces artifacts. This allows calling agents to process partial results—such as streaming text generation—without waiting for the full operation to complete.
Practical Implementation Examples
Synchronous Smart Routing Request
Invoke the smart-routing skill with a standard JSON-RPC payload:
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
Use message/stream for real-time results:
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 in simple terms" }],
},
}),
});
for await (const line of resp.body) {
console.log(line.toString());
}
Task Status Query
Retrieve the current state of a specific task:
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>"}}'
Capability Discovery
Query available skills dynamically:
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"cap","method":"message/send","params":{"skill":"list-capabilities"}}'
Summary
- The A2A v0.3 protocol utilizes JSON-RPC 2.0 over HTTP with a dedicated
POST /a2aendpoint defined insrc/app/a2a/route.ts. - Task lifecycle management is handled by the task manager in
src/lib/a2a/taskManager.ts, which enforces a 5-minute TTL and tracks state transitions. - Skill execution is modularized through the
A2A_SKILL_HANDLERSregistry insrc/lib/a2a/taskExecution.ts, supporting six built-in capabilities. - Real-time streaming is implemented via Server-Sent Events using utilities in
src/lib/a2a/streaming.ts. - Discovery follows the A2A specification through the
/.well-known/agent.jsonendpoint generated insrc/app/.well-known/agent.json/route.ts.
Frequently Asked Questions
What authentication mechanism does the A2A v0.3 protocol use in OmniRoute?
OmniRoute validates requests using a Bearer token scheme. When the OMNIROUTE_API_KEY environment variable is set, the authenticate function in src/app/a2a/route.ts requires an Authorization: Bearer <token> header matching that key. If the variable is unset, the endpoint operates without authentication.
How does OmniRoute handle long-running agent tasks?
Long-running tasks utilize the message/stream method, which opens an SSE connection via createA2AStream in src/lib/a2a/streaming.ts. This allows the server to push incremental artifacts while the task status remains working, avoiding timeouts that would occur with synchronous HTTP requests.
Can I add custom skills to the OmniRoute A2A implementation?
Yes. Create a new TypeScript file in src/lib/a2a/skills/ that exports a handler function receiving an A2ATask object and returning { artifacts, metadata }. Register this handler in the A2A_SKILL_HANDLERS object within src/lib/a2a/taskExecution.ts to make it available for invocation via the JSON-RPC interface.
What happens if the A2A endpoint is disabled in OmniRoute?
The rejectIfA2ADisabled helper checks the a2aEnabled configuration flag before processing any request. If disabled, the server returns a JSON-RPC error with code -32000 and a message indicating that the A2A service is not active, preventing accidental exposure of agent capabilities.
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 →