Content
[](https://www.npmjs.com/package/@artinet/sdk)
[](https://www.npmjs.com/package/@artinet/sdk)
Building interoperable AI agents. Learn more at [the artinet project](https://artinet.io/).
# Artinet SDK: A Production-Ready A2A Protocol Implementation
## Table of Contents
- [Why Choose Artinet SDK?](#why-choose-artinet-sdk)
- [Installation](#installation)
- [Features](#features)
- [Quick Start](#quick-start)
- [1. Server (`quick-server.ts`)](#1-server-quick-serverts)
- [2. Client (`quick-client.ts`)](#2-client-quick-clientts)
- [3. Run It](#3-run-it)
- [Client Usage](#client-usage)
- [Basic Client Usage](#basic-client-usage)
- [Streaming Updates](#streaming-updates)
- [Authentication](#authentication)
- [Server Usage](#server-usage)
- [Implementing an A2A Agent](#implementing-an-a2a-agent)
- [Persistent Storage](#persistent-storage)
- [Logging](#logging)
- [Advanced Server Customization](#advanced-server-customization)
- [Custom `createJSONRPCServer` Function](#custom-createjsonrpcserver-function)
- [Using the Custom Function](#using-the-custom-function)
- [API Reference](#api-reference)
- [Core Classes](#core-classes)
- [Key Types & Interfaces](#key-types--interfaces)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgements](#acknowledgements)
A robust, feature-rich TypeScript client and server library for the [Agent2Agent (A2A) Protocol](https://github.com/google/A2A) - an open protocol for communication between AI agents.
This SDK significantly enhances the foundational A2A concepts and samples provided by Google, offering a production-ready solution with a focus on developer experience, reliability, and comprehensive features.
## Why Choose Artinet SDK?
While the official A2A repository provides basic samples, the Artinet SDK is engineered for real-world applications:
- **Plug-and-Play Server:** Built on Express.js, the `A2AServer` handles JSON-RPC complexity, routing, protocol compliance, and streaming mechanics automatically. Just provide your core agent logic (`TaskHandler`) and basic configuration for a fully functional A2A endpoint with minimal boilerplate.
- **Enhanced Client:** Features refined error handling, flexible header management for authentication, and clear separation of concerns.
- **Comprehensive Testing:** Ensuring reliability and maintainability. Includes tests for core logic, streaming, error conditions, and edge cases.
- **Simplified Developer Experience:** Start quickly with clear TypeScript types, intuitive APIs, and minimal setup. The SDK handles the underlying protocol details, letting you focus _solely_ on your agent's unique capabilities.
- **Flexible Storage:** Offers built-in `InMemoryTaskStore` for development/testing and `FileStore` for persistent task storage, easily extensible for custom storage solutions.
- **Full Protocol Compliance:** Implements the complete A2A specification using the official JSON schema, ensuring interoperability.
## Installation
```bash
npm install @artinet/sdk
```
## Features
- Full implementation of the A2A Protocol using the official Google schema
- Strongly-typed TypeScript interfaces for all protocol structures
- **Client Features:**
- Reliable streaming support (`tasks/sendSubscribe`) with robust handling of Server-Sent Events (SSE) using `eventsource-parser`.
- Push notification configuration support (`tasks/pushNotification/set`, `tasks/pushNotification/get`).
- Built-in, informative error classes (`RpcError`).
- Easy-to-use methods for `agentCard` discovery and capability checking (`supports`).
- Flexible header management for authentication (`addHeader`, `setHeaders`).
- **Server Features:**
- Simplified Express.js server setup (`A2AServer`).
- Abstracted `TaskHandler` interface for easy integration of agent logic using async generators.
* Automatic handling of JSON-RPC request parsing, validation, and routing.
- Pluggable task storage (`InMemoryTaskStore`, `FileStore`, or custom).
- Full support for streaming responses and artifact updates.
- Standardized error handling (`A2AError`) mapped to JSON-RPC error codes.
- Configurable agent card and capabilities.
- **Developer Tools:**
- Integrated structured logging via `pino`, configurable levels (`configureLogger`).
- Exhaustive test suite (`jest`) ensuring high reliability.
## Quick Start
Get a basic A2A server running and interact with it using the client in just a few lines of code.
**1. Server (`quick-server.ts`)**
```typescript
import {
A2AServer,
TaskContext,
TaskHandler,
InMemoryTaskStore,
} from "@artinet/sdk";
const quickAgentLogic: TaskHandler = async function* (context: TaskContext) {
const userInput =
context.userMessage.parts[0].type === "text"
? context.userMessage.parts[0].text
: "";
yield {
state: "working",
message: { role: "agent", parts: [{ type: "text", text: "Thinking..." }] },
};
yield {
state: "completed",
message: {
role: "agent",
parts: [{ type: "text", text: `You said: ${userInput}` }],
},
};
};
// Configure and start the server
const server = new A2AServer({
taskHandler: quickAgentLogic,
taskStore: new InMemoryTaskStore(),
port: 4000,
basePath: "/a2a",
card: {
name: "QuickStart Agent",
url: "http://localhost:4000/a2a",
version: "0.1.0",
capabilities: { streaming: true },
skills: [{ id: "echo", name: "Echo Skill" }],
},
});
server.start();
```
**2. Client (`quick-client.ts`)**
```typescript
import { A2AClient } from "@artinet/sdk";
const client = new A2AClient("http://localhost:4000/a2a");
const message = {
role: "user" as const,
parts: [{ type: "text" as const, text: "Hello Quick Start!" }],
};
const stream = client.sendTaskSubscribe({ id: "quick-task-1", message });
//process the stream
runClient();
```
**3. Run It**
- Save the files above.
- Install the SDK: `npm install @artinet/sdk`
- Run the server: `npx tsx quick-server.ts`
- In another terminal, run the client: `npx tsx quick-client.ts`
You'll see the server start, the client send a message, and the server respond with status updates.
## Client Usage
Interact with A2A-compliant agents using the `A2AClient`.
### Basic Client Usage
Send a task and get the final result. Assumes the server exists and is compatible.
```typescript
import { A2AClient, Message, RpcError } from "@artinet/sdk";
async function runBasicTask() {
const client = new A2AClient("https://your-a2a-server.com/a2a");
const message: Message = {
role: "user",
parts: [{ type: "text", text: "What is the capital of France?" }],
};
const task = await client.sendTask({
id: "basic-task-1",
message,
});
}
runBasicTask();
```
### Streaming Updates
Receive real-time updates via Server-Sent Events (SSE).
```typescript
import {
A2AClient,
Message,
RpcError,
TaskStatusUpdateEvent,
TaskArtifactUpdateEvent,
} from "@artinet/sdk";
async function runStreamingTask() {
const client = new A2AClient("https://your-a2a-server.com/a2a");
const message: Message = {
role: "user",
parts: [{ type: "text", text: "Tell me a short story." }],
};
try {
const stream = client.sendTaskSubscribe({
id: "streaming-task-1",
message,
});
for await (const update of stream) {
if ((update as TaskStatusUpdateEvent).status) {
console.log(
"Received Status Update:",
(update as TaskStatusUpdateEvent).status.state
);
} else if ((update as TaskArtifactUpdateEvent).artifact) {
console.log(
"Received Artifact:",
(update as TaskArtifactUpdateEvent).artifact.name
);
}
}
console.log("Stream finished.");
} catch (error) {
console.error("A2A Streaming Error:", error);
}
}
runStreamingTask();
```
### Authentication
Add authentication headers before making requests.
```typescript
import { A2AClient } from "@artinet/sdk";
const client = new A2AClient("https://your-secure-a2a-server.com/a2a");
client.addHeader("Authorization", "Bearer your-api-token");
```
## Server Usage
Setting up an A2A-compliant agent server is remarkably straightforward with the Artinet SDK. The `A2AServer` class abstracts away the complexities of the A2A protocol and underlying JSON-RPC communication, allowing you to focus purely on implementing your agent's behavior.
### Implementing an A2A Agent
Define agent logic using an async generator `TaskHandler`.
```typescript
import {
A2AServer,
TaskContext,
TaskHandler,
InMemoryTaskStore,
} from "@artinet/sdk";
// Agent logic
const myAgentLogic: TaskHandler = async function* (context: TaskContext) {
const userInput =
context.userMessage.parts[0]?.type === "text"
? context.userMessage.parts[0].text
: "";
yield {
state: "working",
message: {
role: "agent",
parts: [{ type: "text", text: "Processing..." }],
},
};
if (context.isCancelled()) {
yield { state: "canceled" };
return;
}
// Example: Yield an artifact
yield {
name: "result.txt",
mimeType: "text/plain",
parts: [{ type: "text", text: `Report for: ${userInput}` }],
};
// Final result
yield {
state: "completed",
message: {
role: "agent",
parts: [{ type: "text", text: `Finished: ${userInput}` }],
},
};
};
// Server setup
const server = new A2AServer({
handler: myAgentLogic,
taskStore: new InMemoryTaskStore(),
port: 3000,
basePath: "/a2a",
card: {
name: "Example Agent",
url: "http://localhost:3000/a2a",
version: "1.0.0",
capabilities: { streaming: true },
skills: [{ id: "processor", name: "Text Processor" }],
},
});
server.start();
console.log("A2A Server running on port 3000");
```
### Persistent Storage
Use `FileStore` for simple persistence.
```typescript
import { A2AServer, FileStore } from "@artinet/sdk";
import path from "path";
import fs from "fs";
// Assume myAgentLogic is defined
const dataDir = path.join(process.cwd(), "a2a-data");
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
const store = new FileStore(dataDir);
const server = new A2AServer({
handler: myAgentLogic, // Your agent logic here
taskStore: store,
port: 3001,
basePath: "/a2a-persistent",
card: {
name: "Persistent Agent",
url: "http://localhost:3001/a2a-persistent",
version: "1.0.1",
capabilities: { streaming: true },
skills: [{ id: "persistent-skill", name: "Persistent Task Handler" }],
},
});
server.start();
console.log("Persistent A2A Server running on port 3001");
```
## Logging
Configure and use the built-in structured logger.
```typescript
import { logger, configureLogger, LogLevel } from "@artinet/sdk";
// Configure logging levels
configureLogger({
level: "debug", // Default is 'error'
// name: "MyA2AApp" // Optional name
});
// Use the logger
logger.info("Server started.");
logger.error({ err: new Error("Something failed") }, "Operation failed");
logger.debug({ taskId: "123" }, "Task status changed.");
// Child loggers for context
// const taskLogger = logger.child({ taskId: "abc" });
// taskLogger.info("Processing step 1");
```
## Advanced Server Customization
While the `A2AServer` provides a convenient setup using a default [Jayson](https://github.com/tedeh/jayson) JSON-RPC server, you might need more control for specific use cases, such as:
- Integrating A2A methods into an existing Express application with custom middleware.
- Adding non-A2A custom JSON-RPC methods alongside the standard A2A ones.
- Using a different JSON-RPC library or implementation.
The SDK allows you to provide your own function to create the JSON-RPC server instance via the `createJSONRPCServer` option in `A2AServerOptions`.
### Custom `createJSONRPCServer` Function
Implement the `JSONRPCServerFactory` interface.
```typescript
import {
CreateJSONRPCServerParams,
JSONRPCServerType,
JSONRPCServerFactory,
} from "@artinet/sdk";
import jayson from "jayson";
const myCustomCreateServer: JSONRPCServerFactory = (
params
): JSONRPCServerType => {
const jaysonServer = new jayson.Server({
"tasks/send": async (args, callback) => {
// Your custom implementation using params.taskStore, params.taskHandler etc.
callback(null, { id: args.id, status: { state: "submitted" } });
},
// ... implement other required A2A methods ...
myCustomMethod: (args, callback) => {
callback(null, { result: "custom success" });
},
});
return jaysonServer;
};
```
### Using the Custom Function
Pass your factory during `A2AServer` initialization.
```typescript
import { A2AServer, InMemoryTaskStore } from "@artinet/sdk";
const server = new A2AServer({
handler: myAgentLogic,
taskStore: new InMemoryTaskStore(),
port: 3002,
basePath: "/a2a-custom",
createJSONRPCServer: myCustomCreateServer, // Pass factory here
card: {
name: "Custom RPC Agent",
url: "http://localhost:3002/a2a-custom",
version: "1.1.0",
capabilities: { streaming: true },
skills: [{ id: "custom-skill", name: "Custom RPC Handler" }],
},
});
server.start(); // Uses your custom server setup
console.log("Custom A2A Server running on port 3002");
```
This provides maximum flexibility for integrating the A2A protocol handling into diverse server environments.
## API Reference
### Core Classes
- `A2AClient`: Client for interacting with A2A servers.
- `A2AServer`: Express-based server implementation.
- `RpcError`: Client-side A2A protocol errors.
- `A2AError`: Server-side A2A protocol errors (used internally).
- `InMemoryTaskStore`: Simple in-memory task persistence.
- `FileStore`: File-based task persistence.
### Key Types & Interfaces
- `TaskHandler`: Async generator function defining agent logic (`async function*(context: TaskContext): AsyncGenerator<TaskYieldUpdate>`).
- `TaskContext`: Provides task details (`taskId`, `userMessage`, `isCancelled`, `metadata`, `store`) to the `TaskHandler`.
- `TaskYieldUpdate`: Union type for updates yielded by `TaskHandler` (status changes or artifacts).
- `A2AServerOptions`: Configuration for `A2AServer` (port, store, card, basePath, etc.).
- `AgentCard`, `Message`, `Part`, `Artifact`, `Task`, `TaskStatus`, etc.: Types mirroring the A2A JSON Schema.
## Contributing
Contributions are welcome! Please open an issue or submit a Pull Request.
## License
This project is licensed under the MIT License - see the `LICENSE` file for details.
## Acknowledgements
This SDK builds upon the [official A2A Protocol schema](https://github.com/google/A2A/blob/main/samples/js/src/schema.ts) created by Google, providing a robust and developer-friendly implementation suitable for production environments.