Examples

Explore real-world use cases and implementations of the A2A Node SDK. These examples demonstrate practical applications and best practices for building A2A agents.

Quick Examples

Basic Client-Server Communication

A simple example showing how to set up a client and server for basic communication.

Server Implementation

server.ts
import { A2AServer, DefaultAgentExecutor } from '@dexwox-labs/a2a-node';
 
// Create a simple echo agent
const executor = new DefaultAgentExecutor({
  async executeTask(task) {
    const { name, input } = task;
    
    if (name === 'echo') {
      return {
        status: 'completed',
        result: {
          message: `Echo: ${input.message}`,
          timestamp: new Date().toISOString()
        },
        artifacts: [{
          id: `echo-${Date.now()}`,
          type: 'text',
          content: { text: `Echo: ${input.message}` },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }]
      };
    }
    
    throw new Error(`Unknown task: ${name}`);
  }
});
 
// Create and start server
const server = new A2AServer({
  agentCard: {
    id: 'echo-agent',
    name: 'Echo Agent',
    description: 'Simple agent that echoes messages',
    capabilities: ['echo', 'text-processing'],
    endpoint: 'http://localhost:3000',
    version: '1.0.0'
  },
  executor
});
 
await server.start(3000);
console.log('🚀 Echo agent running on http://localhost:3000');

Client Implementation

client.ts
import { AgentClient, TaskClient } from '@dexwox-labs/a2a-node';
 
async function main() {
  // Create clients
  const agentClient = new AgentClient({
    baseUrl: 'http://localhost:3000'
  });
  
  const taskClient = new TaskClient({
    baseUrl: 'http://localhost:3000'
  });
  
  try {
    // Discover the agent
    const agents = await agentClient.resolveAgents();
    console.log('Found agents:', agents.map(a => a.name));
    
    // Create a task
    const task = await taskClient.createTask({
      name: 'echo',
      description: 'Echo a message',
      input: {
        message: 'Hello, A2A World!'
      }
    });
    
    console.log('Task created:', task.id);
    
    // Wait for completion
    let taskStatus = await taskClient.getTask(task.id);
    while (taskStatus.status === 'submitted' || taskStatus.status === 'working') {
      await new Promise(resolve => setTimeout(resolve, 1000));
      taskStatus = await taskClient.getTask(task.id);
    }
    
    console.log('Task completed:', taskStatus.result);
  } catch (error) {
    console.error('Error:', error.message);
  }
}
 
main();

Real-World Examples

1. Weather Agent

A comprehensive weather agent that provides current weather and forecasts.

This example demonstrates API integration, error handling, and structured responses.

weather-agent.ts
import { A2AServer, AgentExecutor, Task, TaskResult } from '@dexwox-labs/a2a-node';
import axios from 'axios';
 
interface WeatherData {
  location: string;
  temperature: number;
  condition: string;
  humidity: number;
  windSpeed: number;
}
 
interface ForecastData {
  location: string;
  days: Array<{
    date: string;
    temperature: { min: number; max: number };
    condition: string;
    precipitation: number;
  }>;
}
 
