# What Is the A2A v0.3 Protocol in OmniRoute for Agent-to-Agent Communication?

> Discover the A2A v0.3 protocol in OmniRoute. This JSON-RPC 2.0 service allows autonomous agents to easily discover, invoke, and stream results from each other.

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

---

**OmniRoute implements the A2A v0.3 protocol as a lightweight JSON-RPC 2.0 service that enables autonomous agents to discover, invoke, and stream results from one another through a standardized interface.**

The A2A (Agent-to-Agent) Protocol version 0.3 is the core mechanism that allows OmniRoute to function as both a client and server in multi-agent systems. Built on three interconnected components—a JSON-RPC endpoint, a task lifecycle manager, and a pluggable skill dispatcher—this protocol transforms OmniRoute nodes into interoperable, autonomous services that any compliant agent can interact with programmatically.

## Core Architecture of A2A v0.3

The protocol implementation in OmniRoute centers on three architectural layers:

| Component | Role | Source Location |
| --- | --- | --- |
| **JSON-RPC 2.0 endpoint** | Canonical entry point (`POST /a2a`) for all A2A methods | [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts) |
| **Task manager** | Tracks task lifecycle (submitted → working → completed/failed/cancelled) with UUID assignment and 5-minute TTL | [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts) |
| **Skill dispatcher** | Maps skill names to concrete handlers via `A2A_SKILL_HANDLERS` registry | [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts) |

### Supported JSON-RPC Methods

The endpoint in [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts) exposes four primary methods:

- **`message/send`** — Synchronous request-response for single-shot tasks
- **`message/stream`** — Initiates Server-Sent Events (SSE) for incremental results
- **`tasks/get`** — Retrieves current state and artifacts for an existing task
- **`tasks/cancel`** — Aborts an in-progress task and updates state to `cancelled`

## Agent Discovery via the Agent Card

