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

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, 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, 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. 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. 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 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. 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, 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:

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:

{
  "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:

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:

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:

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

Response:

{
  "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 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, supporting Zod-validated handlers for capabilities like smartRouting and providerDiscovery.
  • State Persistence: 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 delivers JSON-RPC session/update notifications for real-time progress updates, conforming to schemas in 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. 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. 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.

Can the A2A server recover tasks after a restart?

Yes. The 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →