A2A Protocol

A2A JS SDK Complete Tutorial: Quick Start Guide

MILO
Share
A2A JS SDK Complete Tutorial: Quick Start Guide

Table of Contents

  1. What is A2A JS SDK?
  2. A2A JS Installation & Setup
  3. A2A JS Core Concepts
  4. Creating Your First A2A JS Agent
  5. A2A JS Server Development
  6. A2A JS Client Usage
  7. A2A JS Advanced Features
  8. A2A JS Best Practices
  9. A2A JS Troubleshooting

What is A2A JS SDK?

A2A JS SDK is a powerful library designed specifically for JavaScript/TypeScript developers to build intelligent agent applications that comply with the Agent2Agent (A2A) Protocol. This A2A JS framework enables developers to easily create intelligent agent systems that can communicate and collaborate with each other.

Core Advantages of A2A JS

  • 🚀 Easy to Use: A2A JS provides intuitive APIs that allow developers to get started quickly
  • 🔄 Real-time Communication: Supports streaming processing and Server-Sent Events (SSE)
  • 🛡️ Type Safety: Built on TypeScript, providing complete type support
  • 🌐 Cross-platform: A2A JS can run in both Node.js and browser environments
  • 📡 Standard Protocol: Fully implements A2A protocol specifications

A2A JS Installation & Setup

Installing A2A JS SDK

Install A2A JS SDK using npm:

npm install @a2a-js/sdk

Or using yarn:

yarn add @a2a-js/sdk

Verifying A2A JS Installation

Create a simple test file to verify that A2A JS is correctly installed:

import { A2AClient, AgentCard } from "@a2a-js/sdk";

console.log("A2A JS SDK installed successfully!");

A2A JS Core Concepts

Before starting to use A2A JS, it's important to understand the following core concepts:

1. Agent Card

Each agent in A2A JS requires an Agent Card that describes the agent's capabilities and interfaces:

import { AgentCard } from "@a2a-js/sdk";

const agentCard: AgentCard = {
  name: 'My A2A JS Agent',
  description: 'Intelligent agent built with A2A JS SDK',
  url: 'http://localhost:3000/',
  provider: {
    organization: 'A2A JS Developers',
    url: 'https://example.com'
  },
  version: '1.0.0',
  capabilities: {
    streaming: true,
    pushNotifications: false,
    stateTransitionHistory: true,
  },
  skills: [{
    id: 'general_chat',
    name: 'General Chat',
    description: 'General conversation using A2A JS',
    tags: ['chat', 'a2a-js'],
    examples: ['Hello', 'Help me answer questions']
  }]
};

2. Agent Executor

The core execution logic of A2A JS is implemented through AgentExecutor:

import { AgentExecutor, RequestContext, IExecutionEventBus } from "@a2a-js/sdk";

class MyA2AJSExecutor implements AgentExecutor {
  async execute(
    requestContext: RequestContext,
    eventBus: IExecutionEventBus
  ): Promise<void> {
    // Your A2A JS agent logic
    console.log("A2A JS agent is processing request...");
  }

  async cancelTask(taskId: string, eventBus: IExecutionEventBus): Promise<void> {
    console.log(`A2A JS canceling task: ${taskId}`);
  }
}

Creating Your First A2A JS Agent

Let's create a complete agent example using A2A JS SDK:

Step 1: Define A2A JS Agent Card

import { AgentCard } from "@a2a-js/sdk";

const myAgentCard: AgentCard = {
  name: 'Hello World A2A JS Agent',
  description: 'My first A2A JS agent for learning A2A JS SDK',
  url: 'http://localhost:3000/',
  provider: {
    organization: 'A2A JS Tutorial',
    url: 'https://example.com'
  },
  version: '1.0.0',
  capabilities: {
    streaming: true,
    pushNotifications: false,
    stateTransitionHistory: true,
  },
  defaultInputModes: ['text/plain'],
  defaultOutputModes: ['text/plain'],
  skills: [{
    id: 'hello_world',
    name: 'Hello World',
    description: 'A2A JS example skill: respond to greetings',
    tags: ['hello', 'greeting', 'a2a-js'],
    examples: [
      'Hello',
      'Hi there',
      'Tell me about A2A JS'
    ],
    inputModes: ['text/plain'],
    outputModes: ['text/plain']
  }],
  supportsAuthenticatedExtendedCard: false,
};

