Skip to content

Commit e3ea378

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: add example on how to expose agent via A2A protocol
PiperOrigin-RevId: 876276519
1 parent 4a75571 commit e3ea378

10 files changed

Lines changed: 431 additions & 0 deletions

File tree

a2a/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@
104104
<version>${truth.version}</version>
105105
<scope>test</scope>
106106
</dependency>
107+
<dependency>
108+
<groupId>org.mockito</groupId>
109+
<artifactId>mockito-core</artifactId>
110+
<scope>test</scope>
111+
</dependency>
107112
</dependencies>
108113
<build>
109114
<plugins>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Google ADK A2A Agent Server Sample
2+
3+
This sample demonstrates how to expose a Google ADK (Agent Development Kit)
4+
agent via the A2A (Agent-to-Agent) protocol using A2A SDK and Quarkus service.
5+
6+
## Overview
7+
8+
The application implements a simple conversational agent that checks whether
9+
given numbers are prime numbers. It uses the `LlmAgent` from the Google ADK and
10+
exposes it via an A2A server.
11+
12+
### Key Components
13+
14+
* **`Agent.java`**: Defines the `LlmAgent` instance (`check_prime_agent`) and
15+
the `checkPrime` tool function it uses to verify numbers.
16+
* **`AgentCardProducer.java`**: Loads and provides the `AgentCard` metadata
17+
(from `agent.json`) which defines the agent's identity and capabilities in
18+
the A2A network.
19+
* **`AgentExecutorProducer.java`**: Configures and provides the A2A
20+
`AgentExecutor`, implemented by the ADK library to wire ADK-owned agents
21+
automatically.
22+
* **`StartupConfig.java`**: Contains initialization logic, such as registering
23+
JSON modules for the Vert.x/Quarkus runtime.
24+
* **`application.properties`**: Contains a configuration for the Quarkus
25+
service and A2A, such as port where application will be exposed, application
26+
name and event processing timeouts.
27+
28+
## Building the Project
29+
30+
You can build the project using Maven:
31+
32+
```shell
33+
mvn clean install
34+
```
35+
36+
The Java server can be started using `mvn` as follow (don't forget to set your
37+
GOOGLE_API_KEY before running the service):
38+
39+
```bash
40+
export GOOGLE_API_KEY=<YOUR_API_KEY>
41+
42+
cd contrib/samples/a2a_server
43+
mvn quarkus:dev
44+
```
45+
46+
## Sample request
47+
48+
```bash
49+
curl -X POST http://localhost:9090 \
50+
-H 'Content-Type: application/json' \
51+
-d '{
52+
"jsonrpc": "2.0",
53+
"id": "cli-check-2",
54+
"method": "message/stream",
55+
"params": {
56+
"message": {
57+
"kind": "message",
58+
"contextId": "cli-demo-context",
59+
"messageId": "cli-check-2",
60+
"role": "user",
61+
"parts": [
62+
{
63+
"kind": "text",
64+
"text": "Is 2 prime?"
65+
}
66+
]
67+
}
68+
}
69+
}'
70+
```

