Claude
Skills
Sign in
Back

a2a-js-dev

Included with Lifetime
$97 forever

Build A2A (Agent-to-Agent) protocol applications using the @a2a-js/sdk TypeScript/JavaScript SDK. Use this skill whenever the user wants to create an A2A agent server, build an A2A client, implement agent-to-agent communication, set up agent discovery, handle A2A tasks/streaming, or write any TypeScript/JavaScript code that imports from @a2a-js/sdk. Also triggers when the user mentions 'A2A protocol', 'agent-to-agent', 'agent card', 'AgentExecutor', or references the a2a-js repository — even if they don't explicitly say 'A2A SDK'. Covers both server and client development, all three transport bindings (JSON-RPC, HTTP+JSON/REST, gRPC), and the full task lifecycle.

Backend & APIs

What this skill does


# A2A JavaScript/TypeScript SDK Development Guide

Build standards-compliant agent-to-agent applications using the `@a2a-js/sdk`. This guide covers server agents, clients, task management, streaming, and all three transport bindings.

## Quick Reference

```bash
# Install the SDK
npm install @a2a-js/sdk

# For Express server integration
npm install express

# For gRPC support (Node.js only)
npm install @grpc/grpc-js @bufbuild/protobuf
```

**Import paths:**
```typescript
import { AgentCard, Message, Task, AGENT_CARD_PATH } from '@a2a-js/sdk';
import { AgentExecutor, DefaultRequestHandler, InMemoryTaskStore } from '@a2a-js/sdk/server';
import { agentCardHandler, jsonRpcHandler, restHandler, UserBuilder } from '@a2a-js/sdk/server/express';
import { ClientFactory } from '@a2a-js/sdk/client';
import { GrpcTransportFactory } from '@a2a-js/sdk/client/grpc';
```

The SDK implements **A2A Protocol Specification v0.3.0**.

---

## Architecture Overview

An A2A application has two sides:

1. **Server (Agent)** — exposes capabilities via an Agent Card, handles incoming messages, manages tasks, and publishes events
2. **Client (Consumer)** — discovers agents, sends messages, tracks tasks, and consumes streaming updates

The protocol supports three transport bindings. Each transport provides the same operations — pick based on your deployment needs:

| Transport | Client | Server | Notes |
|-----------|--------|--------|-------|
| JSON-RPC | Yes | Yes | Default, works everywhere |
| HTTP+JSON/REST | Yes | Yes | RESTful, good for web clients |
| gRPC | Yes | Yes | Node.js only, high performance |

---

## Building an A2A Server

### Step 1: Define the Agent Card

The Agent Card is the identity and capability declaration for your agent. Other agents discover it at `/.well-known/agent-card.json`.

```typescript
import { AgentCard } from '@a2a-js/sdk';

const agentCard: AgentCard = {
  name: 'My Agent',
  description: 'Describe what your agent does clearly.',
  protocolVersion: '0.3.0',
  version: '1.0.0',
  url: 'http://localhost:4000/a2a/jsonrpc',
  skills: [
    {
      id: 'skill-id',
      name: 'Skill Name',
      description: 'What this skill does',
      tags: ['relevant', 'tags'],
    },
  ],
  capabilities: {
    pushNotifications: false,
    streaming: true,
  },
  defaultInputModes: ['text'],
  defaultOutputModes: ['text'],
  additionalInterfaces: [
    { url: 'http://localhost:4000/a2a/jsonrpc', transport: 'JSONRPC' },
    { url: 'http://localhost:4000/a2a/rest', transport: 'HTTP+JSON' },
  ],
};
```

**Key design decisions for the Agent Card:**
- `skills` — declare what your agent can do so clients can match capabilities
- `capabilities` — advertise streaming and push notification support honestly; clients check these before using those features
- `additionalInterfaces` — list all transport endpoints you support so `ClientFactory` can auto-select

### Step 2: Implement the AgentExecutor

This is where your agent's logic lives. The `execute` method receives a `RequestContext` and an `ExecutionEventBus`.

