Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

752 Commits

Repository files navigation

A2A Java SDK

License

A2A Logo

A Java library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

Installation

You can build the A2A Java SDK using mvn:

mvn clean install

Regeneration of gRPC files

We copy https://github.com/a2aproject/A2A/blob/main/specification/grpc/a2a.proto to the spec-grpc/ project, and adjust the java_package option to be as follows:

option java_package = "org.a2aproject.sdk.grpc";

Then build the spec-grpc module with mvn clean install -Dskip.protobuf.generate=false to regenerate the gRPC classes in the org.a2aproject.sdk.grpc package.

Examples

You can find examples of how to use the A2A Java SDK in the a2a-samples repository.

More examples will be added soon.

A2A Server

The A2A Java SDK provides a Java server implementation of the Agent2Agent (A2A) Protocol. To run your agentic Java application as an A2A server, simply follow the steps below.

1. Add an A2A Java SDK Server Maven dependency to your project

Adding a dependency on an A2A Java SDK Server will provide access to the core classes that make up the A2A specification and allow you to run your agentic Java application as an A2A server agent.

The A2A Java SDK provides reference A2A server implementations based on Quarkus for use with our tests and examples. However, the project is designed in such a way that it is trivial to integrate with various Java runtimes.

Server Integrations contains a list of community contributed integrations of the server with various runtimes. You might be able to use one of these for your target runtime, or you can use them as inspiration to create your own.

Server Transports

The A2A Java SDK Reference Server implementations support the following transports:

  • JSON-RPC 2.0
  • gRPC
  • HTTP+JSON/REST

To use the reference implementation with the JSON-RPC protocol, add the following dependency to your project:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases --> <version>${org.a2aproject.sdk.version}</version>
</dependency>

To use the reference implementation with the gRPC protocol, add the following dependency to your project:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-grpc</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases --> <version>${org.a2aproject.sdk.version}</version>
</dependency>

To use the reference implementation with the HTTP+JSON/REST protocol, add the following dependency to your project:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-rest</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases --> <version>${org.a2aproject.sdk.version}</version>
</dependency>

Note that you can add more than one of the above dependencies to your project depending on the transports you'd like to support.

2. Add a class that creates an A2A Agent Card

importorg.a2aproject.sdk.server.PublicAgentCard;
importorg.a2aproject.sdk.spec.AgentCapabilities;
importorg.a2aproject.sdk.spec.AgentCard;
importorg.a2aproject.sdk.spec.AgentInterface;
importorg.a2aproject.sdk.spec.AgentSkill;
importorg.a2aproject.sdk.spec.TransportProtocol;
...
@ApplicationScopedpublicclassWeatherAgentCardProducer {
privatestaticfinalStringAGENT_URL = "http://localhost:10001";
@Produces@PublicAgentCardpublicAgentCardagentCard() {
returnAgentCard.builder()
.name("Weather Agent")
.description("Helps with weather")
.supportedInterfaces(List.of(
newAgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL)))
.version("1.0.0")
.capabilities(AgentCapabilities.builder()
.streaming(true)
.pushNotifications(false)
.build())
.defaultInputModes(Collections.singletonList("text"))
.defaultOutputModes(Collections.singletonList("text"))
.skills(Collections.singletonList(AgentSkill.builder()
.id("weather_search")
.name("Search weather")
.description("Helps with weather in cities or states")
.tags(Collections.singletonList("weather"))
.examples(List.of("weather in LA, CA"))
.build()))
.build();
}
}

3. Add a class that creates an A2A Agent Executor

