A2A Protocol

Client and hooks

A2AClient, useA2ARuntime options, hooks, task states, artifacts, errors.

Deep dive on the runtime's API surface. Start with overview and quickstart if you have not already.

A2AClient

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

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

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

Pass a pre-built client to useA2ARuntime:

const runtime = useA2ARuntime({ client });

Client options

OptionTypeDescription
baseUrlstringBase URL of the A2A server.
basePathstringOptional path prefix for API endpoints (e.g. "/v1"). Does not affect agent card discovery.
headersRecord<string, string> or () => Record<string, string>Static or dynamic headers (e.g. for auth tokens).
tenantstringTenant ID for multi-tenant servers (prepended to URL paths).
extensionsstring[]Extension URIs to negotiate via A2A-Extensions header.

Client methods

MethodDescription
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

Pass either a pre-built client or a baseUrl (the runtime creates a client for you). Other options layer on top.

OptionTypeDescription
clientA2AClientPre-built A2A client instance (provide this OR baseUrl).
baseUrlstringA2A server URL (creates a client automatically).
basePathstringPath prefix for API endpoints. Only used with baseUrl.
tenantstringTenant ID for multi-tenant servers. Only used with baseUrl.
headersRecord<string, string> or () => Record<string, string>Headers for the auto-created client.
extensionsstring[]Extension URIs to negotiate. Only used with baseUrl.
contextIdstringInitial context ID for the conversation.
configurationA2ASendMessageConfigurationDefault send message configuration.
onError(error: Error) => voidError callback.
onCancel() => voidCancellation callback.
onArtifactComplete(artifact: A2AArtifact) => voidFired when an incremental artifact finishes.
adapters.attachmentsAttachmentAdapterCustom attachment handling. See adapters.
adapters.speechSpeechSynthesisAdapterText-to-speech. See adapters.
adapters.feedbackFeedbackAdapterFeedback collection. See adapters.
adapters.historyThreadHistoryAdapterMessage persistence. See adapters.
adapters.threadListUseA2AThreadListAdapterThread switching. See threads.

Hooks

Task state

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

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>
  );
}

Artifacts list

useA2AArtifacts returns the artifacts generated by the current task.

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>
  );
}

Agent card

useA2AAgentCard returns the agent card fetched from the server on initialization.

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

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

A2A task stateDescriptionMessage status
unspecifiedUnknown or default state.running
submittedTask acknowledged.running
workingTask in progress.running
completedTask finished.complete
failedTask errored.incomplete (error)
canceledTask cancelled.incomplete (cancelled)
rejectedAgent declined task.incomplete (error)
input_requiredAgent needs user input.requires-action
auth_requiredAuthentication needed.requires-action

When a task enters input_required, the user can continue the conversation normally. The runtime sends the next message with the same taskId to resume the task.

Artifacts

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

The runtime supports:

  • Incremental artifact streaming via append mode.
  • Artifact completion notification via the onArtifactComplete callback.
  • Automatic reset of artifacts on each new run.
const runtime = useA2ARuntime({
  baseUrl: "http://localhost:9999",
  onArtifactComplete: (artifact) => {
    console.log("Artifact ready:", artifact.name);
  },
});

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 the field is unset), the runtime uses POST /message:stream with SSE.
  • If capabilities.streaming: false, the runtime falls back to POST /message:send.

Error handling

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

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

For multi-tenant A2A servers, pass a tenant option:

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).

Feature support

FeatureSupported
Streaming (SSE)Yes
Non-streaming fallbackYes
All 9 task statesYes
Artifacts (text, data, file)Yes
Agent card discoveryYes
Multi-tenancyYes
Structured errorsYes
Push notifications CRUDYes
Extension negotiationYes
Task cancellationYes
Message editingYes
Message reloadYes
History persistenceVia history adapter
Thread list managementVia thread list adapter