A Java library that helps run agentic applications as A2AServers following Google's Agent2Agent (A2A) Protocol.
You can build the A2A Java SDK using mvn:
mvn clean installYou can find an example of how to use the A2A Java SDK here.
More examples will be added soon.
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.
- Add the A2A Java SDK Core Maven dependency to your project
- Add a class that creates an A2A Agent Card
- Add a class that creates an A2A Agent Executor
- Add an A2A Java SDK Server Maven dependency to your project
Note: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be sure to check out the latest tag (you can see the tags here), build from the tag, and reference that version below. For example, if the latest tag is
0.2.3, you can use the following dependency.
<dependency>
<groupId>io.a2a.sdk</groupId>
<artifactId>a2a-java-sdk-core</artifactId>
<version>0.2.3</version>
</dependency>importio.a2a.spec.AgentCapabilities;
importio.a2a.spec.AgentCard;
importio.a2a.spec.AgentSkill;
importio.a2a.spec.PublicAgentCard;
...
@ApplicationScopedpublicclassWeatherAgentCardProducer {
@Produces@PublicAgentCardpublicAgentCardagentCard() {
returnnewAgentCard.Builder()
.name("Weather Agent")
.description("Helps with weather")
.url("http://localhost:10001")
.version("1.0.0")
.capabilities(newAgentCapabilities.Builder()
.streaming(true)
.pushNotifications(false)
.stateTransitionHistory(false)
.build())
.defaultInputModes(Collections.singletonList("text"))
.defaultOutputModes(Collections.singletonList("text"))
.skills(Collections.singletonList(newAgentSkill.Builder()
.id("weather_search")
.name("Search weather")
.description("Helps with weather in city, or states")
.tags(Collections.singletonList("weather"))
.examples(List.of("weather in LA, CA"))
.build()))
.build();
}
}importio.a2a.server.agentexecution.AgentExecutor;
importio.a2a.server.agentexecution.RequestContext;
importio.a2a.server.events.EventQueue;
importio.a2a.server.tasks.TaskUpdater;
importio.a2a.spec.JSONRPCError;
importio.a2a.spec.Message;
importio.a2a.spec.Part;
importio.a2a.spec.Task;
importio.a2a.spec.TaskNotCancelableError;
importio.a2a.spec.TaskState;
importio.a2a.spec.TextPart;
...
@ApplicationScopedpublicclassWeatherAgentExecutorProducer {
@InjectWeatherAgentweatherAgent;
@ProducespublicAgentExecutoragentExecutor() {
returnnewWeatherAgentExecutor(weatherAgent);
}
privatestaticclassWeatherAgentExecutorimplementsAgentExecutor {
privatefinalWeatherAgentweatherAgent;
publicWeatherAgentExecutor(WeatherAgentweatherAgent) {
this.weatherAgent = weatherAgent;
}
@Overridepublicvoidexecute(RequestContextcontext, EventQueueeventQueue) throwsJSONRPCError {
TaskUpdaterupdater = newTaskUpdater(context, eventQueue);
// mark the task as submitted and start working on itif (context.getTask() == null) {
updater.submit();
}
updater.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, null);
List<Part<?>> parts = List.of(responsePart);
// add the response as an artifact and complete the taskupdater.addArtifact(parts, null, null, null);
updater.complete();
}
@Overridepublicvoidcancel(RequestContextcontext, EventQueueeventQueue) throwsJSONRPCError {
Tasktask = context.getTask();
if (task.getStatus().state() == TaskState.CANCELED) {
// task already cancelledthrownewTaskNotCancelableError();
}
if (task.getStatus().state() == TaskState.COMPLETED) {
// task already completedthrownewTaskNotCancelableError();
}
// cancel the taskTaskUpdaterupdater = newTaskUpdater(context, eventQueue);
updater.cancel();
}
privateStringextractTextFromMessage(Messagemessage) {
StringBuildertextBuilder = newStringBuilder();
if (message.getParts() != null) {
for (Partpart : message.getParts()) {
if (partinstanceofTextParttextPart) {
textBuilder.append(textPart.getText());
}
}
}
returntextBuilder.toString();
}
}
}Note: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be sure to check out the latest tag (you can see the tags here), build from the tag, and reference that version below. For example, if the latest tag is
0.2.3, you can use the following dependency.
Adding a dependency on an A2A Java SDK Server will allow you to run your agentic Java application as an A2A server.
The A2A Java SDK provides two A2A server endpoint implementations, one based on Jakarta REST (a2a-java-sdk-server-jakarta) and one based on Quarkus Reactive Routes (a2a-java-sdk-server-quarkus). You can choose the one that best fits your application.
Add one of the following dependencies to your project:
<dependency>
<groupId>io.a2a.sdk</groupId>
<artifactId>a2a-java-sdk-server-jakarta</artifactId>
<version>${io.a2a.sdk.version}</version>
</dependency>OR
<dependency>
<groupId>io.a2a.sdk</groupId>
<artifactId>a2a-java-sdk-server-quarkus</artifactId>
<version>${io.a2a.sdk.version}</version>
</dependency>The A2A Java SDK provides a Java client implementation of the Agent2Agent (A2A) Protocol, allowing communication with A2A servers.
// Create an A2AClient (the URL specified is the server agent's URL, be sure to replace it with the actual URL of the A2A server you want to connect to)A2AClientclient = newA2AClient("http://localhost:1234");// Send a text message to the A2A server agentMessagemessage = A2A.toUserMessage("tell me a joke"); // the message ID will be automatically generated for youMessageSendParamsparams = newMessageSendParams.Builder()
.message(message)
.build();
SendMessageResponseresponse = client.sendMessage(params); Note that A2A#toUserMessage will automatically generate a message ID for you when creating the Message
if you don't specify it. You can also explicitly specify a message ID like this:
Messagemessage = A2A.toUserMessage("tell me a joke", "message-1234"); // messageId is message-1234// Retrieve the task with id "task-1234"GetTaskResponseresponse = client.getTask("task-1234");
// You can also specify the maximum number of items of history for the task// to include in the responseGetTaskResponseresponse = client.getTask(newTaskQueryParams("task-1234", 10));// Cancel the task we previously submitted with id "task-1234"CancelTaskResponseresponse = client.cancelTask("task-1234");
// You can also specify additional properties using a mapMap<String, Object> metadata = ... CancelTaskResponseresponse = client.cancelTask(newTaskIdParams("task-1234", metadata));// Get task push notification configurationGetTaskPushNotificationConfigResponseresponse = client.getTaskPushNotificationConfig("task-1234");
// You can also specify additional properties using a mapMap<String, Object> metadata = ...
GetTaskPushNotificationConfigResponseresponse = client.getTaskPushNotificationConfig(newTaskIdParams("task-1234", metadata));// Set task push notification configurationPushNotificationConfigpushNotificationConfig = newPushNotificationConfig.Builder()
.url("https://example.com/callback")
.authenticationInfo(newAuthenticationInfo(Collections.singletonList("jwt"), null))
.build();
SetTaskPushNotificationResponseresponse = client.setTaskPushNotificationConfig("task-1234", pushNotificationConfig);// Send a text message to the remote agentMessagemessage = A2A.toUserMessage("tell me some jokes"); // the message ID will be automatically generated for youMessageSendParamsparams = newMessageSendParams.Builder()
.message(message)
.build();
// Create a handler that will be invoked for Task, Message, TaskStatusUpdateEvent, and TaskArtifactUpdateEventConsumer<StreamingEventKind> eventHandler = event -> {...};
// Create a handler that will be invoked if an error is receivedConsumer<JSONRPCError> errorHandler = error -> {...};
// Create a handler that will be invoked in the event of a failureRunnablefailureHandler = () -> {...};
// Send the streaming message to the remote agentclient.sendStreamingMessage(params, eventHandler, errorHandler, failureHandler);// Create a handler that will be invoked for Task, Message, TaskStatusUpdateEvent, and TaskArtifactUpdateEventConsumer<StreamingEventKind> eventHandler = event -> {...};
// Create a handler that will be invoked if an error is receivedConsumer<JSONRPCError> errorHandler = error -> {...};
// Create a handler that will be invoked in the event of a failureRunnablefailureHandler = () -> {...};
// Resubscribe to an ongoing task with id "task-1234"TaskIdParamstaskIdParams = newTaskIdParams("task-1234");
client.resubscribeToTask("request-1234", taskIdParams, eventHandler, errorHandler, failureHandler);AgentCardserverAgentCard = client.getAgentCard();An agent card can also be retrieved using the A2A#getAgentCard method:
// http://localhost:1234 is the base URL for the agent whose card we want to retrieveAgentCardagentCard = A2A.getAgentCard("http://localhost:1234");A complete example of an A2A client communicating with a Python A2A server is available in the examples/helloworld directory. This example demonstrates:
- Setting up and using the A2A Java client
- Sending regular and streaming messages
- Receiving and processing responses
The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the example's README for more information.
This project is licensed under the terms of the Apache 2.0 License.
See CONTRIBUTING.md for contribution guidelines.