SDK Reference
Package:
@cendriix/a2a— TypeScript SDK for the A2A Public API. Status: Pre-release. SDK repo (a2a-sdk-ts) created but not yet published.
A2AClient
import { A2AClient } from '@cendriix/a2a';
const client = new A2AClient(options);Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | — | Access Grant secret |
baseUrl | string | No | 'https://api.cendriix.ai' | API base URL |
timeout | number | No | 30_000 | Request timeout in milliseconds |
retries | number | No | 3 | Max automatic retries on transient errors |
retryDelay | number | No | 1_000 | Base delay between retries (exponential backoff) |
dpop | boolean | No | true | Enable DPoP proof generation |
fetch | typeof fetch | No | globalThis.fetch | Custom fetch implementation |
handshake()
Validate the connection and API key.
const result = await client.handshake();
// { ok: true, tenantId, grantId, capabilities, agentIds, expiresAt, serverVersion }invoke()
Invoke an agent synchronously. Waits for the task to reach a terminal state.
const task = await client.invoke(agentId, params, options?);Params
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | The task prompt or instruction |
parts | A2aPart[] | No | Structured content parts (overrides message) |
context | Record<string, unknown> | No | Additional context passed as data part |
sessionId | string | No | Group messages in a conversation session |
skillIds | string[] | No | Restrict to specific skills |
Options
| Field | Type | Default | Description |
|---|---|---|---|
timeout | number | 300_000 | Max wait time for completion (ms) |
idempotencyKey | string | Auto-generated UUID | Deduplication key |
signal | AbortSignal | — | Abort signal for cancellation |
Example
const task = await client.invoke('code-reviewer', {
message: 'Review PR #42 for security vulnerabilities',
context: { repository: 'acme/payments', prNumber: 42 },
});
if (task.status === 'COMPLETED') {
for (const artifact of task.artifacts) {
console.log(`[${artifact.name}]`, artifact.parts[0]?.text);
}
}stream()
Invoke an agent and stream events as an async iterator.
const stream = client.stream(agentId, params, options?);
for await (const event of stream) {
switch (event.type) {
case 'status':
console.log(event.event.status.state);
break;
case 'artifact':
console.log(event.event.artifact.name);
break;
case 'message':
console.log(event.message.parts[0]?.text);
break;
}
}Agent discovery
listAgents()
const agents = await client.listAgents();
// [{ id, slug, name, description, skills, visibility, inputModes, outputModes }]getAgentCard()
const card = await client.getAgentCard('code-reviewer');
// { card: { name, description, capabilities, skills }, signature, signedAt, keyId }getTask()
const task = await client.getTask(taskId);listTasks()
const { tasks, nextPageToken } = await client.listTasks({
agentId: 'code-reviewer',
status: 'COMPLETED',
pageSize: 50,
});cancelTask()
await client.cancelTask(taskId);Error types
| Class | Code | When |
|---|---|---|
AgentNotFoundError | -32010 | Agent slug/ID does not exist |
AgentDisabledError | -32011 | Agent is disabled |
UnauthorizedError | -32012 | Token expired or invalid |
ForbiddenError | -32013 | Insufficient capabilities |
QuotaExceededError | -32014 | Rate limit or budget exceeded |
CrossTenantRejectedError | -32015 | Cross-tenant invocation without consent |
TaskNotFoundError | -32001 | Task ID does not exist |
TaskNotCancelableError | -32002 | Task in terminal state |
import { A2AError, QuotaExceededError } from '@cendriix/a2a';
try {
await client.invoke('orchestrator', { message: '...' });
} catch (err) {
if (err instanceof QuotaExceededError) {
console.log(`Rate limited. Retry after ${err.data?.retryAfter}s`);
}
}React hooks
useA2A
import { useA2A } from '@cendriix/a2a/react';
function ReviewPanel({ prUrl }: { prUrl: string }) {
const { invoke, task, isLoading, error } = useA2A({
apiKey: process.env.NEXT_PUBLIC_CENDRIIX_API_KEY!,
});
return (
<div>
<button onClick={() => invoke('code-reviewer', { message: `Review ${prUrl}` })} disabled={isLoading}>
{isLoading ? 'Reviewing...' : 'Start Review'}
</button>
{error && <p>{error.message}</p>}
{task?.status === 'COMPLETED' && (
<pre>{task.artifacts[0]?.parts[0]?.text}</pre>
)}
</div>
);
}useA2AStream
import { useA2AStream } from '@cendriix/a2a/react';
const { startStream, artifacts, status, isStreaming } = useA2AStream({
apiKey: process.env.NEXT_PUBLIC_CENDRIIX_API_KEY!,
});Vercel integration
Edge Runtime
// app/api/review/route.ts
import { A2AClient } from '@cendriix/a2a';
export const runtime = 'edge';
export async function POST(request: Request) {
const { prUrl } = await request.json();
const client = new A2AClient({ apiKey: process.env.CENDRIIX_API_KEY! });
const task = await client.invoke('code-reviewer', {
message: `Review ${prUrl}`,
context: { prUrl },
});
return Response.json({ status: task.status, review: task.artifacts[0]?.parts[0]?.text });
}Server Actions
'use server';
import { A2AClient } from '@cendriix/a2a';
const client = new A2AClient({ apiKey: process.env.CENDRIIX_API_KEY! });
export async function analyzeCode(code: string) {
const task = await client.invoke('code-reviewer', {
message: 'Review this code for issues',
parts: [{ text: code, mediaType: 'text/plain' }],
});
return { status: task.status, findings: task.artifacts.map(a => ({ name: a.name, content: a.parts[0]?.text ?? '' })) };
}Last updated on