Before invoking capabilities, agents must discover what a node offers. OmniRoute exposes this through the **Agent Card** at [`/.well-known/agent.json`](https://github.com/diegosouzapw/OmniRoute/blob/main//.well-known/agent.json).

This JSON document contains:
- Node name and version derived from [`package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/package.json)
- Complete catalog of available A2A skills
- Authentication requirements
- Endpoint URLs

The card is cached for one hour and generated dynamically in [`src/app/.well-known/agent.json/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/.well-known/agent.json/route.ts), ensuring discovery information stays current with deployed capabilities.

## Request Processing Flow

Every A2A v0.3 request traverses seven validation and execution stages:

1. **Authentication check** — The `authenticate` function validates `Authorization: Bearer <token>` headers against `OMNIROUTE_API_KEY` when configured
2. **Enabled toggle verification** — `rejectIfA2ADisabled` returns error code `-32000` if `a2aEnabled` is false in settings
3. **JSON-RPC parsing** — Strict validation of `"jsonrpc": "2.0"` and supported methods; malformed requests trigger standard codes (`-32700`, `-32600`, `-32601`, `-32602`)
4. **Task creation** — `taskManager.createTask` instantiates an `A2ATask` with UUID, skill selection, message array, and metadata
5. **Skill execution** — The appropriate handler from `A2A_SKILL_HANDLERS` receives the full task object
6. **State transitions** — Manager updates status through `working` to terminal states (`completed`, `failed`, or `cancelled`)
7. **Response formatting** — Single JSON-RPC response for `message/send`, or SSE stream via `createA2AStream` for `message/stream`

This flow is fully documented in [`docs/frameworks/A2A-SERVER.md`](https://github.com/diegosouzapw/OmniRoute/blob/main/docs/frameworks/A2A-SERVER.md), which serves as the authoritative reference for method signatures and error handling.

## Built-in A2A Skills in OmniRoute

Skills are the executable capabilities exposed through the A2A v0.3 protocol. OmniRoute ships with six default skills in `src/lib/a2a/skills/`:

| Skill | File | Purpose |
| --- | --- | --- |
| **Smart Routing** | [`smartRouting.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/smartRouting.ts) | Selects optimal provider/combo based on prompt characteristics and constraints |
| **Quota Management** | [`quotaManagement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/quotaManagement.ts) | Reports per-provider quota usage and limits |
| **Provider Discovery** | [`providerDiscovery.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/providerDiscovery.ts) | Enumerates installed providers with their capabilities |
| **Cost Analysis** | [`costAnalysis.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/costAnalysis.ts) | Estimates monetary cost for requests or full conversations |
| **Health Report** | [`healthReport.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/healthReport.ts) | Summarizes circuit-breaker status and provider health metrics |
| **List Capabilities** | [`listCapabilities.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/listCapabilities.ts) | Returns the complete skills catalog for dynamic discovery |

New skills are added by creating a TypeScript module under `src/lib/a2a/skills/` and registering the handler in `A2A_SKILL_HANDLERS` within [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts).

## Streaming with Server-Sent Events

The A2A v0.3 protocol supports real-time result delivery through `message/stream`. When invoked, OmniRoute:

- Opens an SSE connection with headers defined in `SSE_HEADERS` (from [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts))
- Pushes incremental artifacts as chunks become available
- Transmits final metadata upon completion
- Ensures the calling agent receives partial results without blocking on full operation completion

This streaming mechanism is essential for long-running operations like large language model generation, where intermediate tokens provide value before final aggregation.

## Practical Code Examples

### Synchronous Skill Invocation

```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"}
    }
  }'

```

This pattern from [`docs/frameworks/A2A-SERVER.md`](https://github.com/diegosouzapw/OmniRoute/blob/main/docs/frameworks/A2A-SERVER.md) (lines 55-68) demonstrates the canonical request structure: JSON-RPC envelope with skill selection, message history, and execution metadata.

### Streaming Response Consumption

```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 in simple terms" }],
    },
  }),
});

for await (const line of resp.body) {
  console.log(line.toString());
}

```

The streaming implementation in [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts) handles chunked encoding and connection lifecycle management automatically.

### Task Status Querying

```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>"}}'

```

The `tasks/get` method, implemented around lines 90-110 of [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts), returns full task state including current status, accumulated artifacts, and execution metadata.

### Capability Discovery

```bash
curl http://localhost:20128/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_KEY" \
  -d '{"jsonrpc":"2.0","id":"cap","method":"list-capabilities","params":{}}'

```

This invocation of the `list-capabilities` skill, defined in [`src/lib/a2a/skills/listCapabilities.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/skills/listCapabilities.ts), enables dynamic capability negotiation between agents without hardcoded assumptions.

## Observability and Monitoring

The A2A v0.3 protocol implementation includes built-in telemetry:

- **`logRoutingDecision`** ([`src/lib/a2a/routingLogger.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/routingLogger.ts)) records every smart-routing choice with context for audit and optimization
- **`getStats`** on the task manager exposes counters per state, total task volume, and active stream count

These mechanisms enable operators to monitor A2A traffic, detect bottlenecks, and analyze skill utilization patterns across their OmniRoute deployment.

## Summary

- **A2A v0.3 in OmniRoute** is a JSON-RPC 2.0 protocol enabling standardized agent-to-agent communication through `POST /a2a`
- **Three core components**—endpoint router, task manager, and skill dispatcher—provide complete request lifecycle management
- **Discovery** happens via [`/.well-known/agent.json`](https://github.com/diegosouzapw/OmniRoute/blob/main//.well-known/agent.json), returning node capabilities and authentication requirements
- **Six built-in skills** cover routing, quotas, providers, costs, health, and capability enumeration
- **Streaming support** via SSE allows real-time result delivery for long-running operations
- **Extensibility** through the `A2A_SKILL_HANDLERS` registry enables custom business logic without protocol modification

## Frequently Asked Questions

### How does A2A v0.3 authentication work in OmniRoute?

Authentication is optional and environment-driven. When `OMNIROUTE_API_KEY` is configured, the `authenticate` function in [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts) requires matching `Authorization: Bearer` headers; otherwise, the endpoint operates unauthenticated. This design supports both open development environments and production deployments requiring credential validation.

### What is the default task timeout in A2A v0.3?

Tasks receive a **5-minute TTL (time-to-live)** by default, enforced by the task manager in [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts). After expiration, tasks transition to a terminal failure state and are eligible for cleanup. This prevents resource accumulation from abandoned or hung operations.

### Can I add custom skills to the A2A v0.3 protocol implementation?

Yes. Create a TypeScript module under `src/lib/a2a/skills/` implementing the skill handler interface, then register it in `A2A_SKILL_HANDLERS` within [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts). The skill becomes immediately available through `message/send` and `message/stream` invocations with your chosen identifier.

### How does streaming differ from synchronous calls in A2A v0.3?

**Synchronous calls** (`message/send`) block until completion and return a single JSON-RPC response containing all artifacts. **Streaming calls** (`message/stream`) return immediately with SSE headers, then push incremental chunks through an open connection as the skill produces output. Streaming is implemented in [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts) and is essential for responsive user experiences with generation-heavy skills.