Archived · v0.11.0
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 —
A2XAgentinfers AgentCard fields from your runtime objects. No manual JSON authoring. - Multi-version AgentCard — Generate v0.3 and v1.0 AgentCards from the same instance.
- 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.
- SSE streaming — First-class
message/streamsupport via Server-Sent Events. - 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 via the a2a-x402 v0.2 extension. On-chain verify + settle through any x402 facilitator.
- 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 { 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',
});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)
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,
A2XAgent,
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 A2XAgent (auto-extracts name, description, streaming from runtime)
const a2xAgent = new A2XAgent({ 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(a2xAgent);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 { A2XClient } from '@a2x/sdk/client';
const client = new A2XClient('https://agent.example.com/.well-known/agent.json');
// 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';
a2xAgent
.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 using the a2a-x402 v0.2 extension.
Install the optional peers:
npm install x402 viemServer:
import { X402PaymentExecutor, X402_EXTENSION_URI } from '@a2x/sdk/x402';
const executor = new X402PaymentExecutor(innerExecutor, {
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',
}],
});
const agent = new A2XAgent({ taskStore, executor })
.addExtension({ uri: X402_EXTENSION_URI, required: true });Client:
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.
AgentCard Versions
a2x handles the structural differences between A2A protocol versions transparently. Each agent is bound to one wire format at construction:
const a2xAgentV10 = new A2XAgent({ taskStore, executor }); // v1.0 (default)
const a2xAgentV03 = new A2XAgent({ taskStore, executor, protocolVersion: '0.3' }); // v0.3
a2xAgentV10.getAgentCard(); // v1.0 card
a2xAgentV03.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 | a2a-x402 v0.2 payments (server + client) |
Requirements
- Node.js >= 20
- TypeScript >= 5.6 (recommended)
Links
License
MIT