Class Client
- All Implemented Interfaces:
AutoCloseable
The Client class provides the primary API for sending messages to agents, managing tasks, configuring push notifications, and subscribing to task updates. It abstracts the underlying transport protocol (JSON-RPC, gRPC, REST) and provides a consistent interface for all agent interactions.
Key capabilities:
- Message exchange: Send messages to agents and receive responses via event consumers
- Task management: Query, list, and cancel tasks
- Streaming support: Real-time event streaming when both client and server support it
- Push notifications: Configure webhooks for task state changes
- Resubscription: Resume receiving events for ongoing tasks after disconnection
Resource management: Client implements AutoCloseable and should be used with
try-with-resources to ensure proper cleanup:
AgentCard card = A2A.getAgentCard("http://localhost:9999");
try (Client client = Client.builder(card)
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder())
.addConsumer((event, agentCard) -> {
if (event instanceof MessageEvent me) {
System.out.println("Response: " + me.getMessage().parts());
}
})
.build()) {
// Send messages - client automatically closed when done
client.sendMessage(A2A.toUserMessage("Tell me a joke"));
}
Manual resource management: If not using try-with-resources, call close()
explicitly when done:
Client client = Client.builder(card)
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder())
.addConsumer((event, agentCard) -> {
// Handle events
})
.build();
try {
client.sendMessage(A2A.toUserMessage("Tell me a joke"));
} finally {
client.close(); // Always close to release resources
}
Event consumption model: Responses from the agent are delivered as ClientEvent
instances to the registered consumers:
MessageEvent- contains agent response messages with content partsTaskEvent- contains complete task state (typically final state)TaskUpdateEvent- contains incremental task updates (status or artifact changes)
Streaming vs blocking: The client supports two communication modes:
- Blocking:
sendMessage(org.a2aproject.sdk.spec.Message, java.util.List<java.util.function.BiConsumer<org.a2aproject.sdk.client.ClientEvent, org.a2aproject.sdk.spec.AgentCard>>, java.util.function.Consumer<java.lang.Throwable>, org.a2aproject.sdk.client.transport.spi.interceptors.ClientCallContext)blocks until the agent completes the task - Streaming:
sendMessage(org.a2aproject.sdk.spec.Message, java.util.List<java.util.function.BiConsumer<org.a2aproject.sdk.client.ClientEvent, org.a2aproject.sdk.spec.AgentCard>>, java.util.function.Consumer<java.lang.Throwable>, org.a2aproject.sdk.client.transport.spi.interceptors.ClientCallContext)returns immediately, events delivered asynchronously to consumers as the agent processes the request
ClientConfig.isStreaming() AND AgentCapabilities.streaming().
Both must be true for streaming mode; otherwise blocking mode is used.
Task lifecycle example:
client.addConsumer((event, card) -> {
if (event instanceof TaskUpdateEvent tue) {
TaskState state = tue.getTask().status().state();
switch (state) {
case SUBMITTED -> System.out.println("Task created");
case WORKING -> System.out.println("Agent is processing...");
case COMPLETED -> System.out.println("Task finished");
case FAILED -> System.err.println("Task failed: " +
tue.getTask().status().message());
}
// Check for new artifacts
if (tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent update) {
Artifact artifact = update.artifact();
System.out.println("New content: " + artifact.parts());
}
}
});
Push notifications: Configure webhooks to receive task updates:
// Configure push notifications for a task
TaskPushNotificationConfig pushConfig = TaskPushNotificationConfig.builder()
.id("config-1")
.url("https://my-app.com/webhooks/task-updates")
.authentication(new AuthenticationInfo("Bearer", "my-token"))
.build();
// Send message with push notifications
client.sendMessage(
A2A.toUserMessage("Process this data"),
pushConfig,
null, // metadata
null // context
);
Resubscription after disconnection:
// Original request
client.sendMessage(A2A.toUserMessage("Long-running task"));
// ... client disconnects ...
// Later, reconnect and resume receiving events
String taskId = "task-123"; // From original request
client.subscribeToTask(
new TaskIdParams(taskId),
List.of((event, card) -> {
// Process events from where we left off
}),
null, // error handler
null // context
);
Thread safety: Client instances are thread-safe and can be used concurrently from multiple threads. Event consumers must also be thread-safe as they may be invoked concurrently for different tasks.
Resource management: Clients hold resources (HTTP connections, gRPC channels, etc.) and should be closed when no longer needed:
try (Client client = Client.builder(card)...build()) {
client.sendMessage(...);
} // Automatically closed
-
Field Summary
Fields inherited from class org.a2aproject.sdk.client.AbstractClient
consumers, streamingErrorHandler -
Method Summary
Modifier and TypeMethodDescriptionstatic ClientBuilderCreate a new builder for constructing a client instance.cancelTask(CancelTaskParams request, @Nullable ClientCallContext context) Request cancellation of a task.voidclose()Close this client and release all associated resources.createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) Configure push notifications for a task.voiddeleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) Delete push notification configurations.getExtendedAgentCard(@Nullable String tenant, @Nullable ClientCallContext context) Retrieve the agent's extended agent card.getTask(TaskQueryParams request, @Nullable ClientCallContext context) Retrieve a specific task by ID.getTaskPushNotificationConfiguration(GetTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) Retrieve the push notification configuration for a task.listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigsParams request, @Nullable ClientCallContext context) List all push notification configurations, optionally filtered by task or context.listTasks(ListTasksParams request, @Nullable ClientCallContext context) List tasks for the current session or context.voidsendMessage(@NonNull Message request, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) Send a message to the remote agent.voidsendMessage(@NonNull Message request, @Nullable TaskPushNotificationConfig pushNotificationConfiguration, @Nullable Map<String, Object> metadata, @Nullable ClientCallContext context) Send a message to the agent.voidsendMessage(@NonNull MessageSendParams messageSendParams, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) Send a message to the remote agent.voidsubscribeToTask(@NonNull TaskIdParams request, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) Subscribe to an existing task to receive remaining events.Methods inherited from class org.a2aproject.sdk.client.AbstractClient
cancelTask, createTaskPushNotificationConfiguration, deleteTaskPushNotificationConfigurations, getExtendedAgentCard, getExtendedAgentCard, getExtendedAgentCard, getStreamingErrorHandler, getTask, getTaskPushNotificationConfiguration, listTaskPushNotificationConfigurations, listTasks, sendMessage, sendMessage, sendMessage, sendMessage, subscribeToTask, subscribeToTask, subscribeToTask
-
Method Details
-
builder
Create a new builder for constructing a client instance.This is the primary entry point for creating clients. The builder provides a fluent API for configuring transports, event consumers, and client behavior.
Example:
AgentCard card = A2A.getAgentCard("http://localhost:9999"); Client client = Client.builder(card) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .addConsumer((event, agentCard) -> processEvent(event)) .build();- Parameters:
agentCard- the agent card describing the agent to communicate with- Returns:
- a new builder instance
- See Also:
-
sendMessage
public void sendMessage(@NonNull Message request, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientExceptionDescription copied from class:AbstractClientSend a message to the remote agent. This method will automatically use the streaming or non-streaming approach as determined by the server's agent card and the client configuration. The specified client consumers will be used to handle messages, tasks, and update events received from the remote agent. The specified streaming error handler will be used if an error occurs during streaming. The configured client push notification configuration will get used for streaming.- Specified by:
sendMessagein classAbstractClient- Parameters:
request- the messageconsumers- a list of consumers to pass responses from the remote agent tostreamingErrorHandler- an error handler that should be used for the streaming case if an error occurscontext- optional client call context for the request- Throws:
A2AClientException- if sending the message fails for any reason
-
sendMessage
public void sendMessage(@NonNull Message request, @Nullable TaskPushNotificationConfig pushNotificationConfiguration, @Nullable Map<String, Object> metadata, @Nullable ClientCallContext context) throws A2AClientExceptionSend a message to the agent.This is the primary method for communicating with an agent. The behavior depends on whether streaming is enabled:
- Streaming mode: Returns immediately, events delivered asynchronously to consumers
- Blocking mode: Blocks until the agent completes the task, then invokes consumers
ClientConfig.isStreaming()ANDAgentCapabilities.streaming()aretrue.Simple example:
Message userMessage = A2A.toUserMessage("What's the weather?"); client.sendMessage(userMessage, null, null, null); // Events delivered to consumers registered during client constructionWith push notifications:
TaskPushNotificationConfig pushConfig = TaskPushNotificationConfig.builder() .id("config-1") .url("https://my-app.com/webhook") .authentication(new AuthenticationInfo("Bearer", "token")) .build(); client.sendMessage(userMessage, pushConfig, null, null);With metadata:
Map<String, Object> metadata = Map.of( "userId", "user-123", "sessionId", "session-456" ); client.sendMessage(userMessage, null, metadata, null);- Specified by:
sendMessagein classAbstractClient- Parameters:
request- the message to send (required)pushNotificationConfiguration- webhook configuration for task updates (optional)metadata- custom metadata to attach to the request (optional)context- custom call context for request interceptors (optional)- Throws:
A2AClientException- if the message cannot be sent or if the agent returns an error- See Also:
-
sendMessage
public void sendMessage(@NonNull MessageSendParams messageSendParams, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientExceptionDescription copied from class:AbstractClientSend a message to the remote agent. This method will automatically use the streaming or non-streaming approach as determined by the server's agent card and the client configuration. The specified client consumers will be used to handle messages, tasks, and update events received from the remote agent. The specified streaming error handler will be used if an error occurs during streaming. The configured client push notification configuration will get used for streaming.- Specified by:
sendMessagein classAbstractClient- Parameters:
messageSendParams- the request parametersconsumers- a list of consumers to pass responses from the remote agent tostreamingErrorHandler- an error handler that should be used for the streaming case if an error occurscontext- optional client call context for the request- Throws:
A2AClientException- if sending the message fails for any reason
-
getTask
public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException Retrieve a specific task by ID.This method queries the agent for the current state of a task. It's useful for:
- Checking the status of a task after disconnection
- Retrieving task results without subscribing to events
- Polling for task completion (when streaming is not available)
Example:
Task task = client.getTask(new TaskQueryParams("task-123")); if (task.status().state() == TaskState.COMPLETED) { Artifact result = task.artifact(); System.out.println("Result: " + result.parts()); } else if (task.status().state() == TaskState.FAILED) { System.err.println("Task failed: " + task.status().message()); }- Specified by:
getTaskin classAbstractClient- Parameters:
request- the task query parameters containing the task IDcontext- custom call context for request interceptors (optional)- Returns:
- the current task state
- Throws:
A2AClientException- if the task is not found or if a communication error occurs- See Also:
-
listTasks
public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException List tasks for the current session or context.This method retrieves multiple tasks based on filter criteria. Useful for:
- Viewing all tasks in a session/context
- Finding tasks by state (e.g., all failed tasks)
- Paginating through large task lists
Example:
// List all tasks for a context ListTasksParams params = new ListTasksParams( "session-123", // contextId null, // state filter (null = all states) 10, // limit null // offset ); ListTasksResult result = client.listTasks(params); for (Task task : result.tasks()) { System.out.println(task.id() + ": " + task.status().state()); }- Specified by:
listTasksin classAbstractClient- Parameters:
request- the list parameters with optional filterscontext- custom call context for request interceptors (optional)- Returns:
- the list of tasks matching the criteria
- Throws:
A2AClientException- if a communication error occurs- See Also:
-
cancelTask
public Task cancelTask(CancelTaskParams request, @Nullable ClientCallContext context) throws A2AClientException Request cancellation of a task.This method sends a cancellation request to the agent for the specified task. The agent may or may not honor the request depending on its implementation and the task's current state.
Important notes:
- Cancellation is a request, not a guarantee - agents may decline or be unable to cancel
- Some agents don't support cancellation and will return
UnsupportedOperationError - Tasks in final states (COMPLETED, FAILED, CANCELED) cannot be canceled
- The returned task will have state CANCELED if the cancellation succeeded
Example:
try { Task canceledTask = client.cancelTask(new TaskIdParams("task-123")); if (canceledTask.status().state() == TaskState.CANCELED) { System.out.println("Task successfully canceled"); } } catch (A2AClientException e) { if (e.getCause() instanceof UnsupportedOperationError) { System.err.println("Agent does not support cancellation"); } else if (e.getCause() instanceof TaskNotFoundError) { System.err.println("Task not found"); } }- Specified by:
cancelTaskin classAbstractClient- Parameters:
request- the task ID to cancelcontext- custom call context for request interceptors (optional)- Returns:
- the task with CANCELED status if successful
- Throws:
A2AClientException- if the task cannot be canceled or if a communication error occurs- See Also:
-
createTaskPushNotificationConfiguration
public TaskPushNotificationConfig createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException Configure push notifications for a task.Push notifications allow your application to receive task updates via webhook instead of maintaining an active connection. When configured, the agent will POST events to the specified URL as the task progresses.
Example:
TaskPushNotificationConfig config = TaskPushNotificationConfig.builder() .id("config-1") .taskId("task-123") .url("https://my-app.com/webhooks/task-updates") .authentication(new AuthenticationInfo("Bearer", "my-webhook-secret")) .build(); client.createTaskPushNotificationConfiguration(config);- Specified by:
createTaskPushNotificationConfigurationin classAbstractClient- Parameters:
request- the push notification configuration for the taskcontext- custom call context for request interceptors (optional)- Returns:
- the stored configuration (may include server-assigned IDs)
- Throws:
A2AClientException- if the configuration cannot be set- See Also:
-
getTaskPushNotificationConfiguration
public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException Retrieve the push notification configuration for a task.Example:
GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams("task-123"); TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration(params); System.out.println("Webhook URL: " + config.url());- Specified by:
getTaskPushNotificationConfigurationin classAbstractClient- Parameters:
request- the parameters specifying which task's configuration to retrievecontext- custom call context for request interceptors (optional)- Returns:
- the push notification configuration for the task
- Throws:
A2AClientException- if the configuration cannot be retrieved- See Also:
-
listTaskPushNotificationConfigurations
public ListTaskPushNotificationConfigsResult listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigsParams request, @Nullable ClientCallContext context) throws A2AClientException List all push notification configurations, optionally filtered by task or context.Example:
// List all configurations for a context ListTaskPushNotificationConfigsParams params = new ListTaskPushNotificationConfigsParams("session-123", null, 10, null); ListTaskPushNotificationConfigsResult result = client.listTaskPushNotificationConfigurations(params); for (TaskPushNotificationConfig config : result.configurations()) { System.out.println("Task " + config.taskId() + " -> " + config.url()); }- Specified by:
listTaskPushNotificationConfigurationsin classAbstractClient- Parameters:
request- the list parameters with optional filterscontext- custom call context for request interceptors (optional)- Returns:
- the list of push notification configurations
- Throws:
A2AClientException- if the configurations cannot be retrieved- See Also:
-
deleteTaskPushNotificationConfigurations
public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException Delete push notification configurations.This method removes push notification configurations for the specified tasks or context. After deletion, the agent will stop sending webhook notifications for those tasks.
Example:
// Delete configuration for a specific task DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams( null, // contextId (null = not filtering by context) List.of("task-123", "task-456") // specific task IDs ); client.deleteTaskPushNotificationConfigurations(params);- Specified by:
deleteTaskPushNotificationConfigurationsin classAbstractClient- Parameters:
request- the delete parameters specifying which configurations to removecontext- custom call context for request interceptors (optional)- Throws:
A2AClientException- if the configurations cannot be deleted- See Also:
-
subscribeToTask
public void subscribeToTask(@NonNull TaskIdParams request, @NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientExceptionSubscribe to an existing task to receive remaining events.This method is useful when a client disconnects during a long-running task and wants to resume receiving events without starting a new task. The agent will deliver any events that occurred since the original subscription.
Requirements:
- Both
ClientConfig.isStreaming()andAgentCapabilities.streaming()must betrue - The task must still exist and not be in a final state (or the agent must support historical event replay)
Example:
// Original request (client1) client1.sendMessage(A2A.toUserMessage("Analyze this dataset")); String taskId = ...; // Save task ID from TaskEvent // ... client1 disconnects ... // Later, reconnect (client2) client2.subscribeToTask( new TaskIdParams(taskId), List.of((event, card) -> { if (event instanceof TaskUpdateEvent tue) { System.out.println("Resumed - status: " + tue.getTask().status().state()); } }), throwable -> System.err.println("Subscribe error: " + throwable), null );- Specified by:
subscribeToTaskin classAbstractClient- Parameters:
request- the task ID to subscribe toconsumers- the event consumers for processing events (required)streamingErrorHandler- error handler for streaming errors (optional)context- custom call context for request interceptors (optional)- Throws:
A2AClientException- if subscription is not supported or if the task cannot be found
- Both
-
getExtendedAgentCard
public AgentCard getExtendedAgentCard(@Nullable String tenant, @Nullable ClientCallContext context) throws A2AClientException Retrieve the agent's extended agent card.This method fetches the extended agent card from the agent (if the extendedAgentCard capability is supported). The card may have changed since client construction (e.g., new skills added, capabilities updated). The client's internal reference is updated to the newly retrieved card.
Example:
AgentCard updatedCard = client.getExtendedAgentCard(); System.out.println("Agent version: " + updatedCard.version()); System.out.println("Skills: " + updatedCard.skills().size());- Specified by:
getExtendedAgentCardin classAbstractClient- Parameters:
tenant- Optional tenantcontext- custom call context for request interceptors (optional)- Returns:
- the agent's extended agent card
- Throws:
A2AClientException- if the extended agent card cannot be retrieved- See Also:
-
close
public void close()Close this client and release all associated resources.This method closes the underlying transport (HTTP connections, gRPC channels, etc.) and releases any other resources held by the client. After calling this method, the client instance should not be used further.
Important: Always close clients when done to avoid resource leaks:
Client client = Client.builder(card)...build(); try { client.sendMessage(...); } finally { client.close(); } // Or use try-with-resources if Client implements AutoCloseable- Specified by:
closein interfaceAutoCloseable- Specified by:
closein classAbstractClient
-