# How OmniRoute A2A Protocol Enables Agent-to-Agent Communication

> Discover how OmniRoute A2A protocol facilitates agent-to-agent communication. Learn about capability discovery, skill invocation, and result streaming via JSON-RPC 2.0.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: deep-dive
- Published: 2026-08-08

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main//.well-known/agent.json) (served from [`src/app/.well-known/agent.json/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

1. **Authentication** – If `OMNIROUTE_API_KEY` is configured, the `authenticate` function validates the `Authorization: Bearer …` header. Unauthenticated requests receive an immediate rejection.
2. **Feature Toggle** – The `rejectIfA2ADisabled` helper checks the `a2aEnabled` setting, returning a `-32000` error if the endpoint is administratively disabled.
3. **JSON-RPC Parsing** – The router validates the `jsonrpc: "2.0"` field and method existence.
4. **Task Creation** – `taskManager.createTask` instantiates a task record, persisting the target skill, message array, and optional metadata.
5. **Skill Execution** – The handler registered in `A2A_SKILL_HANDLERS` executes business logic (e.g., routing decisions, quota checks).
6. **State Updates** – The manager transitions the task from `working` to terminal states, capturing error traces on failure.
7. **Response Formation** – For synchronous calls (`message/send`), the server returns a JSON-RPC response object. For streaming (`message/stream`), it opens an SSE connection via `createA2AStream` from [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/smartRouting.ts)) – Selects optimal provider combinations based on prompt characteristics and latency requirements.
- **Quota Management** ([`quotaManagement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/quotaManagement.ts)) – Reports per-provider usage statistics and rate limit status.
- **Provider Discovery** ([`providerDiscovery.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/providerDiscovery.ts)) – Enumerates installed providers and their capability matrices.
- **Cost Analysis** ([`costAnalysis.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/costAnalysis.ts)) – Estimates token costs and pricing for hypothetical requests.
- **Health Report** ([`healthReport.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/healthReport.ts)) – Aggregates circuit-breaker states and provider availability metrics.
- **List Capabilities** ([`listCapabilities.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```bash
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:

```javascript
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:

```bash
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.json`](https://github.com/diegosouzapw/OmniRoute/blob/main//.well-known/agent.json) enables automatic capability discovery between agents.
- **Task lifecycle management** in [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts) provides 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.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts) supports real-time, incremental result delivery.
- Authentication uses standard Bearer tokens controlled by the `OMNIROUTE_API_KEY` environment 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.