```typescript
import { AgentExecutor, RequestContext, ExecutionEventBus } from '@a2a-js/sdk/server';
import { Message, Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent } from '@a2a-js/sdk';
import { v4 as uuidv4 } from 'uuid';

class MyAgentExecutor implements AgentExecutor {
  async execute(
    requestContext: RequestContext,
    eventBus: ExecutionEventBus
  ): Promise<void> {
    const { taskId, contextId, userMessage, task } = requestContext;

    // Access the user's message text
    const userText = userMessage.parts
      .filter((p) => p.kind === 'text')
      .map((p) => p.text)
      .join('\n');

    // If this is a new task (no existing task), initialize it
    if (!task) {
      const newTask: Task = {
        kind: 'task',
        id: taskId,
        contextId,
        status: { state: 'working', timestamp: new Date().toISOString() },
        history: [userMessage],
      };
      eventBus.publish(newTask);
    }

    // --- Your agent logic goes here ---
    // Call your AI model, run tools, process data, etc.
    const result = await doSomethingUseful(userText);

    // Publish an artifact if your agent produces output files/data
    const artifactUpdate: TaskArtifactUpdateEvent = {
      kind: 'artifact-update',
      taskId,
      contextId,
      artifact: {
        artifactId: uuidv4(),
        name: 'result.txt',
        parts: [{ kind: 'text', text: result }],
      },
    };
    eventBus.publish(artifactUpdate);

    // Signal task completion
    const statusUpdate: TaskStatusUpdateEvent = {
      kind: 'status-update',
      taskId,
      contextId,
      status: { state: 'completed', timestamp: new Date().toISOString() },
      final: true,
    };
    eventBus.publish(statusUpdate);

    // Always call finished() to signal execution is done
    eventBus.finished();
  }

  async cancelTask(): Promise<void> {
    // Handle cancellation — clean up resources, abort in-flight work
  }
}
```

**RequestContext fields:**
- `taskId` — unique task identifier (server-generated)
- `contextId` — conversation context identifier
- `userMessage` — the incoming `Message` object
- `task` — the existing `Task` object if this continues an existing task, or `undefined` for new tasks

**ExecutionEventBus methods:**
- `publish(event)` — emit a `Message`, `Task`, `TaskStatusUpdateEvent`, or `TaskArtifactUpdateEvent`
- `finished()` — signal that execution is complete (always call this)

### Step 3: Wire Up the Server

```typescript
import express from 'express';
import { AGENT_CARD_PATH } from '@a2a-js/sdk';
import { DefaultRequestHandler, InMemoryTaskStore } from '@a2a-js/sdk/server';
import {
  agentCardHandler,
  jsonRpcHandler,
  restHandler,
  UserBuilder,
} from '@a2a-js/sdk/server/express';

const executor = new MyAgentExecutor();
const handler = new DefaultRequestHandler(
  agentCard,
  new InMemoryTaskStore(),
  executor
);

const app = express();

// Agent Card discovery endpoint
app.use(`/${AGENT_CARD_PATH}`, agentCardHandler({ agentCardProvider: handler }));

// JSON-RPC transport
app.use(
  '/a2a/jsonrpc',
  jsonRpcHandler({
    requestHandler: handler,
    userBuilder: UserBuilder.noAuthentication, // Use proper auth in production
  })
);

// REST transport
app.use(
  '/a2a/rest',
  restHandler({
    requestHandler: handler,
    userBuilder: UserBuilder.noAuthentication,
  })
);

app.listen(4000, () => {
  console.log('A2A agent running on http://localhost:4000');
});
```

**`DefaultRequestHandler`** orchestrates the full request lifecycle — it manages task creation, state transitions, executor invocation, and event routing. You don't need to handle this yourself.

**`InMemoryTaskStore`** is fine for development. For production, implement the `TaskStore` interface backed by your database.

### Adding gRPC Support

```typescript
import { GrpcServer } from '@a2a-js/sdk/server/grpc';

// Add gRPC alongside Express
const grpcServer = new GrpcServer(handler);
grpcServer.start(4001);

// Update your Agent Card's additionalInterfaces to include:
// { url: 'http://localhost:4001', transport: 'GRPC' }
```

---

## Building an A2A Client

### Auto-Discovery Client

The `ClientFactory` fetches the Agent Card and auto-selects the best transport:

```typescript
import { ClientFactory } from '@a2a-js/sdk/client';
import { v4 as uuidv4 } from 'uuid';

const factory = new ClientFactory();
const client = await factory.createFromUrl('http://localhost:4000');

// Send a message
const response = await client.sendMessage({
  message: {
    messageId: uuidv4(),
    role: 'user',
    parts: [{ kind: 'text', text: 'Hello, agent!' }],
    kind: 'message',
  },
});
```

### Explicit gRPC Client

```typescript
import { ClientFactory } from '@a2a-js/sdk/client';
import { GrpcTransportFactory } from '@a2a-js/sdk/client/grpc';

const factory = new ClientFactory({
  transports: [new GrpcTransportFactory()],
});
const client = await factory.createFromUrl('http://l
Files: 3
Size: 36.1 KB
Complexity: 50/100
Category: Backend & APIs

Related in Backend & APIs