# How the OmniRoute A2A Server Implements JSON-RPC 2.0 for Agent Communication

> Discover how the OmniRoute A2A server uses JSON-RPC 2.0 for agent communication. Learn about its HTTP endpoint, Zod validation, skill dispatch, and SSE streaming.

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

---

**The OmniRoute A2A server exposes a single `/a2a` HTTP endpoint that accepts JSON-RPC 2.0 requests, validates the protocol envelope against Zod schemas, dispatches methods to registered skill handlers, and returns standard JSON-RPC responses with optional Server-Sent Events (SSE) streaming for long-running tasks.**

The OmniRoute repository provides an Agent-to-Agent (A2A) communication layer that enables autonomous agents to invoke remote skills using a standardized protocol. At the core of this system lies a strict JSON-RPC 2.0 implementation that handles request validation, method dispatch, and real-time streaming updates. This article examines how the A2A server processes JSON-RPC envelopes in [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts), routes calls to skill-specific logic, and manages task state through SQLite-backed persistence.

## JSON-RPC 2.0 Protocol Validation and Routing

### Request Envelope Validation

Located in [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts), the A2A server's entry point enforces strict JSON-RPC 2.0 compliance before executing any business logic. Upon receiving a POST request to `/a2a`, the server verifies that the payload contains the required `"jsonrpc": "2.0"` field and a valid `method` string. Requests missing these mandatory fields trigger the `jsonRpcError` helper function, which returns error code `-32600` (Invalid Request) according to the JSON-RPC 2.0 specification, ensuring protocol integrity at the transport layer.

### Method Dispatch to Skill Handlers

Once validated, the `method` field is matched against the `A2A_SKILL_HANDLERS` registry defined in [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts). This registry maps method names such as `smartRouting`, `quotaManagement`, and `providerDiscovery` to Zod-validated handler functions that implement the actual skill logic. If the requested method does not exist in the registry, the server immediately responds with JSON-RPC error code `-32601` (Method not found), preventing undefined behavior.

## Task Execution and State Management

### Task Creation and Persistence

Before executing skill logic, the server initializes a task record via [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts). This module generates a unique `taskId`, instantiates a `TaskState` object, and persists the state to the SQLite database in `src/lib/db`. This persistence layer ensures that task progress survives server restarts and enables external systems to query status asynchronously using the assigned identifier.

### Synchronous and Asynchronous Execution

The `executeA2ATaskWithState` function in [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts) orchestrates the actual skill invocation. For short-lived operations, the function returns results immediately within the HTTP response cycle. For long-running skills requiring multiple inference steps or external API calls, the function coordinates with the streaming subsystem to maintain an open connection while processing continues in the background.

## Streaming Updates via Server-Sent Events

For skills that require extended processing time, the A2A server opens an SSE stream implemented in [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts). Rather than holding the HTTP connection idle, the server emits JSON-RPC notification messages with the method `session/update`. These notifications deliver partial results such as tool calls or agent message chunks, allowing clients to display real-time progress before receiving the final response. The payload structure conforms to the Zod schemas defined in [`open-sse/mcp-server/schemas/a2a.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/a2a.ts), ensuring type safety across all streaming events.

## Practical JSON-RPC 2.0 Examples

### Synchronous Method Call

To query available capabilities, clients send a standard JSON-RPC request to the `/a2a` endpoint:

```bash
curl -X POST https://localhost:20128/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "listCapabilities",
    "params": {}
  }'

```

The server returns a JSON-RPC success response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "capabilities": ["smartRouting", "quotaManagement", "providerDiscovery"]
  }
}

```

### Streaming Long-Running Tasks

For methods like `smartRouting` that involve multiple inference steps, request SSE streaming by including the appropriate Accept header:

```bash
curl -N -X POST https://localhost:20128/a2a \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "abc-123",
    "method": "smartRouting",
    "params": { "prompt": "Explain the weather in Tokyo." }
  }'

```

The response streams JSON-RPC notifications followed by the final result:

```text
event: message
data: {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"abc-123","update":{"sessionUpdate":"tool_call","toolCallId":"tool-1","title":"Search Weather"}}}

event: message
data: {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"abc-123","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"Tokyo is currently partly cloudy..."}}}}

event: done
data: {"jsonrpc":"2.0","id":"abc-123","result":{"answer":"Tokyo is currently partly cloudy with a temperature of 22°C."}}

```

### Error Handling for Invalid Requests

When a request omits required JSON-RPC fields, the server returns a protocol-level error:

```bash
curl -X POST https://localhost:20128/a2a \
  -H "Content-Type: application/json" \
  -d '{"id":2,"method":"listCapabilities"}'

```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32600,
    "message": "Invalid request: missing jsonrpc or method"
  }
}

```

## Summary

- **Protocol Compliance**: The A2A server in [`src/app/a2a/route.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/a2a/route.ts) enforces strict JSON-RPC 2.0 validation, rejecting malformed requests with standard error code `-32600` before reaching business logic.
- **Skill Registry**: Method dispatch occurs through the `A2A_SKILL_HANDLERS` registry in [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts), supporting Zod-validated handlers for capabilities like `smartRouting` and `providerDiscovery`.
- **State Persistence**: [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts) maintains task state in SQLite via `src/lib/db`, enabling robust tracking of long-running operations through unique `taskId` assignments.
- **Streaming Architecture**: Server-Sent Events implementation in [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts) delivers JSON-RPC `session/update` notifications for real-time progress updates, conforming to schemas in [`open-sse/mcp-server/schemas/a2a.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/a2a.ts).

## Frequently Asked Questions

### What JSON-RPC 2.0 error codes does the OmniRoute A2A server use?

The server implements standard JSON-RPC 2.0 error codes as defined in the specification. Error `-32600` indicates an invalid request when the `jsonrpc` field is missing or the envelope is malformed. Error `-32601` signals that the requested method was not found in the `A2A_SKILL_HANDLERS` registry. These standardized codes ensure client libraries can handle failures predictably.

### How does the A2A server handle long-running agent tasks?

For operations exceeding immediate response times, the server utilizes Server-Sent Events (SSE) via [`src/lib/a2a/streaming.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/streaming.ts). It sends JSON-RPC notification messages with the method `session/update` containing partial results, tool calls, or message chunks, allowing clients to stream progress before receiving the final JSON-RPC success response with the complete `result` object.

### Where is the JSON-RPC schema validation defined in OmniRoute?

Zod schemas validating JSON-RPC payloads reside in [`open-sse/mcp-server/schemas/a2a.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/a2a.ts). These schemas enforce the structure of requests, responses, and streaming notifications, ensuring type safety across the `smartRouting`, `quotaManagement`, and other A2A skill handlers defined in [`src/lib/a2a/taskExecution.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskExecution.ts).

### Can the A2A server recover tasks after a restart?

Yes. The [`src/lib/a2a/taskManager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/a2a/taskManager.ts) module persists all task states to an SQLite database located in `src/lib/db`. Each task receives a unique `taskId` upon creation, enabling the system to resume execution or query task status even if the server process restarts during a long-running `smartRouting` or `providerDiscovery` operation.