Skip to content

Commit bb3c49e

Browse files
fjumacescoffier
authored andcommitted
Add a blog post about how to get started with Quarkus and A2A
1 parent cf6b196 commit bb3c49e

File tree

6 files changed

+292
-0
lines changed

6 files changed

+292
-0
lines changed

_data/authors.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,3 +619,10 @@ maeste:
619619
job_title: "Senior Engineering Manager"
620620
twitter: "maeste"
621621
bio: "Senior Engineering Manager at Red Hat / IBM with an old passion for Open Source and more recent one for AI Engineering."
622+
fjuma:
623+
name: "Farah Juma"
624+
625+
emailhash: "e07a676199f6ad0af96a61a691b114b3"
626+
job_title: "Principal Software Engineer"
627+
twitter: "farahjuma"
628+
bio: "Principal Software Engineer at Red Hat / IBM working on AI projects"
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
---
2+
layout: post
3+
title: 'Getting Started with Quarkus and the A2A Java SDK'
4+
date: 2025-07-09
5+
tags: ai a2a
6+
synopsis: 'Today, we released A2A Java SDK 0.2.3.Beta1. This makes it possible to quickly get started with Quarkus and A2A.'
7+
author: fjuma
8+
---
9+
:imagesdir: /assets/images/posts/quarkus-and-a2a-java-sdk
10+
11+
A couple of weeks ago, we https://quarkus.io/blog/a2a-project-launches-java-sdk/[announced] that our https://github.com/a2aproject/a2a-java[A2A Java SDK] has been contributed to the official A2A project! This was a collaboration between our WildFly and Quarkus teams at Red Hat and Google. Today, we have released A2A Java SDK 0.2.3.Beta1, which aligns with the v0.2.3 version of the https://github.com/a2aproject/A2A/tree/v0.2.3[A2A specification]. In this blog post, we'll cover how to easily get started with Quarkus and A2A using the A2A Java SDK.
12+
13+
You can also check out our https://www.youtube.com/watch?v=5CZzW-wvEQs[short video] that gives an introduction to the A2A Java SDK.
14+
15+
video::5CZzW-wvEQs[youtube,width=640, height=360]
16+
17+
== What's A2A?
18+
19+
Before jumping into the details, let's go through what https://a2aproject.github.io/A2A/specification/[A2A] is. The _Agent2Agent_ (A2A) protocol is an open standard developed by Google. It enables AI agents to communicate and collaborate with one another, regardless of each agent's underlying framework, language, or vendor. This is important, as it paves the way for polyglot multi-agent systems.
20+
21+
=== Main Concepts
22+
23+
The A2A protocol involves a few important concepts:
24+
25+
* *User* - This is the end user who has a request that will require the help of one or more agents.
26+
* *A2A Client* - This is the client that will send requests on the user's behalf to an A2A server agent.
27+
* *A2A Server* - This is the server agent that will receive and respond to requests from an A2A client agent. An A2A server agent exposes an HTTP endpoint that implements the A2A protocol.
28+
29+
A2A client and A2A server agents can be implemented using different languages and frameworks. They just need to be able to speak with each other using the A2A protocol. Communication happens using JSON-RPC 2.0 over HTTP(S) as the transport. A2A SDKs written for various programming languages enable this interoperability.
30+
31+
The https://github.com/orgs/a2aproject/repositories[A2A project] aims to provide SDKs for various languages. Using the https://github.com/a2aproject/a2a-python[A2A Python SDK] and our https://github.com/a2aproject/a2a-java[A2A Java SDK], for example, it's possible for an A2A client agent written in Python to communicate with an A2A server agent written in Java and vice versa.
32+
33+
== From a Quarkus LangChain4j AI Service to an A2A Server Agent
34+
35+
Let's say we have a https://docs.quarkiverse.io/quarkus-langchain4j/dev/ai-services.html[Quarkus LangChain4j AI service] that can respond to user queries about the weather by making use of a weather MCP server:
36+
37+
[source,java]
38+
----
39+
@RegisterAiService
40+
@ApplicationScoped
41+
public interface WeatherAgent {
42+
43+
@SystemMessage("""
44+
You are a specialized weather forecast assistant. Your primary function is to
45+
utilize the provided tools to retrieve and relay weather information in response
46+
to user queries. You must rely exclusively on these tools for data and refrain
47+
from inventing information. Ensure that all responses include the detailed output
48+
from the tools used and are formatted in Markdown.
49+
"""
50+
)
51+
@McpToolBox("weather") // <-- The weather MCP server that will be used
52+
String chat(@UserMessage String question);
53+
}
54+
----
55+
56+
To turn this weather agent into an A2A server agent, there are a few simple steps we need to follow:
57+
58+
=== Add an A2A Java SDK Server Dependency
59+
60+
[NOTE]
61+
====
62+
The `io.github.a2asdk` `groupId` is temporary and will likely change for future releases. Keep an eye on the `a2a-java` https://github.com/a2aproject/a2a-java/blob/main/README.md[README] for up-to-date documentation.
63+
====
64+
65+
[source,xml]
66+
----
67+
<dependency>
68+
<groupId>io.github.a2asdk</groupId>
69+
<artifactId>a2a-java-sdk-server-quarkus</artifactId> <1>
70+
</dependency>
71+
----
72+
<1> `a2a-java-sdk-server-quarkus` provides access to the core classes that make up the A2A specification and provides the HTTP endpoint that implements the A2A protocol. This dependency makes use of Quarkus Reactive Routes. If not using Quarkus, the `a2a-java-sdk-server-jakarta` dependency can be used to expose an A2A server agent in a Jakarta server supporting CDI and Jakarta RESTful Web Services.
73+
74+
=== Add a Class that Creates an A2A `AgentCard`
75+
76+
The `AgentCard` is a class that describes the capabilities of an A2A server agent. Other agents or clients will use this to understand what our weather agent can do. The A2A Java SDK will automatically expose this agent card at the server agent's `.well-known/agent.json` URI. For example, if our A2A server agent is running on http://localhost:10001, the agent card will be available at http://localhost:10001/.well-known/agent.json.
77+
78+
[source,java]
79+
----
80+
import io.a2a.server.PublicAgentCard;
81+
import io.a2a.spec.AgentCapabilities;
82+
import io.a2a.spec.AgentCard;
83+
import io.a2a.spec.AgentSkill;
84+
...
85+
86+
@ApplicationScoped
87+
public class WeatherAgentCardProducer {
88+
89+
@Produces
90+
@PublicAgentCard
91+
public AgentCard agentCard() {
92+
return new AgentCard.Builder()
93+
.name("Weather Agent")
94+
.description("Helps with weather")
95+
.url("http://localhost:10001") <1>
96+
.version("1.0.0")
97+
.capabilities(new AgentCapabilities.Builder() <2>
98+
.streaming(true)
99+
.pushNotifications(false)
100+
.stateTransitionHistory(false)
101+
.build())
102+
.defaultInputModes(Collections.singletonList("text"))
103+
.defaultOutputModes(Collections.singletonList("text"))
104+
.skills(Collections.singletonList(new AgentSkill.Builder()
105+
.id("weather_search")
106+
.name("Search weather")
107+
.description("Helps with weather in city, or states") <3>
108+
.tags(Collections.singletonList("weather"))
109+
.examples(List.of("weather in LA, CA")) <4>
110+
.build()))
111+
.build();
112+
}
113+
}
114+
----
115+
<1> The URL of our A2A server agent. We set `quarkus.http.port` to `10001` in our `application.properties` file so our A2A server agent will be available at http://localhost:10001.
116+
<2> Indicates the capabilities of our A2A server agent like whether it supports streaming, push notifications, and state transition history.
117+
<3> Describes what our agent can do.
118+
<4> An example query that our agent can handle.
119+
120+
=== Add a class that creates an A2A `AgentExecutor`
121+
122+
The `AgentExecutor` is a class that will be used to process requests sent to our A2A server agent. It will pass the requests received from the A2A client to our Quarkus LangChain4j AI service and is responsible for returning the responses back to the A2A client. The A2A Java SDK will call this executor when a request is sent to our A2A server agent.
123+
124+
Notice that the `AgentExecutor` interface specifies two methods, `execute` and `cancel`, that we need to implement.
125+
126+
[source,java]
127+
----
128+
import io.a2a.server.agentexecution.AgentExecutor;
129+
import io.a2a.server.agentexecution.RequestContext;
130+
import io.a2a.server.events.EventQueue;
131+
import io.a2a.server.tasks.TaskUpdater;
132+
import io.a2a.spec.JSONRPCError;
133+
import io.a2a.spec.Message;
134+
import io.a2a.spec.Part;
135+
import io.a2a.spec.Task;
136+
import io.a2a.spec.TaskNotCancelableError;
137+
import io.a2a.spec.TaskState;
138+
import io.a2a.spec.TextPart;
139+
...
140+
141+
@ApplicationScoped
142+
public class WeatherAgentExecutorProducer {
143+
144+
@Inject
145+
WeatherAgent weatherAgent; <1>
146+
147+
@Produces
148+
public AgentExecutor agentExecutor() {
149+
return new WeatherAgentExecutor(weatherAgent);
150+
}
151+
152+
private static class WeatherAgentExecutor implements AgentExecutor {
153+
154+
private final WeatherAgent weatherAgent;
155+
156+
public WeatherAgentExecutor(WeatherAgent weatherAgent) {
157+
this.weatherAgent = weatherAgent;
158+
}
159+
160+
@Override
161+
public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { <2>
162+
TaskUpdater updater = new TaskUpdater(context, eventQueue);
163+
164+
// mark the task as submitted and start working on it
165+
if (context.getTask() == null) {
166+
updater.submit();
167+
}
168+
updater.startWork();
169+
170+
// extract the text from the message
171+
String userMessage = extractTextFromMessage(context.getMessage());
172+
173+
// call the weather agent with the user's message
174+
String response = weatherAgent.chat(userMessage); <3>
175+
176+
// create the response part
177+
TextPart responsePart = new TextPart(response, null);
178+
List<Part<?>> parts = List.of(responsePart);
179+
180+
// add the response as an artifact and complete the task
181+
updater.addArtifact(parts, null, null, null);
182+
updater.complete();
183+
}
184+
185+
@Override
186+
public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { <4>
187+
Task task = context.getTask();
188+
189+
if (task.getStatus().state() == TaskState.CANCELED) {
190+
// task already cancelled
191+
throw new TaskNotCancelableError();
192+
}
193+
194+
if (task.getStatus().state() == TaskState.COMPLETED) {
195+
// task already completed
196+
throw new TaskNotCancelableError();
197+
}
198+
199+
// cancel the task
200+
TaskUpdater updater = new TaskUpdater(context, eventQueue);
201+
updater.cancel();
202+
}
203+
204+
private String extractTextFromMessage(Message message) {
205+
StringBuilder textBuilder = new StringBuilder();
206+
if (message.getParts() != null) {
207+
for (Part part : message.getParts()) {
208+
if (part instanceof TextPart textPart) {
209+
textBuilder.append(textPart.getText());
210+
}
211+
}
212+
}
213+
return textBuilder.toString();
214+
}
215+
}
216+
}
217+
----
218+
<1> This is our Quarkus LangChain4j AI service.
219+
<2> The `execute` method will be used to process requests from an A2A client.
220+
<3> Here we are invoking our Quarkus LangChain4j AI service.
221+
<4> The `cancel` method be used to cancel an ongoing request.
222+
223+
224+
That's it, we can now start our Quarkus application as shown below and our A2A server agent will be available at http://localhost:10001. A2A client agents can now send weather-related queries to our A2A server agent and our agent will respond with the weather information.
225+
226+
[source,bash]
227+
----
228+
$ mvn quarkus:dev
229+
----
230+
231+
We've gone from a Quarkus LangChain4j AI service to an A2A server agent in just a few steps!
232+
233+
The source code for this example is available https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/weather_agent[here].
234+
235+
== Validating our A2A Server Agent Using the A2A Inspector
236+
237+
The https://github.com/a2aproject/a2a-inspector[A2A Inspector] is a web application that's very easy to run and can be used to inspect any A2A server agent.
238+
239+
We can use the A2A Inspector to validate our A2A server agent by specifying our server agent's URL in the `Connect` text box.
240+
241+
The A2A Inspector will obtain and show our server agent's agent card:
242+
243+
image::a2a-inspector-agent-card.png[scaledwidth=100%]
244+
245+
Notice that this matches the information we provided in our `WeatherAgentCardProducer` class.
246+
247+
You can also use the inspector to send requests to the A2A server agent and to view the raw HTTP requests and responses.
248+
249+
== Multi-Agent Orchestration with Python and Java Server Agents
250+
251+
Let's take a look at a more complex example that makes use of our weather A2A server agent.
252+
253+
image::multiagent-java-python.png[scaledwidth=100%]
254+
255+
This is a multi-agent example where a host agent delegates requests to two different A2A server agents, an Airbnb agent and our Weather agent, based on the user's question. Under the hood, the host agent makes use of each agent's agent card to determine the capabilities of each agent and uses an LLM to determine which agent to delegate the request to based on their capabilities.
256+
257+
The https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/airbnb_agent[Airbnb agent] is a Python agent that's implemented using LangGraph and makes use of the A2A Python SDK.
258+
259+
The https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/weather_agent[Weather agent] is our Java agent that's implemented using Quarkus LangChain4j and makes use of the A2A Java SDK.
260+
261+
Notice that the host agent uses A2A clients written in Python to communicate with the server agents. It's also possible to use an https://github.com/a2aproject/a2a-java?tab=readme-ov-file#a2a-client[A2A client] written in Java using our A2A Java SDK.
262+
263+
The complete source code for this example is available https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent[here]. To experiment with this multi-agent example, try sending different types of questions to the host agent, for example:
264+
265+
* What's the weather in New York, NY?
266+
* Find me a room in LA, CA, July 7-9, 2 adults
267+
268+
Notice that the host agent will delegate the first question to the Weather agent and the second question to the Airbnb agent.
269+
270+
image::new_york_weather.png[scaledwidth=100%]
271+
272+
And the second question will be delegated to the Airbnb agent:
273+
274+
image::la_airbnb.png[scaledwidth=100%]
275+
276+
== Conclusion
277+
278+
We've seen how easy it is to get started with Quarkus and A2A using the A2A Java SDK. With just a few steps, we can turn a Quarkus LangChain4j AI service into an A2A server agent that can communicate with other A2A agents, regardless of the language or framework they are implemented in. The LangChain4j and Quarkus teams are also working on removing most of the boilerplate code to expose an A2A server and interact with A2A clients. So, stay tuned!
279+
280+
=== Further Reading
281+
282+
* https://a2aproject.github.io/A2A/specification/[A2A Specification]
283+
* https://github.com/a2aproject/a2a-java/blob/main/README.md[A2A Java SDK Documentation]
284+
285+
64.6 KB
Loading
220 KB
Loading
105 KB
Loading
70 KB
Loading

0 commit comments

Comments
 (0)