Step 2: Implement A2A JS Executor

import {
  AgentExecutor,
  RequestContext,
  IExecutionEventBus,
  Task,
  TaskState,
  TaskStatusUpdateEvent
} from "@a2a-js/sdk";
import { v4 as uuidv4 } from "uuid";

class HelloWorldA2AJSExecutor implements AgentExecutor {
  private cancelledTasks = new Set<string>();

  async cancelTask(taskId: string, eventBus: IExecutionEventBus): Promise<void> {
    this.cancelledTasks.add(taskId);
    console.log(`A2A JS executor canceling task: ${taskId}`);
  }

  async execute(
    requestContext: RequestContext,
    eventBus: IExecutionEventBus
  ): Promise<void> {
    const userMessage = requestContext.userMessage;
    const existingTask = requestContext.task;
    
    const taskId = existingTask?.id || uuidv4();
    const contextId = userMessage.contextId || existingTask?.contextId || uuidv4();

    console.log(`A2A JS agent processing message: ${userMessage.parts[0]?.text}`);

    // Create new task
    if (!existingTask) {
      const initialTask: Task = {
        kind: 'task',
        id: taskId,
        contextId: contextId,
        status: {
          state: TaskState.Submitted,
          timestamp: new Date().toISOString(),
        },
        history: [userMessage],
        metadata: userMessage.metadata,
        artifacts: [],
      };
      eventBus.publish(initialTask);
    }

    // Publish working status
    const workingUpdate: TaskStatusUpdateEvent = {
      kind: 'status-update',
      taskId: taskId,
      contextId: contextId,
      status: {
        state: TaskState.Working,
        message: {
          kind: 'message',
          role: 'agent',
          messageId: uuidv4(),
          parts: [{ kind: 'text', text: 'A2A JS agent is thinking...' }],
          taskId: taskId,
          contextId: contextId,
        },
        timestamp: new Date().toISOString(),
      },
      final: false,
    };
    eventBus.publish(workingUpdate);

    // Simulate processing time
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Check cancellation status
    if (this.cancelledTasks.has(taskId)) {
      const cancelledUpdate: TaskStatusUpdateEvent = {
        kind: 'status-update',
        taskId: taskId,
        contextId: contextId,
        status: {
          state: TaskState.Canceled,
          timestamp: new Date().toISOString(),
        },
        final: true,
      };
      eventBus.publish(cancelledUpdate);
      return;
    }

    // Generate response
    const userText = userMessage.parts[0]?.text || '';
    let responseText = '';
    
    if (userText.toLowerCase().includes('hello') || userText.toLowerCase().includes('hi')) {
      responseText = `Hello! Welcome to A2A JS SDK! I'm an intelligent agent built with A2A JS.`;
    } else if (userText.toLowerCase().includes('a2a js')) {
      responseText = `A2A JS SDK is a powerful JavaScript library for building intelligent agent applications!`;
    } else {
      responseText = `I'm an A2A JS agent and I received your message: "${userText}". Thank you for using A2A JS SDK!`;
    }

    // Publish final result
    const finalUpdate: TaskStatusUpdateEvent = {
      kind: 'status-update',
      taskId: taskId,
      contextId: contextId,
      status: {
        state: TaskState.Completed,
        message: {
          kind: 'message',
          role: 'agent',
          messageId: uuidv4(),
          parts: [{ kind: 'text', text: responseText }],
          taskId: taskId,
          contextId: contextId,
        },
        timestamp: new Date().toISOString(),
      },
      final: true,
    };
    eventBus.publish(finalUpdate);
  }
}

Step 3: Start A2A JS Server

import express from 'express';
import {
  A2AExpressApp,
  DefaultRequestHandler,
  InMemoryTaskStore
} from "@a2a-js/sdk";

const taskStore = new InMemoryTaskStore();
const agentExecutor = new HelloWorldA2AJSExecutor();

const requestHandler = new DefaultRequestHandler(
  myAgentCard,
  taskStore,
  agentExecutor
);

const appBuilder = new A2AExpressApp(requestHandler);
const expressApp = appBuilder.setupRoutes(express(), '');

const PORT = process.env.PORT || 3000;
expressApp.listen(PORT, () => {
  console.log(`A2A JS agent server started at http://localhost:${PORT}`);
  console.log(`A2A JS agent card: http://localhost:${PORT}/.well-known/agent.json`);
  console.log('Press Ctrl+C to stop A2A JS server');
});

