> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.thatdevguy.in/llms.txt
> Use this file to discover all available pages before exploring further.

# A2A Protocol: Expose Agents via Agent-to-Agent Standard

> Expose your Anvil agents over Google's Agent2Agent protocol alongside REST and MCP from a single route definition, using A2AServer and a2aHttpHandler.

Anvil supports Google's Agent2Agent (A2A) protocol, letting your agents communicate with other A2A-compatible systems using a standardized interface. The same agent definition that serves a REST endpoint and an MCP server can also serve an A2A endpoint — you choose which protocols to enable. A2A, MCP, and REST share the same routing, tool registry, and observability machinery, so there is no duplication of logic.

<Note>
  The A2A specification is still evolving. Anvil isolates all protocol logic behind an adapter layer (`A2AServer` and `a2aHttpHandler`), so spec updates require changes only to the adapter — not to your agent definitions.
</Note>

## What is A2A?

Agent2Agent (A2A) is an open protocol proposed by Google for standardized communication between AI agents. It allows one agent to call another agent's capabilities using a JSON-RPC 2.0 transport. Agents advertise their skills via an Agent Card — a JSON document served at a well-known URL — and callers discover and invoke those skills via the `message/send` method.

Anvil's implementation covers:

* **Agent Card** — served at `/.well-known/agent-card.json` (and the alias `/agent.json`) so external agents can discover your skills
* **`message/send`** — synchronous task execution; calls the target skill and returns a completed Task artifact
* **`tasks/get`** — retrieve a previously created task by ID

## Setting up an A2A server

<Steps>
  <Step title="Create an AgentRegistry with your agents">
    `A2AServer` dispatches incoming A2A requests to agents by skill ID. Each skill ID must match a registered agent name.

    ```ts theme={null}
    import { AgentRegistry } from 'anvil-sdk/agent';
    import { LlmClient } from 'anvil-sdk/llm';

    const client = new LlmClient({ /* your driver config */ });
    const registry = new AgentRegistry();

    registry.register('support', {
      client,
      model: 'gpt-4o-mini',
      system: 'You are a helpful customer support agent.',
    });

    registry.register('summarizer', {
      client,
      model: 'gpt-4o-mini',
      system: 'Summarize the provided text concisely.',
    });
    ```
  </Step>

  <Step title="Create the A2AServer">
    ```ts theme={null}
    import { A2AServer } from 'anvil-sdk/a2a';

    const a2a = new A2AServer({
      registry,
      name: 'Acme AI Gateway',
      description: 'Customer-facing AI agents for Acme Corp.',
      url: 'https://api.acme.com/a2a',
      version: '1.0.0',
      // Optional: explicitly declare skills. Defaults to all registered agents.
      skills: [
        {
          id: 'support',
          name: 'Customer Support',
          description: 'Answers product and account questions.',
          tags: ['support', 'faq'],
        },
        {
          id: 'summarizer',
          name: 'Text Summarizer',
          description: 'Summarizes long text into key points.',
          tags: ['summarization', 'nlp'],
        },
      ],
      defaultSkill: 'support', // used when a request names no skill
    });
    ```
  </Step>

  <Step title="Mount the HTTP handler">
    `a2aHttpHandler` returns a standard `fetch`-compatible handler you can mount directly in your Anvil app.

    ```ts theme={null}
    import { a2aHttpHandler } from 'anvil-sdk/a2a';
    import { createApp } from 'anvil-sdk';

    const app = createApp();

    const handleA2A = a2aHttpHandler(a2a, {
      path: '/a2a',                              // JSON-RPC endpoint (default)
      cardPath: '/.well-known/agent-card.json',  // Agent Card URL (default)
    });

    // Mount the A2A handler alongside your existing routes
    app.all('/a2a', handleA2A);
    app.get('/.well-known/agent-card.json', handleA2A);
    app.get('/agent.json', handleA2A); // alias, served automatically

    export default app;
    ```
  </Step>
</Steps>

## A2AServer API

### Constructor options