importorg.a2aproject.sdk.server.agentexecution.AgentExecutor;
importorg.a2aproject.sdk.server.agentexecution.RequestContext;
importorg.a2aproject.sdk.server.events.EventQueue;
importorg.a2aproject.sdk.server.tasks.AgentEmitter;
importorg.a2aproject.sdk.spec.JSONRPCError;
importorg.a2aproject.sdk.spec.Message;
importorg.a2aproject.sdk.spec.Part;
importorg.a2aproject.sdk.spec.Task;
importorg.a2aproject.sdk.spec.TaskNotCancelableError;
importorg.a2aproject.sdk.spec.TaskState;
importorg.a2aproject.sdk.spec.TextPart;
...
@ApplicationScopedpublicclassWeatherAgentExecutorProducer {
@InjectWeatherAgentweatherAgent;
@ProducespublicAgentExecutoragentExecutor() {
returnnewWeatherAgentExecutor(weatherAgent);
}
privatestaticclassWeatherAgentExecutorimplementsAgentExecutor {
privatefinalWeatherAgentweatherAgent;
publicWeatherAgentExecutor(WeatherAgentweatherAgent) {
this.weatherAgent = weatherAgent;
}
@Overridepublicvoidexecute(RequestContextcontext, AgentEmitteragentEmitter) throwsJSONRPCError {
// mark the task as submitted and start working on itif (context.getTask() == null) {
agentEmitter.submit();
}
agentEmitter.startWork();
// extract the text from the messageStringuserMessage = extractTextFromMessage(context.getMessage());
// call the weather agent with the user's messageStringresponse = weatherAgent.chat(userMessage);
// create the response partTextPartresponsePart = newTextPart(response);
List<Part<?>> parts = List.of(responsePart);
// add the response as an artifact and complete the taskagentEmitter.addArtifact(parts);
agentEmitter.complete();
}
@Overridepublicvoidcancel(RequestContextcontext, AgentEmitteragentEmitter) throwsJSONRPCError {
Tasktask = context.getTask();
if (task.getStatus().state() == TaskState.CANCELED) {
// task already cancelledthrownewTaskNotCancelableError();
}
if (task.getStatus().state() == TaskState.COMPLETED) {
// task already completedthrownewTaskNotCancelableError();
}
// cancel the taskagentEmitter.cancel();
}
privateStringextractTextFromMessage(Messagemessage) {
StringBuildertextBuilder = newStringBuilder();
for (Part<?> part : message.parts()) {
if (partinstanceofTextParttextPart) {
textBuilder.append(textPart.text());
}
}
returntextBuilder.toString();
}
}
}

4. Configuration System

The A2A Java SDK uses a flexible configuration system that works across different frameworks.

Default behavior: Configuration values come from META-INF/a2a-defaults.properties files on the classpath (provided by core modules and extras). These defaults work out of the box without any additional setup.

Customizing configuration:

  • Quarkus/MicroProfile Config users: Add the microprofile-config integration to override defaults via application.properties, environment variables, or system properties
  • Spring/other frameworks: See the integration module README for how to implement a custom A2AConfigProvider
  • Reference implementations: Already include the MicroProfile Config integration

Configuration Properties

Executor Settings (Optional)

The SDK uses a dedicated executor for async operations like streaming. Default: 5 core threads, 50 max threads.

# Core thread pool size for the @Internal executor (default: 5)a2a.executor.core-pool-size=5
# Maximum thread pool size (default: 50)a2a.executor.max-pool-size=50
# Thread keep-alive time in seconds (default: 60)a2a.executor.keep-alive-seconds=60

Blocking Call Timeouts (Optional)

# Timeout for agent execution in blocking calls (default: 30 seconds)a2a.blocking.agent.timeout.seconds=30
# Timeout for event consumption in blocking calls (default: 5 seconds)a2a.blocking.consumption.timeout.seconds=5

Why this matters:

  • Streaming Performance: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
  • Resource Management: The dedicated executor prevents streaming operations from competing with the ForkJoinPool.
  • Concurrency: In production with high concurrent streaming, increase pool sizes accordingly.
  • Agent Timeouts: LLM-based agents may need longer timeouts (60-120s) compared to simple agents.

Note: The reference server implementations (Quarkus-based) automatically include the MicroProfile Config integration, so properties work out of the box in application.properties.

Serving Older Protocol Versions (Backward Compatibility)

The A2A Java SDK includes compatibility layers that allow your server to accept requests from clients using older protocol versions. Each compatibility layer is a separate set of modules that you add to your project as needed. No changes to your AgentExecutor are needed — the compatibility layer converts older protocol requests to v1.0 internally before delegating to your agent.

Adding v0.3 Protocol Support

To enable v0.3 support, add the v0.3 compat reference module for your chosen transport alongside (or instead of) the v1.0 reference module:

<!-- v0.3 JSON-RPC support -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-reference-jsonrpc</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases -->
<version>${org.a2aproject.sdk.version}</version>
</dependency>
<!-- v0.3 REST support -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-reference-rest</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>
<!-- v0.3 gRPC support -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-reference-grpc</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>