A2A JS Server Development

Task Storage

A2A JS provides in-memory task storage, and you can also implement custom storage:

import { TaskStore, Task } from "@a2a-js/sdk";

class CustomA2AJSTaskStore implements TaskStore {
  private tasks = new Map<string, Task>();

  async getTask(taskId: string): Promise<Task | undefined> {
    console.log(`A2A JS getting task: ${taskId}`);
    return this.tasks.get(taskId);
  }

  async setTask(task: Task): Promise<void> {
    console.log(`A2A JS saving task: ${task.id}`);
    this.tasks.set(task.id, task);
  }

  async deleteTask(taskId: string): Promise<void> {
    console.log(`A2A JS deleting task: ${taskId}`);
    this.tasks.delete(taskId);
  }
}

Middleware Support

A2A JS is based on Express.js and supports all standard middleware:

import cors from 'cors';
import express from 'express';

const app = express();

// A2A JS server middleware configuration
app.use(cors());
app.use(express.json());

// Custom A2A JS logging middleware
app.use((req, res, next) => {
  console.log(`A2A JS request: ${req.method} ${req.path}`);
  next();
});

A2A JS Client Usage

Basic Client Operations

import { A2AClient, MessageSendParams } from "@a2a-js/sdk";
import { v4 as uuidv4 } from "uuid";

const client = new A2AClient("http://localhost:3000");

async function testA2AJSClient() {
  console.log("Testing A2A JS client...");
  
  const messageParams: MessageSendParams = {
    message: {
      messageId: uuidv4(),
      role: "user",
      parts: [{ kind: "text", text: "Hello, A2A JS!" }],
      kind: "message"
    },
    configuration: {
      blocking: true,
      acceptedOutputModes: ['text/plain']
    }
  };

  try {
    const response = await client.sendMessage(messageParams);
    
    if (response.error) {
      console.error("A2A JS client error:", response.error);
      return;
    }

    console.log("A2A JS response:", response.result);
  } catch (error) {
    console.error("A2A JS communication error:", error);
  }
}

testA2AJSClient();

A2A JS Streaming

A2A JS supports real-time streaming communication:

import { A2AClient, TaskStatusUpdateEvent } from "@a2a-js/sdk";

async function streamA2AJSResponse() {
  const client = new A2AClient("http://localhost:3000");
  
  console.log("Starting A2A JS streaming...");
  
  const streamParams = {
    message: {
      messageId: uuidv4(),
      role: "user",
      parts: [{ kind: "text", text: "Stream conversation using A2A JS" }],
      kind: "message"
    }
  };

  try {
    const stream = client.sendMessageStream(streamParams);
    
    for await (const event of stream) {
      if (event.kind === 'task') {
        console.log(`A2A JS task created: ${event.id}`);
      } else if (event.kind === 'status-update') {
        const statusEvent = event as TaskStatusUpdateEvent;
        console.log(`A2A JS status update: ${statusEvent.status.state}`);
        
        if (statusEvent.status.message?.parts[0]?.text) {
          console.log(`A2A JS message: ${statusEvent.status.message.parts[0].text}`);
        }
        
        if (statusEvent.final) {
          console.log("A2A JS streaming completed");
          break;
        }
      }
    }
  } catch (error) {
    console.error("A2A JS streaming error:", error);
  }
}

A2A JS Advanced Features

Artifact Handling

A2A JS supports creation and management of artifacts:

import { TaskArtifactUpdateEvent } from "@a2a-js/sdk";

// Publishing artifacts in AgentExecutor
const artifactUpdate: TaskArtifactUpdateEvent = {
  kind: 'artifact-update',
  taskId: taskId,
  contextId: contextId,
  artifact: {
    artifactId: "a2a-js-example",
    name: "A2A JS Example File",
    parts: [{ 
      text: `# A2A JS Generated Content\n\nThis is an example file generated using A2A JS SDK.`
    }],
  },
  append: false,
  lastChunk: true,
};
eventBus.publish(artifactUpdate);

Security Configuration

Configure security options for A2A JS agents:

const secureAgentCard: AgentCard = {
  name: 'Secure A2A JS Agent',
  description: 'Secure A2A JS agent',
  // ... other configurations
  securitySchemes: {
    apiKey: {
      type: 'apiKey',
      name: 'X-API-Key',
      in: 'header'
    }
  },
  security: [{
    apiKey: []
  }]
};

