# A2A Protocol
URL: /docs/runtimes/a2a

Connect to A2A (Agent-to-Agent) v1.0 protocol servers.

`@assistant-ui/react-a2a` provides a runtime adapter for the [A2A (Agent-to-Agent) v1.0 protocol](https://github.com/a2aproject/A2A), enabling your assistant-ui frontend to communicate with any A2A-compliant agent server.

Requirements \[#requirements]

* An A2A v1.0 compatible agent server
* React 18 or 19

Installation \[#installation]

<InstallCommand npm="[&#x22;@assistant-ui/react&#x22;, &#x22;@assistant-ui/react-a2a&#x22;]" />

Getting Started \[#getting-started]

<Steps>
  <Step>
    Set up the Runtime Provider \[#set-up-the-runtime-provider]

    Create a runtime provider component that connects to your A2A server.

    ```tsx title="app/MyRuntimeProvider.tsx"
    "use client";

    import { AssistantRuntimeProvider } from "@assistant-ui/react";
    import { useA2ARuntime } from "@assistant-ui/react-a2a";

    export function MyRuntimeProvider({
      children,
    }: {
      children: React.ReactNode;
    }) {
      const runtime = useA2ARuntime({
        baseUrl: "http://localhost:9999",
      });

      return (
        <AssistantRuntimeProvider runtime={runtime}>
          {children}
        </AssistantRuntimeProvider>
      );
    }
    ```
  </Step>

  <Step>
    Add the Thread component \[#add-the-thread-component]

    ```tsx title="app/page.tsx"
    import { Thread } from "@assistant-ui/react";
    import { MyRuntimeProvider } from "./MyRuntimeProvider";

    export default function Page() {
      return (
        <MyRuntimeProvider>
          <Thread />
        </MyRuntimeProvider>
      );
    }
    ```
  </Step>

  <Step>
    Setup UI Components \[#setup-ui-components]

    Follow the [UI Setup](/docs/ui) guide to setup the UI components.
  </Step>
</Steps>

A2AClient \[#a2aclient]

The built-in `A2AClient` handles all communication with the A2A server, including JSON serialization, SSE streaming, ProtoJSON enum normalization, and structured error handling.

```ts
import { A2AClient } from "@assistant-ui/react-a2a";

const client = new A2AClient({
  baseUrl: "https://my-agent.example.com",
  headers: { Authorization: "Bearer <token>" },
  tenant: "my-org", // optional, for multi-tenant servers
  extensions: ["urn:a2a:ext:my-extension"], // optional
});
```

You can pass a pre-built client to `useA2ARuntime`:

```tsx
const runtime = useA2ARuntime({ client });
```

Client Options \[#client-options]

| Option       | Type                                                       | Description                                                 |
| ------------ | ---------------------------------------------------------- | ----------------------------------------------------------- |
| `baseUrl`    | `string`                                                   | Base URL of the A2A server                                  |
| `headers`    | `Record<string, string>` or `() => Record<string, string>` | Static or dynamic headers (e.g. for auth tokens)            |
| `tenant`     | `string`                                                   | Tenant ID for multi-tenant servers (prepended to URL paths) |
| `extensions` | `string[]`                                                 | Extension URIs to negotiate via `A2A-Extensions` header     |

Client Methods \[#client-methods]

| Method                                               | Description                                              |
| ---------------------------------------------------- | -------------------------------------------------------- |
| `sendMessage(message, configuration?, metadata?)`    | Send a message (non-streaming)                           |
| `streamMessage(message, configuration?, metadata?)`  | Send a message with SSE streaming                        |
| `getTask(taskId, historyLength?)`                    | Get a task by ID                                         |
| `listTasks(request?)`                                | List tasks with filtering and pagination                 |
| `cancelTask(taskId, metadata?)`                      | Cancel an in-progress task                               |
| `subscribeToTask(taskId)`                            | Subscribe to SSE updates for a task                      |
| `getAgentCard()`                                     | Fetch the agent card from `/.well-known/agent-card.json` |
| `getExtendedAgentCard()`                             | Fetch the extended (authenticated) agent card            |
| `createTaskPushNotificationConfig(config)`           | Create a push notification config                        |
| `getTaskPushNotificationConfig(taskId, configId)`    | Get a push notification config                           |
| `listTaskPushNotificationConfigs(taskId)`            | List push notification configs                           |
| `deleteTaskPushNotificationConfig(taskId, configId)` | Delete a push notification config                        |

useA2ARuntime Options \[#usea2aruntime-options]

| Option                 | Type                          | Description                                               |
| ---------------------- | ----------------------------- | --------------------------------------------------------- |
| `client`               | `A2AClient`                   | Pre-built A2A client instance (provide this OR `baseUrl`) |
| `baseUrl`              | `string`                      | A2A server URL (creates a client automatically)           |
| `headers`              | see above                     | Headers for the auto-created client                       |
| `contextId`            | `string`                      | Initial context ID for the conversation                   |
| `configuration`        | `A2ASendMessageConfiguration` | Default send message configuration                        |
| `onError`              | `(error: Error) => void`      | Error callback                                            |
| `onCancel`             | `() => void`                  | Cancellation callback                                     |
| `adapters.attachments` | `AttachmentAdapter`           | Custom attachment handling                                |
| `adapters.speech`      | `SpeechSynthesisAdapter`      | Text-to-speech                                            |
| `adapters.feedback`    | `FeedbackAdapter`             | Feedback collection                                       |
| `adapters.history`     | `ThreadHistoryAdapter`        | Message persistence                                       |
| `adapters.threadList`  | `UseA2AThreadListAdapter`     | Thread switching                                          |

Hooks \[#hooks]

useA2ATask \[#usea2atask]

Returns the current A2A task object, including task state and status message.

```tsx
import { useA2ATask } from "@assistant-ui/react-a2a";

function TaskStatus() {
  const task = useA2ATask();

  if (!task) return null;

  return <div>Task {task.id}: {task.status.state}</div>;
}
```

useA2AArtifacts \[#usea2aartifacts]

Returns the artifacts generated by the current task.

```tsx
import { useA2AArtifacts } from "@assistant-ui/react-a2a";

function ArtifactList() {
  const artifacts = useA2AArtifacts();

  return (
    <ul>
      {artifacts.map((artifact) => (
        <li key={artifact.artifactId}>
          {artifact.name}: {artifact.parts.length} parts
        </li>
      ))}
    </ul>
  );
}
```

useA2AAgentCard \[#usea2aagentcard]

Returns the agent card fetched from the server on initialization.

```tsx
import { useA2AAgentCard } from "@assistant-ui/react-a2a";

function AgentInfo() {
  const card = useA2AAgentCard();

  if (!card) return null;

  return (
    <div>
      <h3>{card.name}</h3>
      <p>{card.description}</p>
      <div>Skills: {card.skills.map((s) => s.name).join(", ")}</div>
    </div>
  );
}
```

Task States \[#task-states]

The A2A protocol defines 9 task states. The runtime maps them to assistant-ui message statuses:

| A2A Task State   | Description            | Message Status           |
| ---------------- | ---------------------- | ------------------------ |
| `unspecified`    | Unknown/default state  | `running`                |
| `submitted`      | Task acknowledged      | `running`                |
| `working`        | Task in progress       | `running`                |
| `completed`      | Task finished          | `complete`               |
| `failed`         | Task errored           | `incomplete (error)`     |
| `canceled`       | Task cancelled         | `incomplete (cancelled)` |
| `rejected`       | Agent declined task    | `incomplete (error)`     |
| `input_required` | Agent needs user input | `requires-action`        |
| `auth_required`  | Authentication needed  | `requires-action`        |

<Callout type="info">
  When a task enters `input_required`, the user can continue the conversation normally. The runtime will send the next message with the same `taskId` to resume the task.
</Callout>

Artifacts \[#artifacts]

A2A agents can produce artifacts (files, code, data) alongside their responses. Artifacts are accumulated during streaming and accessible via the `useA2AArtifacts` hook.

The runtime supports:

* **Incremental artifact streaming** via `append` mode
* **Artifact completion notification** via `onArtifactComplete` callback
* **Automatic reset** of artifacts on each new run

```tsx
const runtime = useA2ARuntime({
  baseUrl: "http://localhost:9999",
  onArtifactComplete: (artifact) => {
    console.log("Artifact ready:", artifact.name);
  },
});
```

Streaming vs Non-Streaming \[#streaming-vs-non-streaming]

The runtime automatically selects the communication mode based on the agent's capabilities:

* If the agent card indicates `capabilities.streaming: true` (or unset), the runtime uses `POST /message:stream` with SSE
* If `capabilities.streaming: false`, the runtime falls back to `POST /message:send`

Error Handling \[#error-handling]

The client throws `A2AError` instances with structured error information following the `google.rpc.Status` format:

```tsx
import { A2AError } from "@assistant-ui/react-a2a";

const runtime = useA2ARuntime({
  baseUrl: "http://localhost:9999",
  onError: (error) => {
    if (error instanceof A2AError) {
      console.log(error.code);    // HTTP status code
      console.log(error.status);  // e.g. "NOT_FOUND"
      console.log(error.details); // google.rpc.ErrorInfo details
    }
  },
});
```

Multi-Tenancy \[#multi-tenancy]

For multi-tenant A2A servers, pass a `tenant` option to the client:

```ts
const client = new A2AClient({
  baseUrl: "https://agent.example.com",
  tenant: "my-org",
});
```

This prepends `/{tenant}` to all API paths (e.g. `/my-org/message:send`).

Features \[#features]

| Feature                      | Supported |
| ---------------------------- | --------- |
| Streaming (SSE)              | Yes       |
| Non-streaming fallback       | Yes       |
| All 9 task states            | Yes       |
| Artifacts (text, data, file) | Yes       |
| Agent card discovery         | Yes       |
| Multi-tenancy                | Yes       |
| Structured errors            | Yes       |
| Push notifications CRUD      | Yes       |
| Extension negotiation        | Yes       |
| Task cancellation            | Yes       |
| Message editing              | Yes       |
| Message reload               | Yes       |
| History persistence          | Yes       |
| Thread list management       | Yes       |