For example, a server that supports v1.0 and v0.3 over JSON-RPC would include both:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-reference-jsonrpc</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>

A server that only needs to support v0.3 would include only the compat dependency.

Multi-Version Convenience Modules

For JSON-RPC and REST, multi-version modules are provided that bundle all supported protocol versions together with version-dispatching routes:

<!-- Includes v1.0 + v0.3 JSON-RPC support with automatic version routing -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-multiversion-jsonrpc</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>
<!-- Includes v1.0 + v0.3 REST support with automatic version routing -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-multiversion-rest</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>

These are a convenience — they transitively include all the individual compat reference modules.

How Version Routing Works

  • JSON-RPC and REST: When serving multiple protocol versions, version routing inspects the A2A-Version HTTP header on each request. If the header is "1.0", the request is routed to the v1.0 handler. If it is "0.3" or absent, the request is routed to the v0.3 handler.
  • gRPC: Version dispatch is implicit — v0.3 clients use the a2a.v1 protobuf package and v1.0 clients use lf.a2a.v1, so requests are routed to the correct service automatically.
  • Agent card: When both v1.0 and v0.3 are enabled, the v1.0 AgentCard takes precedence and is served at /.well-known/agent-card.json. The v0.3 AgentCard_v0_3 is ignored. If only v0.3 is enabled, the v0.3 agent card is used. If only v1.0 is enabled, the v1.0 agent card is used as-is.

Making the v1.0 Agent Card Compatible with v0.3 Clients

When serving both protocol versions, you need to ensure the v1.0 agent card contains fields that v0.3 clients expect. Existing v0.3 client implementations (in any language) look for url, preferredTransport, and additionalInterfaces with transport/url entries — fields that don't exist in the v1.0 format by default.

To make your v1.0 AgentCard parsable by v0.3 clients, set these fields on the builder:

AgentCardcard = AgentCard.builder()
.name("My Agent")
// ... other v1.0 fields ...
.supportedInterfaces(List.of(
newAgentInterface("jsonrpc", "http://localhost:9999")))
// v0.3 backward-compatibility fields:
.url("http://localhost:9999")
.preferredTransport("jsonrpc")
.additionalInterfaces(List.of(
newLegacy_0_3_AgentInterface("jsonrpc", "http://localhost:9999")))
.build();

The two interface lists serve different clients:

  • supportedInterfaces — used by v1.0 clients to discover endpoints (uses AgentInterface with protocolBinding/url/tenant fields)
  • additionalInterfaces — used by v0.3 clients to discover endpoints (uses Legacy_0_3_AgentInterface with v0.3 field names: transport/url)
  • url and preferredTransport — top-level fields that v0.3 clients use to discover the primary endpoint

Push Notification Behavior

Push notification payloads are automatically formatted to match the protocol version used when the push notification configuration was registered. When a v0.3 client registers a push notification configuration (via any transport), the server records the protocol version alongside the configuration. When a notification is later sent to that webhook, the payload is formatted as a v0.3 Task object. Configurations registered by v1.0 clients receive v1.0 StreamResponse payloads as usual. This happens transparently — no additional configuration is needed beyond adding the compat reference module.

A2A Client

The A2A Java SDK provides a Java client implementation of the Agent2Agent (A2A) Protocol, allowing communication with A2A servers. The Java client implementation supports the following transports:

  • JSON-RPC 2.0
  • gRPC
  • HTTP+JSON/REST

To make use of the Java Client:

1. Add the A2A Java SDK Client dependency to your project

Adding a dependency on a2a-java-sdk-client will provide access to a ClientBuilder that you can use to create your A2A Client.

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-client</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases -->
<version>${org.a2aproject.sdk.version}</version>
</dependency>

2. Add one or more dependencies on the A2A Java SDK Client Transport(s) you'd like to use

By default, the sdk-client artifact includes the JSONRPC transport dependency. However, you must still explicitly configure this transport when building the Client as described in the JSON-RPC Transport section.

If you want to use the gRPC transport, you'll need to add a relevant dependency:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-client-transport-grpc</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases -->
<version>${org.a2aproject.sdk.version}</version>
</dependency>

If you want to use the HTTP+JSON/REST transport, you'll need to add a relevant dependency:

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-client-transport-rest</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases -->
<version>${org.a2aproject.sdk.version}</version>
</dependency>

Sample Usage

