Class Client

All Implemented Interfaces:
AutoCloseable

public class Client extends AbstractClient
A client for communicating with A2A agents using the Agent2Agent Protocol.

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 parts
  • TaskEvent - 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:

The mode is determined by 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
 
See Also:
  • Method Details

    • builder

      public static ClientBuilder builder(AgentCard agentCard)
      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 A2AClientException
      Description copied from class: AbstractClient
      Send 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:
      sendMessage in class AbstractClient
      Parameters:
      request - the message
      consumers - a list of consumers to pass responses from the remote agent to
      streamingErrorHandler - an error handler that should be used for the streaming case if an error occurs
      context - 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 A2AClientException
      Send 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
      Streaming mode is active when both ClientConfig.isStreaming() AND AgentCapabilities.streaming() are true.

      Simple example:

      
       Message userMessage = A2A.toUserMessage("What's the weather?");
       client.sendMessage(userMessage, null, null, null);
       // Events delivered to consumers registered during client construction
       

      With 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:
      sendMessage in class AbstractClient
      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 A2AClientException
      Description copied from class: AbstractClient
      Send 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:
      sendMessage in class AbstractClient
      Parameters:
      messageSendParams - the request parameters
      consumers - a list of consumers to pass responses from the remote agent to
      streamingErrorHandler - an error handler that should be used for the streaming case if an error occurs
      context - 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:
      getTask in class AbstractClient
      Parameters:
      request - the task query parameters containing the task ID
      context - 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:
      listTasks in class AbstractClient
      Parameters:
      request - the list parameters with optional filters
      context - 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:
      cancelTask in class AbstractClient
      Parameters:
      request - the task ID to cancel
      context - 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:
      createTaskPushNotificationConfiguration in class AbstractClient
      Parameters:
      request - the push notification configuration for the task
      context - 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:
      getTaskPushNotificationConfiguration in class AbstractClient
      Parameters:
      request - the parameters specifying which task's configuration to retrieve
      context - 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:
      listTaskPushNotificationConfigurations in class AbstractClient
      Parameters:
      request - the list parameters with optional filters
      context - 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:
      deleteTaskPushNotificationConfigurations in class AbstractClient
      Parameters:
      request - the delete parameters specifying which configurations to remove
      context - 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 A2AClientException
      Subscribe 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:

      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:
      subscribeToTask in class AbstractClient
      Parameters:
      request - the task ID to subscribe to
      consumers - 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
    • 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:
      getExtendedAgentCard in class AbstractClient
      Parameters:
      tenant - Optional tenant
      context - 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:
      close in interface AutoCloseable
      Specified by:
      close in class AbstractClient