The TypeScript SDK provides a Node.js interface for AgentAnycast. It communicates with the Go daemon over gRPC, giving your TypeScript/JavaScript agents full access to the P2P network.
npm install agentanycastThe daemon binary is automatically downloaded on install. Skip with AGENTANYCAST_SKIP_DOWNLOAD=1 if building from source.
With specific framework integrations:
npm install agentanycast # Core SDK- Node.js 18+ (ES2022 modules)
- The
agentanycastddaemon (auto-downloaded, or build from source)
import { Node, type AgentCard } from "agentanycast";
const card: AgentCard = {
name: "EchoAgent",
description: "Echoes back any message",
skills: [{ id: "echo", description: "Echo the input" }],
};
const node = new Node({ card });
await node.start();
console.log(`Peer ID: ${node.peerId}`);
node.onTask(async (task) => {
const text = task.messages.at(-1)?.parts[0]?.text ?? "";
await task.complete([{ parts: [{ text: `Echo: ${text}` }] }]);
});
await node.serveForever();const node = new Node({
card: { name: "Client", skills: [] },
home: "/tmp/agentanycast-client",
});
await node.start();
const handle = await node.sendTask(
{ role: "user", parts: [{ text: "Hello!" }] },
{ peerId: "12D3KooW..." },
);
const result = await handle.wait(30_000);
console.log(result.artifacts[0].parts[0].text); // "Echo: Hello!"
await node.stop();The Node class is the main entry point for the SDK.
const node = new Node({
card: AgentCard, // Required: agent capability descriptor
home?: string, // Data directory (default: ~/.agentanycast)
relay?: string, // Relay multiaddr for cross-network
daemonPath?: string, // Path to agentanycastd binary
daemonPort?: number, // gRPC port (default: auto)
});| Method | Description |
|---|---|
start() |
Launch daemon, connect gRPC, register card |
stop() |
Stop daemon and clean up |
sendTask(message, target) |
Send a task (by peerId, skill, or url) |
getCard(peerId) |
Fetch a remote agent's card |
discover(skill, options?) |
Find agents by skill with optional tag filtering |
onTask(handler) |
Register handler for incoming tasks |
serveForever() |
Block until stopped, processing incoming tasks |
| Property | Type | Description |
|---|---|---|
peerId |
string |
This node's libp2p Peer ID |
didKey |
string |
W3C did:key derived from the Ed25519 key |
// 1. Direct — by Peer ID
await node.sendTask(message, { peerId: "12D3KooW..." });
// 2. Anycast — by Skill
await node.sendTask(message, { skill: "translate" });
// 3. HTTP Bridge — by URL
await node.sendTask(message, { url: "https://agent.example.com" });// Find all agents with a specific skill
const agents = await node.discover("translate");
// Filter by tags
const frenchAgents = await node.discover("translate", {
tags: { lang: "fr" },
});
// Use the results
for (const agent of agents) {
console.log(`${agent.name} (${agent.peerId})`);
}interface AgentCard {
name: string;
description?: string;
version?: string;
protocolVersion?: string;
skills: Skill[];
// Read-only (populated by daemon):
peerId?: string;
supportedTransports?: string[];
relayAddresses?: string[];
didKey?: string; // W3C did:key
didWeb?: string; // did:web identifier
didDns?: string; // did:dns identifier
verifiableCredentials?: string[]; // JSON-encoded VCs
}
interface Skill {
id: string;
description?: string;
tags?: Record<string, string>;
inputSchema?: string; // JSON Schema
outputSchema?: string; // JSON Schema
}Returned by sendTask(), allows you to track task progress:
const handle = await node.sendTask(message, { peerId: "..." });
// Wait for completion (with timeout)
const result = await handle.wait(30_000);
// Access results
console.log(result.status); // "completed"
console.log(result.artifacts); // Artifact[]
console.log(result.messages); // Message[]Received by onTask() handler:
node.onTask(async (task) => {
// Read the incoming message
const lastMessage = task.messages.at(-1);
const text = lastMessage?.parts[0]?.text ?? "";
// Update status
await task.updateStatus("working");
// Complete with artifacts
await task.complete([
{
name: "result",
parts: [{ text: `Processed: ${text}` }],
},
]);
// Or fail
// await task.fail("Something went wrong");
// Or request more input
// await task.requestInput("Please provide more details");
});Convert between Peer IDs and W3C DIDs:
import {
peerIdToDIDKey,
didKeyToPeerId,
didWebToUrl,
urlToDidWeb,
} from "agentanycast";
// Peer ID ↔ did:key
const did = peerIdToDIDKey("12D3KooW..."); // "did:key:z6Mk..."
const peerId = didKeyToPeerId("did:key:z6Mk..."); // "12D3KooW..."
// did:web ↔ URL
const url = didWebToUrl("did:web:example.com:agents:myagent");
const didWeb = urlToDidWeb("https://example.com/agents/myagent");Bidirectional mapping between MCP tools and A2A skills:
import {
mcpToolToSkill,
skillToMcpTool,
mcpToolsToAgentCard,
} from "agentanycast";
// MCP Tool → A2A Skill
const skill = mcpToolToSkill(mcpTool);
// A2A Skill → MCP Tool
const tool = skillToMcpTool(skill);
// Convert all tools to an AgentCard
const card = mcpToolsToAgentCard(tools, {
name: "MyMCPAgent",
description: "Agent wrapping MCP tools",
});The SDK provides a hierarchy of error classes:
import {
AgentAnycastError, // Base error
ConnectionError, // Network/connection issues
DaemonError, // Daemon management issues
TaskError, // Task-related errors
TimeoutError, // Operation timeout
NotFoundError, // Resource not found
} from "agentanycast";
try {
const handle = await node.sendTask(message, { peerId: "..." });
const result = await handle.wait(10_000);
} catch (err) {
if (err instanceof TimeoutError) {
console.error("Task timed out");
} else if (err instanceof ConnectionError) {
console.error("Could not reach peer");
}
}| Variable | Description |
|---|---|
AGENTANYCAST_HOME |
Data directory (default: ~/.agentanycast) |
AGENTANYCAST_BOOTSTRAP_PEERS |
Relay multiaddr(s), comma-separated |
AGENTANYCAST_SKIP_DOWNLOAD |
Set to 1 to skip daemon auto-download |
AGENTANYCAST_DAEMON_PATH |
Path to agentanycastd binary |
const node = new Node({
card,
home: "/custom/data/dir",
relay: "/ip4/1.2.3.4/tcp/4001/p2p/12D3KooW...",
daemonPath: "/usr/local/bin/agentanycastd",
});The TypeScript SDK includes a CLI for quick experimentation:
agentanycast-ts demo # Start an echo agent
agentanycast-ts discover translate # Find agents by skill
agentanycast-ts send 12D3KooW... "Hello!" # Send a task
agentanycast-ts status # Check node status
agentanycast-ts info # Show version infoAll commands support --relay, --home, and --verbose flags. Install globally with npm install -g agentanycast or use npx agentanycast-ts <command>.
npm install # Install dependencies
npm run build # Compile TypeScript → dist/
npm test # Run tests (vitest)
npm run lint # ESLint
npm run clean # Remove dist/TypeScript and Python agents are fully interoperable — they use the same daemon and protocol. A TypeScript server can receive tasks from a Python client and vice versa:
// TypeScript server
const node = new Node({ card });
await node.start();
node.onTask(async (task) => {
// This handler works with both TypeScript and Python clients
const text = task.messages.at(-1)?.parts[0]?.text ?? "";
await task.complete([{ parts: [{ text: `Hello from TypeScript: ${text}` }] }]);
});# Python client sending to the TypeScript server
async with Node(card=card) as node:
handle = await node.send_task(
peer_id="12D3KooW...", # TypeScript agent's Peer ID
message={"role": "user", "parts": [{"text": "Hello!"}]},
)
result = await handle.wait()
print(result.artifacts[0].parts[0].text) # "Hello from TypeScript: Hello!"