Create a Client using the ClientBuilder

// First, get the agent card for the A2A server agent you want to connect toAgentCardagentCard = newA2ACardResolver("http://localhost:1234").getAgentCard();
// Specify configuration for the ClientBuilderClientConfigclientConfig = newClientConfig.Builder()
.setAcceptedOutputModes(List.of("text"))
.build();
// Create event consumers to handle responses that will be received from the A2A server// (these consumers will be used for both streaming and non-streaming responses)List<BiConsumer<ClientEvent, AgentCard>> consumers = List.of(
(event, card) -> {
if (eventinstanceofMessageEventmessageEvent) {
// handle the messageEvent.getMessage()
...
} elseif (eventinstanceofTaskEventtaskEvent) {
// handle the taskEvent.getTask()
...
} elseif (eventinstanceofTaskUpdateEventupdateEvent) {
// handle the updateEvent.getTask()
...
}
}
);
// Create a handler that will be used for any errors that occur during streamingConsumer<Throwable> errorHandler = error -> {
// handle the error.getMessage()
...
};
// Create the client using the builderClientclient = Client
.builder(agentCard)
.clientConfig(clientConfig)
.withTransport(JSONRPCTransport.class, newJSONRPCTransportConfig())
.addConsumers(consumers)
.streamingErrorHandler(errorHandler)
.build();

Configuring Transport-Specific Settings

Different transport protocols can be configured with specific settings using specific ClientTransportConfig implementations. The A2A Java SDK provides JSONRPCTransportConfig for the JSON-RPC transport and GrpcTransportConfig for the gRPC transport.

JSON-RPC Transport Configuration

For the JSON-RPC transport, to use the default JdkA2AHttpClient, provide a JSONRPCTransportConfig created with its default constructor.

To use a custom HTTP client implementation, simply create a JSONRPCTransportConfig as follows:

// Create a custom HTTP clientA2AHttpClientcustomHttpClient = ...
// Configure the client settingsClientConfigclientConfig = newClientConfig.Builder()
.setAcceptedOutputModes(List.of("text"))
.build();
Clientclient = Client
.builder(agentCard)
.clientConfig(clientConfig)
.withTransport(JSONRPCTransport.class, newJSONRPCTransportConfig(customHttpClient))
.build();

To customize the default JDK HTTP client without replacing the SDK implementation, provide your own java.net.http.HttpClient to JdkA2AHttpClient:

HttpClientjdkHttpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL)
.version(HttpClient.Version.HTTP_2)
.build();
Clientclient = Client
.builder(agentCard)
.withTransport(JSONRPCTransport.class, newJSONRPCTransportConfig(
newJdkA2AHttpClient(jdkHttpClient)))
.build();
gRPC Transport Configuration

For the gRPC transport, you must configure a channel factory:

// Create a channel factory function that takes the agent URL and returns a ChannelFunction<String, Channel> channelFactory = agentUrl -> {
returnManagedChannelBuilder.forTarget(agentUrl)
...
.build();
};
// Configure the client with transport-specific settingsClientConfigclientConfig = newClientConfig.Builder()
.setAcceptedOutputModes(List.of("text"))
.build();
Clientclient = Client
.builder(agentCard)
.clientConfig(clientConfig)
.withTransport(GrpcTransport.class, newGrpcTransportConfig(channelFactory))
.build();
HTTP+JSON/REST Transport Configuration

For the HTTP+JSON/REST transport, if you'd like to use the default JdkA2AHttpClient, provide a RestTransportConfig created with its default constructor.

To use a custom HTTP client implementation, simply create a RestTransportConfig as follows:

// Create a custom HTTP clientA2AHttpClientcustomHttpClient = ...
// Configure the client settingsClientConfigclientConfig = newClientConfig.Builder()
.setAcceptedOutputModes(List.of("text"))
.build();
Clientclient = Client
.builder(agentCard)
.clientConfig(clientConfig)
.withTransport(RestTransport.class, newRestTransportConfig(customHttpClient))
.build();
Multiple Transport Configurations

You can specify configuration for multiple transports, the appropriate configuration will be used based on the selected transport:

// Configure both JSON-RPC and gRPC transportsClientclient = Client
.builder(agentCard)
.withTransport(GrpcTransport.class, newGrpcTransportConfig(channelFactory))
.withTransport(JSONRPCTransport.class, newJSONRPCTransportConfig())
.withTransport(RestTransport.class, newRestTransportConfig())
.build();

