
# A2A Protocol Tutorial: Getting Started with Agent-to-Agent Communication

**Author:** Brenner Axiom  
**Date:** 2026-02-23  
**Bead:** beads-hub-98w (A2A Enablement Epic)  
**Status:** Working prototype on `localhost:3001`

## What is A2A?

A2A (Agent-to-Agent) is [Google's open protocol](https://google.github.io/A2A/) for enabling AI agents to communicate with each other. It uses:

- **JSON-RPC 2.0** for structured request/response
- **Agent Cards** (`/.well-known/agent.json`) for capability discovery
- **SSE (Server-Sent Events)** for streaming long-running tasks
- **Standard HTTP** — no proprietary transports

For #B4mad, A2A is how our agent fleet becomes interoperable with the wider agent ecosystem. Any external agent that speaks A2A can discover and task our agents — and vice versa.

## Architecture

```
┌─────────────────┐         ┌─────────────────┐
│  External Agent  │  HTTP   │  #B4mad A2A     │
│  (A2A Client)    │ ──────→ │  Server (:3001)  │
│                  │         │                  │
│  1. Discover     │ GET     │  /.well-known/   │
│     agent card   │ ──────→ │  agent.json      │
│                  │         │                  │
│  2. Send task    │ POST    │  /a2a            │
│     (JSON-RPC)   │ ──────→ │  tasks/send      │
│                  │         │                  │
│  3. Poll status  │ POST    │  /a2a            │
│     or stream    │ ──────→ │  tasks/get       │
└─────────────────┘         └─────────────────┘
```

## Prerequisites

- Node.js v22+ (installed on gamer-0)
- The A2A server module at `~/.openclaw/workspaces/codemonkey/a2a-server/`

## Quick Start

### 1. Start the A2A Server

```bash
cd ~/.openclaw/workspaces/codemonkey/a2a-server
npm start
# Output: A2A Server listening at http://localhost:3001
```

### 2. Discover the Agent Card

Every A2A agent exposes a card at `/.well-known/agent.json` describing its capabilities:

```bash
curl -s http://localhost:3001/.well-known/agent.json | python3 -m json.tool
```

Response:
```json
{
    "capabilities": {
        "tasks": {
            "send": true,
            "get": true,
            "cancel": true
        },
        "streaming": {
            "sse": true
        },
        "protocol": "A2A",
        "version": "1.0"
    },
    "description": "A2A Server Implementation"
}
```

This tells any client: "I can accept tasks, report status, cancel tasks, and stream results via SSE."

### 3. Authenticate

All task endpoints require a Bearer token. API keys are configured in `config.js`:

```bash
# Without auth → 401 Unauthorized
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tasks/send","params":{"task":{"message":"test"}},"id":1}'
```

```json
{
    "jsonrpc": "2.0",
    "error": {
        "code": -32000,
        "message": "Unauthorized: Missing or invalid Bearer token"
    },
    "id": 1
}
```

To authenticate, pass your API key as a Bearer token:

```bash
export A2A_KEY="a2a-server-key-12345"
```

All subsequent examples use `$A2A_KEY` in the Authorization header.

#### Rate Limiting

Each API key is rate-limited to **100 requests per hour**. Exceeding this returns:

```json
{
    "jsonrpc": "2.0",
    "error": { "code": -32001, "message": "Rate limit exceeded" },
    "id": null
}
```

#### Audit Logging

Every request is logged to `audit.log` with timestamp, IP, method, and auth status:

```json
{"timestamp":"2026-02-23T09:15:00.000Z","ip":"::1","method":"POST","authStatus":"AUTHORIZED","requestId":null}
```

### 4. Send a Task

Tasks are sent via JSON-RPC 2.0 `POST` to `/a2a`:

```bash
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $A2A_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/send",
    "params": {
      "task": {
        "message": "Summarize the latest #B4mad research papers"
      }
    },
    "id": 1
  }' | python3 -m json.tool
```

Response:
```json
{
    "jsonrpc": "2.0",
    "result": {
        "taskId": "task-1771837003190",
        "status": "queued"
    },
    "id": 1
}
```

The server returns a `taskId` you can use to track progress. Tasks are **persisted to disk** — they survive server restarts.

### 5. Check Task Status

Poll for results with `tasks/get` (returns real task state, not simulated):

```bash
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $A2A_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/get",
    "params": {
      "taskId": "task-1771837003190"
    },
    "id": 2
  }' | python3 -m json.tool
```

Response:
```json
{
    "jsonrpc": "2.0",
    "result": {
        "taskId": "task-1771837003190",
        "status": "completed",
        "output": "Task result would be here"
    },
    "id": 2
}
```

### 6. Cancel a Task

```bash
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $A2A_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/cancel",
    "params": {
      "taskId": "task-1771837003190"
    },
    "id": 3
  }' | python3 -m json.tool
```

### 7. Stream Results (SSE)

For long-running tasks, use Server-Sent Events by setting `Accept: text/event-stream`:

```bash
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $A2A_KEY" \
  -H "Accept: text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/send",
    "params": {
      "task": {
        "message": "Long-running research task"
      }
    },
    "id": 4
  }'
```

This returns a stream of `data:` events with progress updates until the task completes.

## Error Handling

The server returns standard JSON-RPC 2.0 errors plus custom A2A codes:

| Code | Meaning | When |
|---|---|---|
| `-32700` | Parse error | Malformed JSON body |
| `-32600` | Invalid Request | Missing `jsonrpc: "2.0"` or `method` field |
| `-32601` | Method not found | Unknown RPC method |
| `-32000` | Unauthorized | Missing/invalid Bearer token |
| `-32001` | Rate limit exceeded | Too many requests per key |

```bash
# Unknown method
curl -s -X POST http://localhost:3001/a2a \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $A2A_KEY" \
  -d '{"jsonrpc":"2.0","method":"unknown/method","params":{},"id":5}'
```

```json
{
    "jsonrpc": "2.0",
    "error": {
        "code": -32601,
        "message": "Method not found"
    },
    "id": 5
}
```

Request validation catches malformed JSON-RPC before it reaches any handler — this ensures agents always get a structured error response, never an HTML error page or stack trace.

## Using the A2A Client Library

We also ship a **client library** for agents that need to call *other* A2A agents. Located at `a2a-server/a2a-client/`:

```javascript
const A2AClient = require('./a2a-client');

// Create client with server URL and API key
const client = new A2AClient('http://localhost:3001', 'a2a-server-key-12345');

// 1. Discover what the remote agent can do
const agentCard = await client.discoverAgent('http://localhost:3001');
console.log(agentCard.capabilities);

// 2. Send a task
const result = await client.sendTask({
  name: 'summarize-papers',
  input: 'Summarize the latest #B4mad research papers'
});
console.log(result.taskId); // "task-1771838500123-456"

// 3. Poll until complete
const final = await client.sendAndPoll({
  name: 'research-task',
  input: 'Analyze ERC-8004 implications'
}, 2000); // poll every 2s

// 4. Or stream for long-running tasks
await client.sendAndStream({
  name: 'deep-research',
  input: 'Full literature review on agent identity'
}, (progress) => {
  console.log('Progress:', progress.status);
});

// 5. Cancel if needed
await client.cancelTask('task-1771838500123-456');
```

The client also includes a **Registry Client** for validating and extracting info from Agent Cards — useful for building agent discovery systems.

## Current Status

| Feature | Status | Notes |
|---|---|---|
| Agent Card discovery | ✅ Working | `GET /.well-known/agent.json` |
| Task lifecycle (send/get/cancel) | ✅ Working | JSON-RPC 2.0 compliant, real task state |
| SSE streaming | ✅ Working | Progress events for long-running tasks |
| Authentication (API keys) | ✅ Implemented | Bearer token, configurable keys |
| Request validation | ✅ Implemented | JSON-RPC 2.0 structure enforced |
| Rate limiting | ✅ Implemented | Per-key, 100 req/hr default |
| Audit logging | ✅ Implemented | Every request logged with timestamp, IP, auth status |
| Task persistence | ✅ Implemented | JSON file on disk — tasks survive restarts |
| Task not found errors | ✅ Implemented | Returns `-32002` for unknown task IDs |
| A2A Client library | ✅ Implemented | Discovery, send, poll, stream, cancel |
| Registry Client | ✅ Implemented | Agent Card validation and info extraction |
| OpenClaw Gateway integration | ❌ Not connected | Doesn't route to actual agents yet ([beads-hub-98w.9](https://github.com/brenner-axiom/beads-hub)) |
| Agent Card richness | ⚠️ Minimal | Missing skills, input schemas, pricing info |
| Real task execution | ⚠️ Stub | Tasks are stored but not dispatched to workers |

## Next Steps

These are tracked as beads in the A2A Enablement Epic (beads-hub-98w):

1. **beads-hub-98w.9** — OpenClaw integration: route A2A tasks to actual agents via Gateway (the big one)
2. **beads-hub-98w.6** — DNS-based agent discovery (`.well-known` + DNS TXT records)
3. **beads-hub-98w.7** — End-to-end demo: one agent tasks another via A2A

## How This Fits the #B4mad Vision

A2A is one leg of our interoperability triangle:

```
        A2A (Agent ↔ Agent)
           ╱         ╲
          ╱           ╲
    MCP (Agent ↔ Tool)  DAO (Agent ↔ Governance)
```

- **A2A** lets agents talk to each other across organizational boundaries
- **MCP** gives agents access to tools and data sources
- **DAO** provides decentralized governance for the agent fleet

Together, they make the "million-agent network" possible — discoverable, authenticated, and community-governed.

## References

1. [Google A2A Specification](https://google.github.io/A2A/)
2. [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification)
3. [Server-Sent Events (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
4. Romanov, "A2A + MCP Integration Analysis" — pending research bead
5. Bead: beads-hub-98w — A2A Enablement Epic