contrib/samples/a2a_server/pom.xml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>com.google.adk</groupId>
9+
<artifactId>google-adk-samples</artifactId>
10+
<version>0.7.1-SNAPSHOT</version><!-- {x-version-update:google-adk:current} -->
11+
<relativePath>..</relativePath>
12+
</parent>
13+
14+
<artifactId>google-adk-sample-a2a-agent</artifactId>
15+
<packaging>jar</packaging>
16+
17+
<name>Google ADK - Sample - A2A Agent Server</name>
18+
<description>Demonstrates exposing ADK agent via A2A.</description>
19+
20+
<properties>
21+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
22+
<java.version>17</java.version>
23+
<google-adk.version>${project.version}</google-adk.version>
24+
<google-adk-a2a.version>${project.version}</google-adk-a2a.version>
25+
<a2a.sdk.version>0.3.0.Beta1</a2a.sdk.version>
26+
<quarkus.platform.version>3.30.6</quarkus.platform.version>
27+
<flogger.version>0.8</flogger.version>
28+
</properties>
29+
30+
<dependencyManagement>
31+
<dependencies>
32+
<dependency>
33+
<groupId>io.quarkus</groupId>
34+
<artifactId>quarkus-bom</artifactId>
35+
<version>${quarkus.platform.version}</version>
36+
<type>pom</type>
37+
<scope>import</scope>
38+
</dependency>
39+
</dependencies>
40+
</dependencyManagement>
41+
42+
<dependencies>
43+
<dependency>
44+
<groupId>io.github.a2asdk</groupId>
45+
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
46+
<version>${a2a.sdk.version}</version>
47+
</dependency>
48+
<dependency>
49+
<groupId>io.quarkus</groupId>
50+
<artifactId>quarkus-resteasy-jackson</artifactId>
51+
</dependency>
52+
<dependency>
53+
<groupId>com.fasterxml.jackson.datatype</groupId>
54+
<artifactId>jackson-datatype-jsr310</artifactId>
55+
</dependency>
56+
<dependency>
57+
<groupId>com.google.adk</groupId>
58+
<artifactId>google-adk</artifactId>
59+
<version>${google-adk.version}</version>
60+
</dependency>
61+
<dependency>
62+
<groupId>com.google.adk</groupId>
63+
<artifactId>google-adk-a2a</artifactId>
64+
<version>${google-adk-a2a.version}</version>
65+
</dependency>
66+
67+
<dependency>
68+
<groupId>io.github.a2asdk</groupId>
69+
<artifactId>a2a-java-sdk-spec</artifactId>
70+
<version>${a2a.sdk.version}</version>
71+
</dependency>
72+
73+
<dependency>
74+
<groupId>io.quarkus</groupId>
75+
<artifactId>quarkus-arc</artifactId>
76+
</dependency>
77+
<dependency>
78+
<groupId>io.quarkus</groupId>
79+
<artifactId>quarkus-reactive-routes</artifactId>
80+
</dependency>
81+
<dependency>
82+
<groupId>io.quarkus</groupId>
83+
<artifactId>quarkus-jackson</artifactId>
84+
</dependency>
85+
86+
<dependency>
87+
<groupId>io.github.a2asdk</groupId>
88+
<artifactId>a2a-java-sdk-client</artifactId>
89+
<version>${a2a.sdk.version}</version>
90+
</dependency>
91+
92+
<dependency>
93+
<groupId>com.google.flogger</groupId>
94+
<artifactId>flogger</artifactId>
95+
<version>${flogger.version}</version>
96+
</dependency>
97+
98+
<dependency>
99+
<groupId>com.google.flogger</groupId>
100+
<artifactId>google-extensions</artifactId>
101+
<version>${flogger.version}</version>
102+
</dependency>
103+
104+
<dependency>
105+
<groupId>com.google.flogger</groupId>
106+
<artifactId>flogger-system-backend</artifactId>
107+
<version>${flogger.version}</version>
108+
</dependency>
109+
110+
</dependencies>
111+
112+
<build>
113+
<resources>
114+
<resource>
115+
<directory>src/main/java</directory>
116+
<includes>
117+
<include>**/*.json</include>
118+
</includes>
119+
</resource>
120+
<resource>
121+
<directory>src/main/resources</directory>
122+
</resource>
123+
</resources>
124+
<plugins>
125+
<plugin>
126+
<groupId>io.quarkus</groupId>
127+
<artifactId>quarkus-maven-plugin</artifactId>
128+
<version>${quarkus.platform.version}</version>
129+
<extensions>true</extensions>
130+
<executions>
131+
<execution>
132+
<goals>
133+
<goal>build</goal>
134+
<goal>generate-code</goal>
135+
<goal>generate-code-tests</goal>
136+
</goals>
137+
</execution>
138+
</executions>
139+
</plugin>
140+
</plugins>
141+
</build>
142+
</project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.google.adk.samples.a2aagent;
2+
3+
import io.a2a.server.PublicAgentCard;
4+
import io.a2a.spec.AgentCard;
5+
import io.a2a.util.Utils;
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import jakarta.enterprise.inject.Produces;
8+
import java.io.InputStream;
9+
import java.nio.charset.StandardCharsets;
10+
11+
/** Produces the {@link AgentCard} from the bundled JSON resources. */
12+
@ApplicationScoped
13+
public class AgentCardProducer {
14+
15+
@Produces
16+
@PublicAgentCard
17+
public AgentCard agentCard() {
18+
try (InputStream is = getClass().getResourceAsStream("/agent/agent.json")) {
19+
if (is == null) {
20+
throw new RuntimeException("agent.json not found in resources");
21+
}
22+
23+
// Read the JSON file content
24+
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
25+
26+
// Use the SDK's built-in mapper to convert JSON string to AgentCard record
27+
return Utils.OBJECT_MAPPER.readValue(json, AgentCard.class);
28+
29+
} catch (Exception e) {
30+
throw new RuntimeException("Failed to load AgentCard from JSON", e);
31+
}
32+
}
33+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.google.adk.samples.a2aagent;
2+
3+
import com.google.adk.a2a.executor.AgentExecutorConfig;
4+
import com.google.adk.samples.a2aagent.agent.Agent;
5+
import com.google.adk.sessions.InMemorySessionService;
6+
import io.a2a.server.agentexecution.AgentExecutor;
7+
import jakarta.enterprise.context.ApplicationScoped;
8+
import jakarta.enterprise.inject.Produces;
9+
import org.eclipse.microprofile.config.inject.ConfigProperty;
10+
11+
/** Produces the {@link AgentExecutor} instance that handles agent interactions. */
12+
@ApplicationScoped
13+
public class AgentExecutorProducer {
14+
15+
@ConfigProperty(name = "my.adk.app.name", defaultValue = "default-app")
16+
String appName;
17+
18+
@Produces
19+
public AgentExecutor agentExecutor() {
20+
InMemorySessionService sessionService = new InMemorySessionService();
21+
return new com.google.adk.a2a.executor.AgentExecutor.Builder()
22+
.agent(Agent.ROOT_AGENT)
23+
.appName(appName)
24+
.sessionService(sessionService)
25+
.agentExecutorConfig(AgentExecutorConfig.builder().build())
26+
.build();
27+
}
28+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.google.adk.samples.a2aagent;
2+
3+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
4+
import io.quarkus.runtime.StartupEvent;
5+
import io.vertx.core.json.jackson.DatabindCodec;
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import jakarta.enterprise.event.Observes;
8+
9+
/** Configuration applied on startup, such as Jackson module registrations. */
10+
@ApplicationScoped
11+
public class StartupConfig {
12+
13+
void onStart(@Observes StartupEvent ev) {
14+
// Register globally for Vert.x's internal JSON handling
15+
DatabindCodec.mapper().registerModule(new JavaTimeModule());
16+
}
17+
}

0 commit comments

Comments
 (0)