Send a message to the A2A server agent

// Send a text message to the A2A server agentMessagemessage = A2A.toUserMessage("tell me a joke");
// Send the message (uses configured consumers to handle responses)// Streaming will automatically be used if supported by both client and server,// otherwise the non-streaming send message method will be used automaticallyclient.sendMessage(message);
// You can also optionally specify a ClientCallContext with call-specific config to useclient.sendMessage(message, clientCallContext);

Send a message with custom event handling

// Create custom consumers for this specific messageList<BiConsumer<ClientEvent, AgentCard>> customConsumers = List.of(
(event, card) -> {
// handle this specific message's responses
...
}
);
// Create custom error handlerConsumer<Throwable> customErrorHandler = error -> {
// handle the error
...
};
Messagemessage = A2A.toUserMessage("tell me a joke");
client.sendMessage(message, customConsumers, customErrorHandler);

Get the current state of a task

// Retrieve the task with id "task-1234"Tasktask = client.getTask(newTaskQueryParams("task-1234"));
// You can also specify the maximum number of history items for the task// to include in the response Tasktask = client.getTask(newTaskQueryParams("task-1234", 10));
// You can also optionally specify a ClientCallContext with call-specific config to useTasktask = client.getTask(newTaskQueryParams("task-1234"), clientCallContext);

Cancel an ongoing task

// Cancel the task we previously submitted with id "task-1234"TaskcancelledTask = client.cancelTask(newTaskIdParams("task-1234"));
// You can also specify additional properties using a mapMap<String, Object> metadata = Map.of("reason", "user_requested");
TaskcancelledTask = client.cancelTask(newTaskIdParams("task-1234", metadata));
// You can also optionally specify a ClientCallContext with call-specific config to useTaskcancelledTask = client.cancelTask(newTaskIdParams("task-1234"), clientCallContext);

Get a push notification configuration for a task

// Get task push notification configurationTaskPushNotificationConfigconfig = client.getTaskPushNotificationConfiguration(
newGetTaskPushNotificationConfigParams("task-1234"));
// The push notification configuration ID can also be optionally specifiedTaskPushNotificationConfigconfig = client.getTaskPushNotificationConfiguration(
newGetTaskPushNotificationConfigParams("task-1234", "config-4567"));
// Additional properties can be specified using a mapMap<String, Object> metadata = Map.of("source", "client");
TaskPushNotificationConfigconfig = client.getTaskPushNotificationConfiguration(
newGetTaskPushNotificationConfigParams("task-1234", "config-1234", metadata));
// You can also optionally specify a ClientCallContext with call-specific config to useTaskPushNotificationConfigconfig = client.getTaskPushNotificationConfiguration(
newGetTaskPushNotificationConfigParams("task-1234"), clientCallContext);

Set a push notification configuration for a task

// Set task push notification configurationPushNotificationConfigpushNotificationConfig = PushNotificationConfig.builder()
.url("https://example.com/callback")
.authenticationInfo(newAuthenticationInfo(Collections.singletonList("jwt"), null))
.build();
TaskPushNotificationConfigtaskConfig = TaskPushNotificationConfig.builder()
.taskId("task-1234")
.pushNotificationConfig(pushNotificationConfig)
.build();
TaskPushNotificationConfigresult = client.createTaskPushNotificationConfiguration(taskConfig);
// You can also optionally specify a ClientCallContext with call-specific config to useTaskPushNotificationConfigresult = client.createTaskPushNotificationConfiguration(taskConfig, clientCallContext);

List the push notification configurations for a task

List<TaskPushNotificationConfig> configs = client.listTaskPushNotificationConfigurations(
newListTaskPushNotificationConfigParams("task-1234"));
// Additional properties can be specified using a mapMap<String, Object> metadata = Map.of("filter", "active");
List<TaskPushNotificationConfig> configs = client.listTaskPushNotificationConfigurations(
newListTaskPushNotificationConfigParams("task-1234", metadata));
// You can also optionally specify a ClientCallContext with call-specific config to useList<TaskPushNotificationConfig> configs = client.listTaskPushNotificationConfigurations(
newListTaskPushNotificationConfigParams("task-1234"), clientCallContext);

Delete a push notification configuration for a task