A2A JS Best Practices

1. Error Handling

Implement comprehensive error handling in A2A JS applications:

class RobustA2AJSExecutor implements AgentExecutor {
  async execute(requestContext: RequestContext, eventBus: IExecutionEventBus) {
    try {
      // A2A JS execution logic
      await this.processRequest(requestContext, eventBus);
    } catch (error) {
      console.error("A2A JS execution error:", error);
      
      // Publish error status
      const errorUpdate: TaskStatusUpdateEvent = {
        kind: 'status-update',
        taskId: requestContext.task?.id || uuidv4(),
        contextId: requestContext.userMessage.contextId || uuidv4(),
        status: {
          state: TaskState.Failed,
          message: {
            kind: 'message',
            role: 'agent',
            messageId: uuidv4(),
            parts: [{ kind: 'text', text: 'An error occurred during A2A JS processing, please try again later.' }],
          },
          timestamp: new Date().toISOString(),
        },
        final: true,
      };
      eventBus.publish(errorUpdate);
    }
  }
}

2. Performance Optimization

Optimize your A2A JS application performance:

// Optimize A2A JS client with connection pooling
const client = new A2AClient("http://localhost:3000", {
  keepAlive: true,
  timeout: 30000
});

// A2A JS agent response caching
class CachedA2AJSExecutor implements AgentExecutor {
  private cache = new Map<string, string>();
  
  async execute(requestContext: RequestContext, eventBus: IExecutionEventBus) {
    const userText = requestContext.userMessage.parts[0]?.text || '';
    const cacheKey = `a2a-js-${userText}`;
    
    // Check A2A JS cache
    if (this.cache.has(cacheKey)) {
      console.log("A2A JS using cached response");
      // Return cached response
    }
    
    // Process new request and cache result
  }
}

3. Logging

Add detailed logging to A2A JS applications:

import { createLogger, format, transports } from 'winston';

const a2aJSLogger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.printf(({ timestamp, level, message }) => {
      return `[A2A JS] ${timestamp} ${level}: ${message}`;
    })
  ),
  transports: [
    new transports.Console(),
    new transports.File({ filename: 'a2a-js.log' })
  ]
});

// Use in A2A JS code
a2aJSLogger.info('A2A JS agent started successfully');
a2aJSLogger.error('A2A JS processing error', { error: errorDetails });

A2A JS Troubleshooting

Common Issue Resolution

1. A2A JS Connection Issues

// Check A2A JS server connection
async function checkA2AJSConnection() {
  try {
    const client = new A2AClient("http://localhost:3000");
    const response = await fetch("http://localhost:3000/.well-known/agent.json");
    
    if (response.ok) {
      console.log("A2A JS server connection normal");
    } else {
      console.error("A2A JS server response abnormal:", response.status);
    }
  } catch (error) {
    console.error("A2A JS connection failed:", error);
  }
}

You can also try the following methods:

2. A2A JS Type Errors

Ensure correct A2A JS type imports:

// Correct A2A JS type imports
import {
  AgentCard,
  AgentExecutor,
  A2AClient,
  Task,
  TaskState,
  Message,
  MessageSendParams
} from "@a2a-js/sdk";

3. A2A JS Performance Debugging

// A2A JS performance monitoring
class PerformanceA2AJSExecutor implements AgentExecutor {
  async execute(requestContext: RequestContext, eventBus: IExecutionEventBus) {
    const startTime = Date.now();
    console.log(`A2A JS started processing: ${startTime}`);
    
    try {
      // Your A2A JS logic
      await this.processRequest(requestContext, eventBus);
    } finally {
      const endTime = Date.now();
      console.log(`A2A JS processing completed, duration: ${endTime - startTime}ms`);
    }
  }
}

Summary

A2A JS SDK is a powerful tool for building intelligent agent applications. Through this tutorial, you have learned:

  • Core concepts and architecture of A2A JS
  • How to create and configure A2A JS agents
  • A2A JS server and client development
  • Advanced features and best practices of A2A JS
  • Troubleshooting methods for A2A JS applications

Now you can start building your own A2A JS applications! Remember, A2A JS SDK provides rich functionality to help you create powerful, scalable intelligent agent systems.

For more A2A JS resources and examples, please visit:

Start your A2A JS development journey! 🚀