class WeatherAgentExecutor implements AgentExecutor {
  private apiKey: string;
  
  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }
  
  async executeTask(task: Task): Promise<TaskResult> {
    const { name, input } = task;
    
    try {
      switch (name) {
        case 'get-current-weather':
          return await this.getCurrentWeather(input);
        case 'get-forecast':
          return await this.getForecast(input);
        case 'get-weather-alerts':
          return await this.getWeatherAlerts(input);
        default:
          throw new Error(`Unsupported task: ${name}`);
      }
    } catch (error) {
      return {
        status: 'failed',
        error: error.message
      };
    }
  }
  
  private async getCurrentWeather(input: any): Promise<TaskResult> {
    const { location } = input;
    
    if (!location) {
      throw new Error('Location is required');
    }
    
    // Call weather API
    const response = await axios.get(
      `https://api.openweathermap.org/data/2.5/weather`,
      {
        params: {
          q: location,
          appid: this.apiKey,
          units: 'metric'
        }
      }
    );
    
    const data = response.data;
    const weatherData: WeatherData = {
      location: data.name,
      temperature: Math.round(data.main.temp),
      condition: data.weather[0].description,
      humidity: data.main.humidity,
      windSpeed: data.wind.speed
    };
    
    return {
      status: 'completed',
      result: weatherData,
      artifacts: [
        {
          id: `weather-${Date.now()}`,
          type: 'json',
          content: { data: weatherData },
          metadata: {
            source: 'OpenWeatherMap',
            timestamp: new Date().toISOString()
          },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private async getForecast(input: any): Promise<TaskResult> {
    const { location, days = 5 } = input;
    
    if (!location) {
      throw new Error('Location is required');
    }
    
    if (days < 1 || days > 16) {
      throw new Error('Days must be between 1 and 16');
    }
    
    // Call forecast API
    const response = await axios.get(
      `https://api.openweathermap.org/data/2.5/forecast`,
      {
        params: {
          q: location,
          appid: this.apiKey,
          units: 'metric',
          cnt: days * 8 // 8 forecasts per day (3-hour intervals)
        }
      }
    );
    
    const data = response.data;
    const forecastData: ForecastData = {
      location: data.city.name,
      days: this.processForecastData(data.list, days)
    };
    
    return {
      status: 'completed',
      result: forecastData,
      artifacts: [
        {
          id: `forecast-${Date.now()}`,
          type: 'json',
          content: { data: forecastData },
          metadata: {
            source: 'OpenWeatherMap',
            timestamp: new Date().toISOString()
          },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private async getWeatherAlerts(input: any): Promise<TaskResult> {
    const { location } = input;
    
    // Simulate weather alerts (in real implementation, use weather alerts API)
    const alerts = [
      {
        id: 'alert-1',
        type: 'warning',
        title: 'Heavy Rain Warning',
        description: 'Heavy rainfall expected in the next 6 hours',
        severity: 'moderate',
        startTime: new Date().toISOString(),
        endTime: new Date(Date.now() + 6 * 60 * 60 * 1000).toISOString()
      }
    ];
    
    return {
      status: 'completed',
      result: { location, alerts },
      artifacts: [
        {
          id: `alerts-${Date.now()}`,
          type: 'json',
          content: { alerts },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private processForecastData(forecastList: any[], days: number) {
    const dailyData = new Map();
    
    forecastList.forEach(item => {
      const date = item.dt_txt.split(' ')[0];
      
      if (!dailyData.has(date)) {
        dailyData.set(date, {
          date,
          temperatures: [],
          conditions: [],
          precipitation: 0
        });
      }
      
      const dayData = dailyData.get(date);
      dayData.temperatures.push(item.main.temp);
      dayData.conditions.push(item.weather[0].description);
      dayData.precipitation += item.rain?.['3h'] || 0;
    });
    
    return Array.from(dailyData.values())
      .slice(0, days)
      .map(day => ({
        date: day.date,
        temperature: {
          min: Math.round(Math.min(...day.temperatures)),
          max: Math.round(Math.max(...day.temperatures))
        },
        condition: day.conditions[0], // Use first condition
        precipitation: Math.round(day.precipitation * 10) / 10
      }));
  }
}
 
// Create and start the weather agent
const weatherAgent = new A2AServer({
  agentCard: {
    id: 'weather-agent-v2',
    name: 'Weather Information Agent',
    description: 'Provides current weather, forecasts, and alerts',
    version: '2.0.0',
    capabilities: [
      'weather-current',
      'weather-forecast',
      'weather-alerts',
      'location-services'
    ],
    endpoint: 'http://localhost:3001',
    metadata: {
      author: 'Weather Corp',
      license: 'MIT',
      supportedRegions: ['global']
    },
    supportedTasks: [
      {
        name: 'get-current-weather',
        description: 'Get current weather for a location',
        inputSchema: {
          type: 'object',
          properties: {
            location: { type: 'string', description: 'Location name or coordinates' }
          },
          required: ['location']
        }
      },
      {
        name: 'get-forecast',
        description: 'Get weather forecast for a location',
        inputSchema: {
          type: 'object',
          properties: {
            location: { type: 'string', description: 'Location name or coordinates' },
            days: { type: 'number', minimum: 1, maximum: 16, default: 5 }
          },
          required: ['location']
        }
      },
      {
        name: 'get-weather-alerts',
        description: 'Get weather alerts for a location',
        inputSchema: {
          type: 'object',
          properties: {
            location: { type: 'string', description: 'Location name or coordinates' }
          },
          required: ['location']
        }
      }
    ]
  },
  executor: new WeatherAgentExecutor(process.env.OPENWEATHER_API_KEY!)
});
 
await weatherAgent.start(3001);
console.log('🌤️ Weather agent running on http://localhost:3001');

2. File Processing Agent

An agent that processes various file types and extracts information.

file-processor-agent.ts
import { A2AServer, AgentExecutor, Task, TaskResult } from '@dexwox-labs/a2a-node';
import { promises as fs } from 'fs';
import path from 'path';
import csv from 'csv-parser';
import * as XLSX from 'xlsx';
 
class FileProcessorExecutor implements AgentExecutor {
  private tempDir: string;
  
  constructor() {
    this.tempDir = path.join(process.cwd(), 'temp');
    this.ensureTempDir();
  }
  
  private async ensureTempDir() {
    try {
      await fs.mkdir(this.tempDir, { recursive: true });
    } catch (error) {
      // Directory already exists
    }
  }
  
  async executeTask(task: Task): Promise<TaskResult> {
    const { name, input } = task;
    
    try {
      switch (name) {
        case 'process-csv':
          return await this.processCsv(input);
        case 'process-excel':
          return await this.processExcel(input);
        case 'extract-text':
          return await this.extractText(input);
        case 'analyze-file':
          return await this.analyzeFile(input);
        default:
          throw new Error(`Unsupported task: ${name}`);
      }
    } catch (error) {
      return {
        status: 'failed',
        error: error.message
      };
    }
  }
  
  private async processCsv(input: any): Promise<TaskResult> {
    const { fileContent, options = {} } = input;
    
    if (!fileContent) {
      throw new Error('File content is required');
    }
    
    // Save content to temp file
    const tempFile = path.join(this.tempDir, `csv-${Date.now()}.csv`);
    await fs.writeFile(tempFile, fileContent);
    
    try {
      const rows: any[] = [];
      
      return new Promise((resolve, reject) => {
        require('fs').createReadStream(tempFile)
          .pipe(csv(options))
          .on('data', (row: any) => rows.push(row))
          .on('end', () => {
            const result = {
              rowCount: rows.length,
              columns: Object.keys(rows[0] || {}),
              data: rows.slice(0, 100), // Limit to first 100 rows
              summary: this.generateCsvSummary(rows)
            };
            
            resolve({
              status: 'completed',
              result,
              artifacts: [
                {
                  id: `csv-analysis-${Date.now()}`,
                  type: 'json',
                  content: { analysis: result },
                  createdAt: new Date().toISOString(),
                  updatedAt: new Date().toISOString()
                }
              ]
            });
          })
          .on('error', reject);
      });
    } finally {
      // Cleanup temp file
      await fs.unlink(tempFile).catch(() => {});
    }
  }
  
  private async processExcel(input: any): Promise<TaskResult> {
    const { fileContent, sheetName } = input;
    
    if (!fileContent) {
      throw new Error('File content is required');
    }
    
    // Parse Excel file
    const workbook = XLSX.read(fileContent, { type: 'buffer' });
    const targetSheet = sheetName || workbook.SheetNames[0];
    
    if (!workbook.Sheets[targetSheet]) {
      throw new Error(`Sheet '${targetSheet}' not found`);
    }
    
    const worksheet = workbook.Sheets[targetSheet];
    const data = XLSX.utils.sheet_to_json(worksheet);
    
    const result = {
      sheetName: targetSheet,
      availableSheets: workbook.SheetNames,
      rowCount: data.length,
      columns: Object.keys(data[0] || {}),
      data: data.slice(0, 100), // Limit to first 100 rows
      summary: this.generateExcelSummary(data)
    };
    
    return {
      status: 'completed',
      result,
      artifacts: [
        {
          id: `excel-analysis-${Date.now()}`,
          type: 'json',
          content: { analysis: result },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private async extractText(input: any): Promise<TaskResult> {
    const { fileContent, fileType } = input;
    
    if (!fileContent) {
      throw new Error('File content is required');
    }
    
    let extractedText = '';
    
    switch (fileType?.toLowerCase()) {
      case 'txt':
      case 'text':
        extractedText = fileContent.toString();
        break;
      case 'json':
        try {
          const jsonData = JSON.parse(fileContent);
          extractedText = JSON.stringify(jsonData, null, 2);
        } catch (error) {
          throw new Error('Invalid JSON format');
        }
        break;
      default:
        // Try to extract as text
        extractedText = fileContent.toString();
    }
    
    const analysis = {
      characterCount: extractedText.length,
      wordCount: extractedText.split(/\s+/).filter(word => word.length > 0).length,
      lineCount: extractedText.split('\n').length,
      preview: extractedText.substring(0, 500) + (extractedText.length > 500 ? '...' : '')
    };
    
    return {
      status: 'completed',
      result: {
        text: extractedText,
        analysis
      },
      artifacts: [
        {
          id: `text-extraction-${Date.now()}`,
          type: 'text',
          content: { text: extractedText },
          metadata: { analysis },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private async analyzeFile(input: any): Promise<TaskResult> {
    const { fileName, fileSize, fileType, fileContent } = input;
    
    const analysis = {
      fileName,
      fileSize,
      fileType,
      sizeCategory: this.categorizeSizeFile(fileSize),
      typeCategory: this.categorizeFileType(fileType),
      contentPreview: fileContent ? fileContent.toString().substring(0, 200) : null,
      recommendations: this.generateRecommendations(fileType, fileSize)
    };
    
    return {
      status: 'completed',
      result: analysis,
      artifacts: [
        {
          id: `file-analysis-${Date.now()}`,
          type: 'json',
          content: { analysis },
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString()
        }
      ]
    };
  }
  
  private generateCsvSummary(rows: any[]) {
    if (rows.length === 0) return {};
    
    const columns = Object.keys(rows[0]);
    const summary: any = {};
    
    columns.forEach(column => {
      const values = rows.map(row => row[column]).filter(val => val !== null && val !== undefined);
      const numericValues = values.filter(val => !isNaN(Number(val))).map(Number);
      
      summary[column] = {
        totalValues: values.length,
        uniqueValues: new Set(values).size,
        nullValues: rows.length - values.length,
        dataType: numericValues.length > values.length * 0.8 ? 'numeric' : 'text'
      };
      
      if (numericValues.length > 0) {
        summary[column].statistics = {
          min: Math.min(...numericValues),
          max: Math.max(...numericValues),
          avg: numericValues.reduce((a, b) => a + b, 0) / numericValues.length
        };
      }
    });
    
    return summary;
  }
  
  private generateExcelSummary(data: any[]) {
    return this.generateCsvSummary(data); // Same logic for Excel
  }
  
  private categorizeSizeFile(size: number): string {
    if (size < 1024) return 'tiny';
    if (size < 1024 * 1024) return 'small';
    if (size < 10 * 1024 * 1024) return 'medium';
    if (size < 100 * 1024 * 1024) return 'large';
    return 'very-large';
  }
  
  private categorizeFileType(type: string): string {
    const documentTypes = ['pdf', 'doc', 'docx', 'txt'];
    const spreadsheetTypes = ['xls', 'xlsx', 'csv'];
    const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
    const videoTypes = ['mp4', 'avi', 'mov', 'wmv'];
    
    if (documentTypes.includes(type?.toLowerCase())) return 'document';
    if (spreadsheetTypes.includes(type?.toLowerCase())) return 'spreadsheet';
    if (imageTypes.includes(type?.toLowerCase())) return 'image';
    if (videoTypes.includes(type?.toLowerCase())) return 'video';
    
    return 'other';
  }
  
  private generateRecommendations(fileType: string, fileSize: number): string[] {
    const recommendations = [];
    
    if (fileSize > 50 * 1024 * 1024) {
      recommendations.push('Consider compressing large files before processing');
    }
    
    if (['csv', 'xlsx'].includes(fileType?.toLowerCase())) {
      recommendations.push('Use process-csv or process-excel tasks for structured data analysis');
    }
    
    if (['txt', 'json'].includes(fileType?.toLowerCase())) {
      recommendations.push('Use extract-text task for text content analysis');
    }
    
    return recommendations;
  }
}
 
// Create and start the file processor agent
const fileProcessorAgent = new A2AServer({
  agentCard: {
    id: 'file-processor-v1',
    name: 'File Processing Agent',
    description: 'Processes and analyzes various file types',
    version: '1.0.0',
    capabilities: [
      'file-processing',
      'data-analysis',
      'text-extraction',
      'csv-processing',
      'excel-processing'
    ],
    endpoint: 'http://localhost:3002'
  },
  executor: new FileProcessorExecutor()
});
 
await fileProcessorAgent.start(3002);
console.log('📁 File processor agent running on http://localhost:3002');

3. Multi-Agent Orchestration

An example showing how multiple agents can work together to complete complex tasks.

orchestrator.ts
import { AgentClient, TaskClient, MessageClient } from '@dexwox-labs/a2a-node';
 
interface AgentInfo {
  id: string;
  name: string;
  capabilities: string[];
  client: TaskClient;
}
 
class AgentOrchestrator {
  private agents: Map<string, AgentInfo> = new Map();
  private agentClient: AgentClient;
  private messageClient: MessageClient;
  
  constructor() {
    this.agentClient = new AgentClient({ baseUrl: 'http://localhost:3000' });
    this.messageClient = new MessageClient({ baseUrl: 'http://localhost:3000' });
  }
  
  async discoverAgents(): Promise<void> {
    console.log('🔍 Discovering available agents...');
    
    // Discover agents on different ports
    const ports = [3001, 3002, 3003];
    
    for (const port of ports) {
      try {
        const agentClient = new AgentClient({ baseUrl: `http://localhost:${port}` });
        const agents = await agentClient.resolveAgents();
        
        for (const agent of agents) {
          this.agents.set(agent.id, {
            id: agent.id,
            name: agent.name,
            capabilities: agent.capabilities,
            client: new TaskClient({ baseUrl: `http://localhost:${port}` })
          });
          
          console.log(`✅ Found agent: ${agent.name} (${agent.capabilities.join(', ')})`);
        }
      } catch (error) {
        console.log(`❌ No agent found on port ${port}`);
      }
    }
  }
  
  async processWeatherReport(location: string): Promise<any> {
    console.log(`\n🌤️ Generating weather report for ${location}...`);
    
    // Step 1: Get current weather
    const weatherAgent = this.findAgentByCapability('weather-current');
    if (!weatherAgent) {
      throw new Error('No weather agent available');
    }
    
    const weatherTask = await weatherAgent.client.createTask({
      name: 'get-current-weather',
      input: { location }
    });
    
    const weatherResult = await this.waitForTask(weatherAgent.client, weatherTask.id);
    console.log('✅ Current weather retrieved');
    
    // Step 2: Get forecast
    const forecastTask = await weatherAgent.client.createTask({
      name: 'get-forecast',
      input: { location, days: 3 }
    });
    
    const forecastResult = await this.waitForTask(weatherAgent.client, forecastTask.id);
    console.log('✅ Forecast retrieved');
    
    // Step 3: Generate report document (if document agent available)
    const docAgent = this.findAgentByCapability('document-generation');
    let reportDocument = null;
    
    if (docAgent) {
      const reportData = {
        location,
        currentWeather: weatherResult.result,
        forecast: forecastResult.result,
        generatedAt: new Date().toISOString()
      };
      
      const docTask = await docAgent.client.createTask({
        name: 'generate-report',
        input: {
          template: 'weather-report',
          data: reportData
        }
      });
      
      reportDocument = await this.waitForTask(docAgent.client, docTask.id);
      console.log('✅ Report document generated');
    }
    
    return {
      location,
      currentWeather: weatherResult.result,
      forecast: forecastResult.result,
      document: reportDocument?.result,
      generatedAt: new Date().toISOString()
    };
  }
  
  async processDataAnalysis(csvData: string): Promise<any> {
    console.log('\n📊 Processing data analysis workflow...');
    
    // Step 1: Process CSV data
    const fileAgent = this.findAgentByCapability('file-processing');
    if (!fileAgent) {
      throw new Error('No file processing agent available');
    }
    
    const csvTask = await fileAgent.client.createTask({
      name: 'process-csv',
      input: { fileContent: csvData }
    });
    
    const csvResult = await this.waitForTask(fileAgent.client, csvTask.id);
    console.log('✅ CSV data processed');
    
    // Step 2: Generate insights (if analytics agent available)
    const analyticsAgent = this.findAgentByCapability('data-analytics');
    let insights = null;
    
    if (analyticsAgent) {
      const analyticsTask = await analyticsAgent.client.createTask({
        name: 'generate-insights',
        input: {
          data: csvResult.result.data,
          summary: csvResult.result.summary
        }
      });
      
      insights = await this.waitForTask(analyticsAgent.client, analyticsTask.id);
      console.log('✅ Data insights generated');
    }
    
    return {
      dataAnalysis: csvResult.result,
      insights: insights?.result,
      processedAt: new Date().toISOString()
    };
  }
  
  private findAgentByCapability(capability: string): AgentInfo | null {
    for (const agent of this.agents.values()) {
      if (agent.capabilities.includes(capability)) {
        return agent;
      }
    }
    return null;
  }
  
  private async waitForTask(client: TaskClient, taskId: string): Promise<any> {
    let task = await client.getTask(taskId);
    
    while (task.status === 'submitted' || task.status === 'working') {
      await new Promise(resolve => setTimeout(resolve, 1000));
      task = await client.getTask(taskId);
    }
    
    if (task.status === 'failed') {
      throw new Error(`Task failed: ${task.error}`);
    }
    
    return task;
  }
  
  listAvailableAgents(): void {
    console.log('\n📋 Available Agents:');
    for (const agent of this.agents.values()) {
      console.log(`  • ${agent.name} (${agent.id})`);
      console.log(`    Capabilities: ${agent.capabilities.join(', ')}`);
    }
  }
}
 
// Usage example
async function main() {
  const orchestrator = new AgentOrchestrator();
  
  try {
    // Discover available agents
    await orchestrator.discoverAgents();
    orchestrator.listAvailableAgents();
    
    // Example 1: Weather report workflow
    const weatherReport = await orchestrator.processWeatherReport('New York, NY');
    console.log('\n📄 Weather Report Generated:', weatherReport);
    
    // Example 2: Data analysis workflow
    const csvData = `name,age,city
John,25,New York
Jane,30,Los Angeles
Bob,35,Chicago`;
    
    const dataAnalysis = await orchestrator.processDataAnalysis(csvData);
    console.log('\n📈 Data Analysis Complete:', dataAnalysis);
    
  } catch (error) {
    console.error('❌ Orchestration failed:', error.message);
  }
}
 
main();

Testing Examples

Unit Testing Agents

weather-agent.test.ts
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import { A2AServer } from '@dexwox-labs/a2a-node';
import { WeatherAgentExecutor } from './weather-agent';
import request from 'supertest';
 
describe('Weather Agent', () => {
  let server: A2AServer;
  let app: any;
  
  beforeEach(async () => {
    server = new A2AServer({
      agentCard: {
        id: 'test-weather-agent',
        name: 'Test Weather Agent',
        capabilities: ['weather-current'],
        endpoint: 'http://localhost:3000',
        version: '1.0.0'
      },
      executor: new WeatherAgentExecutor('test-api-key')
    });
    
    app = server.getApp();
  });
  
  afterEach(async () => {
    if (server) {
      await server.stop();
    }
  });
  
  it('should return agent card', async () => {
    const response = await request(app)
      .get('/.well-known/agent.json')
      .expect(200);
    
    expect(response.body.id).toBe('test-weather-agent');
    expect(response.body.capabilities).toContain('weather-current');
  });
  
  it('should create weather task', async () => {
    const response = await request