client.deleteTaskPushNotificationConfigurations(
newDeleteTaskPushNotificationConfigParams("task-1234", "config-4567"));
// Additional properties can be specified using a mapMap<String, Object> metadata = Map.of("reason", "cleanup");
client.deleteTaskPushNotificationConfigurations(
newDeleteTaskPushNotificationConfigParams("task-1234", "config-4567", metadata));
// You can also optionally specify a ClientCallContext with call-specific config to useclient.deleteTaskPushNotificationConfigurations(
newDeleteTaskPushNotificationConfigParams("task-1234", "config-4567", clientCallContext);

Subscribe to a task

// Subscribe to an ongoing task with id "task-1234" using configured consumersTaskIdParamstaskIdParams = newTaskIdParams("task-1234");
client.subscribeToTask(taskIdParams);
// Or subscribe with custom consumers and error handlerList<BiConsumer<ClientEvent, AgentCard>> customConsumers = List.of(
(event, card) -> System.out.println("Subscribe event: " + event)
);
Consumer<Throwable> customErrorHandler = error -> System.err.println("Subscribe error: " + error.getMessage());
client.subscribeToTask(taskIdParams, customConsumers, customErrorHandler);
// You can also optionally specify a ClientCallContext with call-specific config to useclient.subscribeToTask(taskIdParams, clientCallContext);

Retrieve details about the server agent that this client agent is communicating with

AgentCardserverAgentCard = client.getAgentCard();

Communicating with v0.3 Agents

If you need to communicate with an agent that only supports protocol v0.3, use the compatibility client Client_v0_3.

1. Add the v0.3 client dependency

<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-client</artifactId>
<!-- Use a released version from https://github.com/a2aproject/a2a-java/releases -->
<version>${org.a2aproject.sdk.version}</version>
</dependency>

2. Add a v0.3 client transport dependency

<!-- JSON-RPC transport for v0.3 -->
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-compat-0.3-client-transport-jsonrpc</artifactId>
<version>${org.a2aproject.sdk.version}</version>
</dependency>

gRPC and REST transports are also available:

  • a2a-java-sdk-compat-0.3-client-transport-grpc
  • a2a-java-sdk-compat-0.3-client-transport-rest

3. Create the v0.3 client

AgentCardcard = newA2ACardResolver("http://localhost:1234").getAgentCard();
// Find the v0.3 interface from the agent cardAgentInterfacev03Interface = card.supportedInterfaces().stream()
.filter(iface -> A2AProtocol_v0_3.PROTOCOL_VERSION.equals(iface.protocolVersion()))
.findFirst()
.orElseThrow();
// Create the v0.3 compatibility clientClient_v0_3client = ClientBuilder_v0_3.forUrl(v03Interface.url())
.withTransport(JSONRPCTransport_v0_3.class, newJSONRPCTransportConfigBuilder_v0_3())
.build();

Note:Client_v0_3 exposes only operations available in protocol v0.3. For example, listTasks() is not available (it was added in v1.0). Return types use v0.3 domain objects from the org.a2aproject.sdk.compat03.spec package.

Additional Examples

Hello World Client Example

A complete example of a Java A2A client communicating with a Python A2A server is available in the examples/helloworld/client directory. This example demonstrates:

  • Setting up and using the A2A Java client
  • Sending regular and streaming messages to a Python A2A server
  • Receiving and processing responses from the Python A2A server

The example includes detailed instructions on how to run the Python A2A server and how to run the Java A2A client using JBang.

Check out the example's README for more information.

Hello World Server Example

A complete example of a Python A2A client communicating with a Java A2A server is available in the examples/helloworld/server directory. This example demonstrates:

  • A sample AgentCard producer
  • A sample AgentExecutor producer
  • A Java A2A server receiving regular and streaming messages from a Python A2A client

Check out the example's README for more information.

Community Articles

See COMMUNITY_ARTICLES.md for a list of community articles and videos.

License

This project is licensed under the terms of the Apache 2.0 License.

Contributing

See CONTRIBUTING.md for contribution guidelines.

Server Integrations

The following list contains community contributed integrations with various Java Runtimes.

To contribute an integration, please see CONTRIBUTING_INTEGRATIONS.md.

Extras

See the extras folder for extra functionality not provided by the SDK itself!

About

Java SDK for the Agent2Agent (A2A) Protocol

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages