@a2a-relay/server
v0.2.0
Published
Build A2A-compliant agents in minutes
Maintainers
Readme
@a2a-relay/server
Build A2A-compliant agents in minutes. Define skills, start the server, done.
Part of Agent Relay — the open infrastructure for the agent economy.
Install
npm install @a2a-relay/serverQuick Start
import { AgentRelay } from '@a2a-relay/server';
const relay = new AgentRelay({
name: 'My Agent',
description: 'An agent that does useful things',
version: '0.1.0',
});
relay.skill({
id: 'greet',
name: 'Greeting',
description: 'Say hello',
tags: ['hello', 'greeting'],
handler: async (ctx) => `Hello! You said: "${ctx.text}"`,
});
relay.listen(3000);That's it. Your agent is live with:
- Agent Card at
/.well-known/agent-card.json(A2A discovery) - JSON-RPC endpoint at
/a2a/jsonrpc(A2A standard) - REST endpoint at
/a2a/rest/message/send(simpler alternative) - Health check at
/health
Features
- 🔌 A2A Protocol — fully compliant with the Agent-to-Agent protocol
- ⚡ Skill-based routing — register multiple skills, messages get routed automatically
- 🔐 Built-in auth — bearer token validation out of the box
- 📋 Auto-generated Agent Card — skills become discoverable metadata
- 🛠️ Express under the hood — add custom middleware, mount on existing apps
Multiple Skills
const relay = new AgentRelay({
name: 'Car Expert',
description: 'Automotive diagnostics and advice',
version: '0.1.0',
});
relay.skill({
id: 'diagnose',
name: 'Engine Diagnostics',
description: 'Describe symptoms, get likely causes',
tags: ['engine', 'diagnostics', 'repair'],
handler: async (ctx) => {
// Your logic here — call an LLM, query a database, anything
return `Based on "${ctx.text}", the likely cause is...`;
},
});
relay.skill({
id: 'estimate',
name: 'Repair Estimate',
description: 'Get a rough cost estimate for repairs',
tags: ['cost', 'estimate', 'repair'],
handler: async (ctx) => ({
text: 'Estimated repair cost: $200-400',
data: { minCost: 200, maxCost: 400, currency: 'USD' },
}),
});
relay.listen(3000);Authentication
const relay = new AgentRelay({
name: 'Private Agent',
description: 'Only authorized agents can talk to me',
version: '0.1.0',
auth: {
type: 'bearer',
tokens: ['secret-token-1', 'secret-token-2'],
},
});
// Or use a custom validator
const relay = new AgentRelay({
name: 'Private Agent',
description: 'Custom auth logic',
version: '0.1.0',
auth: {
type: 'bearer',
validator: async (token) => {
// Check against your database, JWT, etc.
return token === process.env.API_KEY;
},
},
});Returning Data
Skill handlers can return a string or a structured result:
// Simple text response
handler: async (ctx) => `Hello, ${ctx.text}!`
// Structured response with text + data
handler: async (ctx) => ({
text: 'Here are the results',
data: { items: [...], count: 42 },
})
// File response
handler: async (ctx) => ({
text: 'Generated your report',
file: {
name: 'report.pdf',
mimeType: 'application/pdf',
bytes: base64EncodedContent,
},
})Auto-Registration
Register with an agent registry automatically on startup:
const relay = new AgentRelay({
name: 'My Agent',
description: 'Does useful things',
version: '0.1.0',
url: 'https://my-agent.example.com', // Your public URL
registry: 'https://registry.agent-relay.dev',
});The agent registers when it starts, sends periodic heartbeats (every 5 min by default), and unregisters on graceful shutdown.
// Multiple registries
registry: ['https://registry1.com', 'https://registry2.com']
// Custom heartbeat interval (ms), or 0 to disable
registryHeartbeat: 600_000 // 10 minutes
// Manual registration
await relay.register('https://another-registry.com');
await relay.unregister('https://another-registry.com');Custom Middleware
Access the underlying Express app for custom routes or middleware:
const relay = new AgentRelay({ name: 'My Agent', description: '...', version: '0.1.0' });
const app = relay.getApp();
app.use(cors());
app.get('/custom', (req, res) => res.json({ hello: 'world' }));
relay.listen(3000);API
new AgentRelay(config)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| name | string | required | Agent name |
| description | string | required | What your agent does |
| version | string | required | Semantic version |
| port | number | 3000 | Listen port |
| url | string | — | Public URL (for Agent Card) |
| provider | { organization, url? } | — | Who runs this agent |
| auth | AuthConfig | — | Authentication config |
| streaming | boolean | false | Enable streaming |
| registry | string \| string[] | — | Registry URL(s) to auto-register with |
| registryHeartbeat | number | 300000 | Re-registration interval (ms), 0 to disable |
relay.skill(definition)
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| id | string | ✅ | Unique skill ID |
| name | string | ✅ | Human-readable name |
| description | string | ✅ | What this skill does |
| tags | string[] | — | Searchable tags |
| examples | string[] | — | Example queries |
| handler | (ctx) => Promise<string \| SkillResult> | ✅ | Your logic |
relay.listen(port?)
Start the server. Returns a Promise that resolves when listening.
relay.stop()
Stop the server gracefully.
relay.getApp()
Get the Express app instance.
Related
@a2a-relay/client— discover and talk to A2A agents- Agent Relay — the full project
- A2A Protocol — the underlying standard
License
MIT
