Skip to content
reaatechREAATECH

@reaatech/a2a-reference-client

npm v0.1.0

Provides a TypeScript client class for interacting with A2A agents via JSON-RPC 2.0 and Server-Sent Events. It includes built-in agent discovery, automatic retry logic, and an `AsyncGenerator` interface for consuming real-time task streams.

@reaatech/a2a-reference-client

npm version License: MIT CI

Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.

TypeScript client SDK for discovering A2A agents, submitting tasks, and consuming real-time Server-Sent Events (SSE) streams. Built on the standard fetch API with pluggable transport for edge runtimes and custom headers.

Installation

terminal
npm install @reaatech/a2a-reference-client
# or
pnpm add @reaatech/a2a-reference-client

Feature Overview

  • Agent discovery — fetch and cache agent cards from /.well-known/agent.json
  • JSON-RPC 2.0 transport — all task operations use standards-compliant RPC
  • SSE streamingAsyncGenerator-based consumption of status and artifact events
  • Automatic retry — exponential backoff with jitter on 5xx errors and network failures
  • Pluggable fetch — inject custom fetchImpl for edge runtimes, proxies, or auth headers
  • Full type safety — all responses validated against Zod schemas from @reaatech/a2a-reference-core

Quick Start

typescript
import { A2AClient } from "@reaatech/a2a-reference-client";
 
// Discover an agent from its card URL
const client = await A2AClient.discover("http://localhost:3000");
 
// Or construct directly
const client = new A2AClient({ baseUrl: "http://localhost:3000" });
 
// Send a message and get the resulting task
const task = await client.sendMessage({
  messageId: "msg-1",
  role: "user",
  parts: [{ kind: "text", text: "Hello, agent!" }],
});
 
console.log(`Task ${task.id} is ${task.status.state}`);

API Reference

A2AClient (class)

Static Methods

MethodDescription
discover(cardUrl, fetchImpl?)Fetches agent card from cardUrl, validates it, returns a configured A2AClient
fromCardUrl(cardUrl, fetchImpl?)Alias for discover

Constructor

typescript
new A2AClient(options: A2AClientOptions)

A2AClientOptions

PropertyTypeDefaultDescription
baseUrlstring(required)Base URL of the A2A agent server
fetchImpltypeof fetchglobalThis.fetchCustom fetch implementation
agentCardTtlMsnumber300000 (5 min)Cache TTL for agent card
maxRetriesnumber0Max retry attempts for failed RPC calls
retryDelayMsnumber1000Base delay for exponential backoff

Instance Methods — Discovery

MethodDescription
getAgentCard()Returns the cached agent card (fetches on first call)
clearAgentCardCache()Invalidates the cached agent card

Instance Methods — Task Management

All methods use JSON-RPC 2.0 over POST {baseUrl}.

MethodRPC MethodReturnsDescription
sendMessage(message, contextId?, taskId?)tasks/sendTaskStart or continue a task
getTask(taskId)tasks/getTaskRetrieve a task by ID
listTasks()tasks/list{ tasks, nextPageToken?, totalSize? }List all tasks (paginated)
cancelTask(taskId)tasks/cancelTaskRequest cancellation

Instance Methods — SSE Streaming

Both methods check that the agent supports streaming (capabilities.streaming) and return AsyncGenerators.

MethodHTTPYieldsDescription
sendSubscribe(message, contextId?)POST /tasks/sendSubscribeStreamEventSend message + subscribe to stream
subscribe(taskId)GET /tasks/:taskId/subscribeStreamEventSubscribe to existing task stream

Stream Events

The AsyncGenerator yields a union of four event types, discriminated by kind:

typescript
type StreamEvent =
  | TaskStatusUpdateEvent   // { kind: "status", taskId?, status, final? }
  | TaskArtifactUpdateEvent // { kind: "artifact", taskId?, artifact, append?, lastChunk? }
  | { kind: "task", task: Task }
  | { kind: "message", message: Message };

Usage Patterns

Streaming Task Progress

typescript
const client = new A2AClient({ baseUrl: "http://localhost:3000" });
 
for await (const event of client.sendSubscribe({
  messageId: "stream-1",
  role: "user",
  parts: [{ kind: "text", text: "Count to 10" }],
})) {
  if (event.kind === "status") {
    console.log(`Status: ${event.status.state}`);
  } else if (event.kind === "artifact") {
    const text = event.artifact.parts
      .filter((p) => p.kind === "text")
      .map((p) => p.text)
      .join(" ");
    console.log(`Output: ${text}`);
  }
}

Custom Fetch (Auth Headers, Edge Runtimes)

typescript
import { A2AClient } from "@reaatech/a2a-reference-client";
 
const client = new A2AClient({
  baseUrl: "http://localhost:3000",
  fetchImpl: (url, init) =>
    globalThis.fetch(url, {
      ...init,
      headers: {
        ...(init?.headers ?? {}),
        "x-api-key": process.env.A2A_API_KEY,
      },
    }),
});
 
const card = await client.getAgentCard();

Retry with Backoff

typescript
const client = new A2AClient({
  baseUrl: "http://localhost:3000",
  maxRetries: 3,
  retryDelayMs: 500,
});
 
// RPC calls automatically retry on 5xx and network errors
const task = await client.sendMessage({
  messageId: "retry-1",
  role: "user",
  parts: [{ kind: "text", text: "do work" }],
});

Multi-Agent Orchestration

typescript
const mathClient = new A2AClient({ baseUrl: "http://localhost:3001" });
const formatterClient = new A2AClient({ baseUrl: "http://localhost:3002" });
 
// Delegate to math agent
const mathTask = await mathClient.sendMessage({
  messageId: "math-1",
  role: "user",
  parts: [{ kind: "text", text: "2 + 2" }],
});
 
// Poll for result
let result = mathTask;
while (result.status.state !== "completed" && result.status.state !== "failed") {
  await sleep(100);
  result = await mathClient.getTask(mathTask.id);
}

Error Handling

The client throws typed errors from @reaatech/a2a-reference-core:

ErrorWhen
TaskNotFoundErrorServer returns “not found” for a task ID
UnsupportedOperationErrorStreaming requested but agent doesn’t support it
InvalidAgentResponseErrorServer response fails Zod validation

Retry behavior:

  • 5xx responses: retried with exponential backoff + jitter
  • 4xx responses: not retried (client error)
  • Network errors (ECONNREFUSED, fetch failed): retried
  • Timeout: 30-second AbortController per RPC call

License

MIT