@craftpipe/a2a-framework
v1.0.0
Published
<p align="center"> <img src=".github/assets/banner.svg" alt="@craftpipe/a2a-framework banner" width="100%"/> </p>
Readme
@craftpipe/a2a-framework
Shared A2A (Agent-to-Agent) framework for building Craftpipe multi-agent systems. Provides an Express-based server, a typed client, and an in-memory task store — all wired up to a Craftpipe-flavored JSON-RPC 2.0 / A2A protocol.
Architecture
graph LR
A["Agent A<br/>(A2AClient)"] -->|"POST /tasks<br/>JSON-RPC 2.0"| B["A2A Framework<br/>(A2AServer)"]
B -->|"Task result"| A
B -->|"GET /tasks/:id/stream<br/>SSE"| A
B["A2A Framework<br/>(A2AServer)"] --> C["Agent B<br/>(skill handler)"]
C -->|"output"| BProtocol note: We implement a Craftpipe-flavored A2A protocol inspired by Google's A2A spec. Key deviation: capability names are used as JSON-RPC method names (not the canonical tasks/send) for simplicity and directness.
Features
- A2AServer — register skills, handle JSON-RPC requests, serve Agent Cards, SSE streaming, bearer auth, rate limiting
- A2AClient — discover agents, send tasks, stream SSE events, retry on 429/5xx
- InMemoryTaskStore — task lifecycle, TTL-based cleanup, thread-safe reads
- Full JSON-RPC 2.0 — structured errors (
-32601,-32602,-32001) - Pro skill gating —
PRO_LICENSEenv var required forpro: trueskills - SSE reconnection —
id:field on every event forLast-Event-ID
Installation
npm install @craftpipe/a2a-frameworkQuick Start
Build a server
import { A2AServer } from '@craftpipe/a2a-framework';
import { z } from 'zod';
const server = new A2AServer({
name: 'My Agent',
description: 'Does useful things',
version: '1.0.0',
port: 3000,
});
server.skill({
id: 'summarize',
name: 'Summarize',
description: 'Summarizes text',
tags: ['text', 'ai'],
inputSchema: z.object({ text: z.string(), maxWords: z.number().optional() }),
outputSchema: z.object({ summary: z.string() }),
handler: async (input, ctx) => {
const { text } = input as { text: string };
ctx.sendProgress('Analyzing text...');
// ... your logic
return { summary: 'The quick brown fox...' };
},
});
await server.listen();
console.log('Agent listening on port 3000');Use the client
import { A2AClient } from '@craftpipe/a2a-framework';
const client = new A2AClient('http://localhost:3000', {
token: process.env.AGENT_TOKEN,
timeout: 30000,
retries: 3,
});
// Discover capabilities
const card = await client.discover();
console.log(card.skills.map(s => s.id));
// Send a task
const task = await client.sendTask('summarize', { text: 'A long article...' });
console.log(task.output?.summary);
// Stream events
for await (const event of client.streamTask('summarize', { text: 'Another article' })) {
if (event.type === 'progress') console.log(event.message);
if (event.type === 'status' && event.status === 'completed') console.log(event.output);
}HTTP Endpoints
| Method | Path | Description |
|:-------|:-----|:------------|
| GET | /.well-known/agent.json | Agent Card — capabilities + auth info |
| POST | /tasks | Create task { jsonrpc: "2.0", method: "skill-id", params: {...} } |
| GET | /tasks/:id | Get task status and result |
| POST | /tasks/:id/cancel | Cancel a running task |
| POST | /tasks/:id/input | Respond to input-required task { data: {...} } |
| GET | /tasks/:id/stream | SSE stream with id: for reconnection |
| GET | /health | { status: "ok", uptime: N } |
Task Lifecycle
submitted → working → completed
→ failed
→ input-required → working → completed
→ canceledConfiguration
const server = new A2AServer({
name: 'My Agent',
description: 'Agent description',
version: '1.0.0',
port: 3000,
auth: { type: 'bearer', env_var: 'MY_AGENT_TOKEN' }, // optional
taskTtlMs: 3_600_000, // 1 hour (default)
maxConcurrentTasks: 100, // default
rateLimitPerMinute: 100, // default
});SSE Stream Format
id: 1
event: progress
data: {"task_id":"uuid","message":"Scanning PII fields..."}
id: 2
event: status
data: {"task_id":"uuid","status":"completed","output":{...}}Error Codes
| Code | Meaning |
|:-----|:--------|
| -32700 | Parse error (invalid JSON) |
| -32601 | Method not found (unknown skill) |
| -32602 | Invalid params (Zod validation failed) |
| -32001 | PRO_LICENSE required |
License
MIT — see LICENSE
