@a2x/sdk
v0.25.0
Published
A2A (Agent-to-Agent) protocol SDK for TypeScript
Maintainers
Readme
@a2x/sdk
A self-contained TypeScript SDK for building A2A (Agent-to-Agent) protocol agents with multi-provider LLM support, built-in authentication, and SSE streaming.
Why a2x?
- Auto-extraction —
A2XServerinfers AgentCard fields from your runtime objects. No manual JSON authoring. - Multi-version AgentCard — Generate v0.3 and v1.0 AgentCards from the same instance. A v1.0 server accepts the v1.0 JSON-RPC method names (
SendMessage,GetTask, …), theA2A-Extensionsheader, andA2A-Versionpinning, while still serving v0.3-speaking clients. - Multi-provider — Anthropic Claude, OpenAI GPT, and Google Gemini out of the box.
- Framework-agnostic — Works with Express, Fastify, Hono, Next.js, or any HTTP framework.
- JSON-RPC and HTTP+JSON transports — First-class A2A v1.0 client and server bindings with deterministic AgentCard selection, REST resources, structured errors, and SSE streaming.
- SSE streaming — First-class
message/streamsupport via Server-Sent Events. - Multi-modal artifacts — Agents can yield
text,file, anddataevents; the default executor maps each into A2ATextPart/FilePart/DataPartartifacts. - Built-in auth — API Key, Bearer, OAuth 2.0 (Authorization Code, Client Credentials, Device Code), OpenID Connect, and Mutual TLS.
- x402 payments — Charge per call with on-chain cryptocurrency payments, supporting both x402 protocol versions (legacy V1 and the x402 Foundation V2 transport) — each deployment speaks one, V1 by default, V2 via
new X402Context({ x402Version: 2 }). Compose the@a2x/sdk/x402mechanics directly, or opt into its host-neutralMerchantGatefor shared exact,upto, andbatch-settlementpricing, metering, frozen terms, lifecycle-aware replay protection, and explicit buffered or progressive delivery timing. - Usage-based payments — Native support for the x402 V2
uptoscheme: the payer signs a Permit2 authorization up to a maximum, the agent meters the work and settles only the actual charge withsettle(ctx, classified, { amountAtomic }), clamped SDK-side so a metering bug can never overcharge. Bill by LLM tokens instead of a flat fee. - Conversation-scoped payments —
UptoSessionManagerholds oneuptoauthorization across an A2A context, accumulates trusted usage over concurrent turns, and settles once on idle, deadline, budget, or host shutdown. CAS-backed opening reservations and turn leases claim each turn before resource work starts, preventing duplicate settlement and unmetered race losers. - Batched payments — Native payer and merchant support for the x402 V2
batch-settlementscheme: fund an on-chain channel once, then pay per call with an off-chain voucher. Merchant verification, reservation cancellation, metered commit, refund bypass, and recovery receipts run through the official x402 resource-server lifecycle. - Zero runtime dependencies — Core module uses only Node.js built-in APIs.
- TypeScript-first — Full type safety with types derived from A2A JSON Schema.
Installation
npm install @a2x/sdkInstall the LLM provider SDK you plan to use:
# Pick one (or more)
npm install @google/genai # Google Gemini
npm install @anthropic-ai/sdk # Anthropic Claude
npm install openai # OpenAI GPTQuick Start
import { A2A_TRANSPORTS, LlmAgent, toA2x } from '@a2x/sdk';
import { GoogleProvider } from '@a2x/sdk/google';
const agent = new LlmAgent({
name: 'my_assistant',
description: 'A helpful assistant.',
instruction: 'You are a helpful assistant.',
provider: new GoogleProvider({
model: 'gemini-2.5-flash',
apiKey: process.env.GOOGLE_API_KEY!,
}),
});
const app = toA2x(agent, {
port: 4000,
defaultUrl: 'http://localhost:4000/a2a',
transports: [A2A_TRANSPORTS.JSONRPC, A2A_TRANSPORTS.HTTP_JSON],
});This starts an A2A-compliant server with:
GET /.well-known/agent.json— Agent discoveryPOST /a2a— JSON-RPC endpoint (message/send,message/stream,tasks/get,tasks/cancel)/a2a/message:send,/a2a/message:stream, and/a2a/tasks/*— v1.0 HTTP+JSON routes whenHTTP+JSONis configured
Providers
Google Gemini
import { GoogleProvider } from '@a2x/sdk/google';
const provider = new GoogleProvider({
model: 'gemini-2.5-flash',
apiKey: process.env.GOOGLE_API_KEY!,
});Anthropic Claude
import { AnthropicProvider } from '@a2x/sdk/anthropic';
const provider = new AnthropicProvider({
model: 'claude-sonnet-4-20250514',
apiKey: process.env.ANTHROPIC_API_KEY!,
});OpenAI GPT
import { OpenAIProvider } from '@a2x/sdk/openai';
const provider = new OpenAIProvider({
model: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY!,
});Server Setup (Manual Wiring)
For full control over routing and middleware:
import {
LlmAgent,
InMemoryRunner,
AgentExecutor,
StreamingMode,
InMemoryTaskStore,
A2XServer,
DefaultRequestHandler,
createSSEStream,
} from '@a2x/sdk';
import type { RequestContext } from '@a2x/sdk';
import { GoogleProvider } from '@a2x/sdk/google';
// 1. Define your agent
const agent = new LlmAgent({
name: 'my_agent',
description: 'My A2A agent.',
instruction: 'You are a helpful assistant.',
provider: new GoogleProvider({
model: 'gemini-2.5-flash',
apiKey: process.env.GOOGLE_API_KEY!,
}),
});
// 2. Wire up the runtime
const runner = new InMemoryRunner({ agent, appName: agent.name });
const executor = new AgentExecutor({
runner,
runConfig: { streamingMode: StreamingMode.SSE },
});
const taskStore = new InMemoryTaskStore();
// 3. Create A2XServer (auto-extracts name, description, streaming from runtime)
const a2xServer = new A2XServer({ taskStore, executor })
.setDefaultUrl('https://my-agent.example.com/a2a')
.addSkill({
id: 'chat',
name: 'Chat',
description: 'General conversation',
tags: ['chat'],
});
// 4. Create the request handler
const handler = new DefaultRequestHandler(a2xServer);Express
import express from 'express';
const app = express();
app.use(express.json());
app.get('/.well-known/agent.json', (req, res) => {
res.json(handler.getAgentCard());
});
app.post('/a2a', async (req, res) => {
const context: RequestContext = { headers: req.headers, query: req.query };
const result = await handler.handle(req.body, context);
if (result && typeof result === 'object' && Symbol.asyncIterator in result) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
const stream = createSSEStream(result);
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(typeof value === 'string' ? value : new TextDecoder().decode(value));
}
res.end();
} else {
res.json(result);
}
});
app.listen(4000);Next.js App Router
export async function GET() {
return Response.json(handler.getAgentCard());
}
export async function POST(request: Request) {
const body = await request.json();
const result = await handler.handle(body);
if (result && typeof result === 'object' && Symbol.asyncIterator in result) {
const stream = createSSEStream(result);
return new Response(stream, {
headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },
});
}
return Response.json(result);
}Client
import { A2A_TRANSPORTS, A2XClient } from '@a2x/sdk/client';
const client = new A2XClient('https://agent.example.com/.well-known/agent.json', {
// Default order is JSONRPC, then HTTP+JSON.
preferredTransports: [
A2A_TRANSPORTS.HTTP_JSON,
A2A_TRANSPORTS.JSONRPC,
],
});
// Send a message
const task = await client.sendMessage({
message: { role: 'user', parts: [{ text: 'Hello!' }] },
});
// Stream a response
for await (const event of client.sendMessageStream({
message: { role: 'user', parts: [{ text: 'Tell me a story' }] },
})) {
console.log(event);
}
// Task management
const existing = await client.getTask('task-id');
const canceled = await client.cancelTask('task-id');Tools
FunctionTool
import { FunctionTool } from '@a2x/sdk';
const weatherTool = new FunctionTool({
name: 'get_weather',
description: 'Get weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
},
required: ['location'],
},
execute: async ({ location }) => {
return { temp: 72, condition: 'sunny', location };
},
});
const agent = new LlmAgent({
name: 'weather_bot',
description: 'Weather assistant',
instruction: 'Use the get_weather tool to answer weather questions.',
provider,
tools: [weatherTool],
});AgentTool
Use another agent as a callable tool:
import { AgentTool } from '@a2x/sdk';
const researchAgent = new LlmAgent({ /* ... */ });
const mainAgent = new LlmAgent({
name: 'orchestrator',
description: 'Orchestrates sub-agents',
instruction: 'Delegate research tasks to the research agent.',
provider,
tools: [new AgentTool({ agent: researchAgent })],
});Agent Patterns
| Pattern | Description |
|---|---|
| LlmAgent | Single LLM-powered agent |
| SequentialAgent | Pipeline of agents executed in order |
| ParallelAgent | Agents executed concurrently |
| LoopAgent | Iterative refinement until exit condition |
Authentication
import { ApiKeyAuthorization, HttpBearerAuthorization } from '@a2x/sdk';
a2xServer
.addSecurityScheme('apiKey', new ApiKeyAuthorization({
in: 'header',
name: 'x-api-key',
keys: ['your-secret-key'],
}))
.addSecurityScheme('bearer', new HttpBearerAuthorization({
validator: async (token) => {
const valid = token === process.env.AUTH_TOKEN;
return { authenticated: valid };
},
}))
// OR logic: either scheme satisfies auth
.addSecurityRequirement({ apiKey: [] })
.addSecurityRequirement({ bearer: [] });Supported schemes: ApiKeyAuthorization, HttpBearerAuthorization, OAuth2AuthorizationCodeAuthorization, OAuth2ClientCredentialsAuthorization, OAuth2DeviceCodeAuthorization, OpenIdConnectAuthorization, MutualTlsAuthorization.
x402 Payments
Gate agent calls behind on-chain cryptocurrency payments. A2X supports both x402 protocol versions — legacy V1 (a2a-x402 v0.2) and the x402 Foundation V2 transport. A server speaks the one its deployment configures (V1 by default; new X402Context({ x402Version: 2 }) for V2), and the client signs whichever version it receives.
Install the optional peers:
npm install @x402/core @x402/evm viemUse @x402/core and @x402/evm >=2.20.0 <3.
Server (the agent owns the flow; X402Context bundles the offering store + facilitator + event builders into one object):
import { AgentExecutor, BaseAgent, StreamingMode } from '@a2x/sdk';
import { X402Context, X402_FOUNDATION_EXTENSION_URI } from '@a2x/sdk/x402';
const ACCEPTS = [{
network: 'base-sepolia',
amount: '10000', // 0.01 USDC (6 decimals)
asset: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // USDC on Base Sepolia
payTo: process.env.MERCHANT_ADDRESS!,
resource: 'https://api.example.com/premium',
description: 'Premium agent access',
}];
class PaidAgent extends BaseAgent {
constructor(private readonly x402: X402Context) {
super({ name: 'paid_agent' });
}
async *run(ctx) {
const result = await this.x402.classify(ctx);
switch (result.kind) {
case 'no-submission':
yield* this.x402.requestPayment(ctx, { accepts: ACCEPTS, expiresInSeconds: 600 });
return;
case 'rejected':
case 'no-stored-offering':
case 'unmatched':
case 'invalid-shape':
yield this.x402.failedEvent({ code: result.code, reason: result.reason });
return;
case 'valid':
break;
}
const verify = await this.x402.verify(ctx, result);
if (!verify.isValid) {
yield this.x402.failedEvent({
code: 'VERIFY_FAILED',
reason: verify.invalidReason ?? 'Payment verification failed.',
});
return;
}
// [insert any custom logic between verify and settle]
const receipt = await this.x402.settle(ctx, result);
if (!receipt.success) {
yield this.x402.failedEvent({
code: 'SETTLEMENT_FAILED',
reason: receipt.errorReason ?? 'Settlement failed.',
failureReceipt: receipt,
});
return;
}
await this.x402.clearOffering(ctx);
yield { type: 'text', role: 'agent', text: 'thanks for paying' };
yield this.x402.completedEvent({ receipt });
}
}
const x402 = new X402Context();
const executor = new AgentExecutor({
runner,
runConfig: { streamingMode: StreamingMode.SSE },
});
const agent = new A2XServer({ taskStore, executor })
.addExtension({ uri: X402_FOUNDATION_EXTENSION_URI, required: true });For deployments that need full bespoke control (multiple facilitators, custom store routing, inserting logic mid-validation), the lower-level stateless helpers X402Context is built on (parseX402PaymentSubmission, pickX402Requirement, validateX402PayloadShape, buildX402Payment*Metadata, …) remain exported.
For a reusable merchant flow, import MerchantGate from @a2x/sdk/x402. It returns request-payment, refuse, handled, and proceed outcomes instead of SDK events, so a BaseAgent.run() generator and a custom executor can render the same policy differently. Rates, paid/free selection, exact settlement timing, delivery timing, and missing-usage behavior remain required host configuration. authorizeDelivery() records publication before a host emits buffered or provisional content; if work later fails, abort() settles anything already delivered. Its batch-settlement path uses a configured x402 resource server for reservation, cancellation, refund bypass, and metered commit. UptoSessionManager optionally holds one upto authorization across a conversation and settles accumulated usage once; session delivery is necessarily progressive. Multi-replica hosts must inject durable lifecycle, offer, and session stores with atomic compare-and-set operations. See the x402 guide for the complete examples.
Client (unchanged):
import { A2XClient } from '@a2x/sdk/client';
import { privateKeyToAccount } from 'viem/accounts';
const client = new A2XClient(url, {
x402: { signer: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) },
});
const task = await client.sendMessage({ message: { role: 'user', parts: [{ text: '...' }] } });Full guide: docs/guides/advanced/x402-payments.md.
Migration from x402PaymentHook (SDK 0.13.x): docs/guides/advanced/migration-x402-v2.md.
AgentCard Versions
a2x handles the structural differences between A2A protocol versions transparently. Each agent is bound to one wire format at construction:
const a2xServerV10 = new A2XServer({ taskStore, executor }); // v1.0 (default)
const a2xServerV03 = new A2XServer({ taskStore, executor, protocolVersion: '0.3' }); // v0.3
a2xServerV10.getAgentCard(); // v1.0 card
a2xServerV03.getAgentCard(); // v0.3 card| Field | v0.3 | v1.0 |
|---|---|---|
| URL | AgentCard.url | supportedInterfaces[].url |
| Transport | preferredTransport | supportedInterfaces[].protocolBinding |
| Security | security + securitySchemes | securityRequirements + securitySchemes |
Exports
| Path | Description |
|---|---|
| @a2x/sdk | Core SDK (agents, tools, runner, transport, types) |
| @a2x/sdk/client | A2XClient for calling remote A2A agents |
| @a2x/sdk/auth | DeviceFlowClient for OAuth 2.0 Device Code flow |
| @a2x/sdk/anthropic | AnthropicProvider |
| @a2x/sdk/openai | OpenAIProvider |
| @a2x/sdk/google | GoogleProvider |
| @a2x/sdk/x402 | x402 payments, V1 + V2 (server, client, and optional merchant-policy composition) |
Requirements
- Node.js >= 22.11
- TypeScript >= 5.6 (recommended)
Links
License
MIT