<Accordion title="A2AServerOptions">
  | Option         | Type            | Required | Description                                                                                            |
  | -------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------------ |
  | `registry`     | `AgentRegistry` | ✅        | The agent registry that backs skill execution                                                          |
  | `name`         | `string`        | ✅        | Human-readable name for this A2A server, advertised in the Agent Card                                  |
  | `description`  | `string`        | ✅        | Human-readable description advertised in the Agent Card                                                |
  | `url`          | `string`        | ✅        | The public URL of the A2A endpoint, included in the Agent Card                                         |
  | `version`      | `string`        | —        | Server version string. Defaults to `'0.0.1'`                                                           |
  | `skills`       | `A2ASkill[]`    | —        | Skills to advertise. Each `id` must be a registered agent name. Defaults to all agents in the registry |
  | `defaultSkill` | `string`        | —        | Skill to invoke when `message/send` includes no `skillId`. Defaults to the first skill                 |
</Accordion>

### `server.agentCard()`

Returns the `AgentCard` object that describes this server's capabilities. The HTTP handler serves this automatically at the card path.

```ts theme={null}
const card = a2a.agentCard();
// {
//   protocolVersion: '0.3.0',
//   name: 'Acme AI Gateway',
//   capabilities: { streaming: false },
//   skills: [...],
//   ...
// }
```

### `server.handle(message)`

Process a raw JSON-RPC 2.0 request object. Returns a JSON-RPC response or `null` for notifications (requests with no `id`). You can call this directly if you are integrating with a custom transport.

```ts theme={null}
const response = await a2a.handle({
  jsonrpc: '2.0',
  id: 1,
  method: 'message/send',
  params: {
    message: {
      role: 'user',
      parts: [{ kind: 'text', text: 'What is your return policy?' }],
    },
    metadata: { skillId: 'support' },
  },
});
```

## Supported JSON-RPC methods

| Method         | Description                                                                     |
| -------------- | ------------------------------------------------------------------------------- |
| `message/send` | Send a message to a skill and receive a completed `Task` artifact synchronously |
| `tasks/get`    | Retrieve a previously created task by its `id`                                  |

Any other method returns a `-32601 Unknown method` error.

## AgentCard and skill discovery

External agents discover your capabilities by fetching the Agent Card from `/.well-known/agent-card.json`. Anvil serves it automatically:

```json theme={null}
{
  "protocolVersion": "0.3.0",
  "name": "Acme AI Gateway",
  "description": "Customer-facing AI agents for Acme Corp.",
  "url": "https://api.acme.com/a2a",
  "version": "1.0.0",
  "capabilities": { "streaming": false },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "support",
      "name": "Customer Support",
      "description": "Answers product and account questions.",
      "tags": ["support", "faq"]
    }
  ]
}
```

## A2ASkill fields

| Field         | Type       | Required | Description                                                    |
| ------------- | ---------- | -------- | -------------------------------------------------------------- |
| `id`          | `string`   | ✅        | Must match a registered agent name in the `AgentRegistry`      |
| `name`        | `string`   | ✅        | Human-readable skill name                                      |
| `description` | `string`   | ✅        | What the skill does — shown to calling agents during discovery |
| `tags`        | `string[]` | —        | Optional categorization tags                                   |

## a2aHttpHandler options

| Option     | Type     | Default                          | Description                                                                                             |
| ---------- | -------- | -------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `path`     | `string` | `'/a2a'`                         | The path for the JSON-RPC endpoint                                                                      |
| `cardPath` | `string` | `'/.well-known/agent-card.json'` | The path to serve the Agent Card. The alias `/.well-known/agent.json` is always served at this path too |

## One definition, three protocols

Because `A2AServer` wraps the same `AgentRegistry` your application already uses, enabling A2A requires only a few extra lines — your agent logic and tools are defined once and reused:

```ts theme={null}
import { createApp } from 'anvil-sdk';
import { a2aHttpHandler, A2AServer } from 'anvil-sdk/a2a';
import { registry } from './agents.js';  // shared AgentRegistry

const app = createApp();

// REST — agents are already mounted as HTTP route handlers
// A2A — same registry, A2A transport
const a2aServer = new A2AServer({ registry, name: 'My App', description: 'My Anvil app', url: 'https://example.com/a2a' });
app.all('/a2a', a2aHttpHandler(a2aServer));
app.get('/.well-known/agent-card.json', a2aHttpHandler(a2aServer));

export default app;
```

<Tip>
  Enable only the transports your architecture requires. Each transport adapter adds a single route — you incur no overhead for protocols you do not mount.
</Tip>
