Skip to content

Commit eae0a18

Browse files
committed
feat: add unified client with A2A 0.3 compatibility
1 parent 2ee4196 commit eae0a18

115 files changed

Lines changed: 5966 additions & 83 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/architecture/compatibility_0.3.md‎

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ The A2A protocol evolved from v0.3 to v1.0 with significant breaking changes. Ex
1616

1717
- Dedicated `compat-0.3` Maven module structure containing **only** 0.3-specific code
1818
- gRPC code generation from the v0.3 `a2a.proto`
19-
- Dedicated v0.3 client (`Client_v0_3`) exposing only features available in v0.3
19+
- Unified v1.0 client compatibility adapters that allow the normal `Client` API to communicate with v0.3 agents
20+
- Legacy v0.3 client (`Client_v0_3`) exposing only features available in v0.3
2021
- Server-side conversion layer (`Convert_v0_3_To10RequestHandler`) that accepts v0.3 requests and delegates to v1.0 server-common
2122
- Server-side transport handlers for v0.3 (JSON-RPC, gRPC, REST)
2223
- Bidirectional mapping layer between v0.3 and v1.0 domain objects
@@ -30,7 +31,7 @@ The A2A protocol evolved from v0.3 to v1.0 with significant breaking changes. Ex
3031
### Out of Scope
3132

3233
- Changes to existing v1.0 modules (no regressions, no API changes)
33-
- Automatic protocol version detection (client must explicitly choose API version)
34+
- Implicit protocol downgrade or automatic version selection (the unified client requires the caller to explicitly request supported protocol versions during agent-card discovery)
3435
- Extras modules (OpenTelemetry, JPA stores, etc.) for v0.3
3536
- Serving a separate v0.3-format agent card (the v1.0 card is served, with optional v0.3-compatible fields added by the user)
3637

@@ -107,13 +108,21 @@ All compat-0.3 classes use a `_v0_3` suffix to avoid naming conflicts with v1.0
107108

108109
### Dedicated v0.3 Client
109110

110-
The compat layer exposes a **dedicated `Client_v0_3`** that only provides features available in v0.3:
111+
The compat layer retains a **dedicated `Client_v0_3`** for applications that already use the v0.3 API directly. It only provides features available in v0.3:
111112

112113
- No `listTasks()` method (absent in v0.3)
113114
- Method names reflect v0.3 semantics where they differ
114115
- The client is a standalone API, not a wrapper around the v1.0 `Client`
115116

116-
Users must explicitly check the `protocolVersion` field from the agent card and instantiate the correct client accordingly. No automatic version detection.
117+
This is the legacy path. New applications should use the unified v1.0 `Client` path below instead.
118+
119+
### Unified v1.0 Client with v0.3 Compatibility
120+
121+
The recommended path for new applications is the normal v1.0 `Client` API with optional v0.3 compatibility artifacts. Applications continue to use v1.0 `AgentCard`, request, response, event, configuration, context, and interceptor types; they do not need to import `Client_v0_3` or v0.3 domain types.
122+
123+
The caller explicitly requests the protocol versions it is willing to use during agent-card discovery. If a v0.3 interface is selected, the compatibility parser projects its card into a v1.0 `AgentCard`, and a version-aware transport adapter converts calls between v1.0 and v0.3. The unified client does not silently downgrade when v0.3 was not requested.
124+
125+
The compatibility parser and the adapter for the selected binding are optional dependencies. The adapter rejects v1.0 operations that have no v0.3 equivalent, such as `listTasks`, before sending a request. If the requested compatibility parser or binding adapter is absent, discovery or client construction reports the missing artifact.
117126

118127
### Server-Side Conversion Layer
119128

@@ -231,9 +240,12 @@ compat-0.3/
231240
│ └── ListTaskPushNotificationConfigsResultMapper_v0_3.java
232241
├── tests/ # Test infrastructure
233242
│ └── server-common/ # Shared test base classes (AgentExecutorProducer_v0_3)
234-
├── client/ # v0.3-compatible client
235-
│ ├── base/ # Client_v0_3 — dedicated 0.3 API
236-
│ │ └── pom.xml
243+
├── client/ # Client compatibility support
244+
│ ├── base/ # Client_v0_3 — legacy dedicated 0.3 API
245+
│ ├── adapter/ # Unified v1.0 Client compatibility adapter
246+
│ ├── adapter-jsonrpc/ # Unified JSON-RPC client adapter
247+
│ ├── adapter-rest/ # Unified REST client adapter
248+
│ ├── adapter-grpc/ # Unified gRPC client adapter
237249
│ └── transport/
238250
│ ├── spi/ # Transport SPI
239251
│ │ └── pom.xml
@@ -427,7 +439,38 @@ The `server-conversion` module produces a test-jar containing shared test infras
427439

428440
### Client: Talking to a v0.3 Agent
429441

430-
**1. Add the compat client dependency:**
442+
New applications should use the unified v1.0 `Client` API. Add the compatibility parser and the adapter for the desired binding:
443+
444+
```xml
445+
<dependency>
446+
<groupId>org.a2aproject.sdk</groupId>
447+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter</artifactId>
448+
</dependency>
449+
<!-- Use the matching -jsonrpc, -rest, or -grpc adapter. -->
450+
<dependency>
451+
<groupId>org.a2aproject.sdk</groupId>
452+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-jsonrpc</artifactId>
453+
</dependency>
454+
```
455+
456+
Request the versions explicitly and build the ordinary client:
457+
458+
```java
459+
AgentCard agentCard = A2A.getAgentCard(
460+
"http://localhost:1234", Set.of("1.0", "0.3"));
461+
462+
Client client = Client.builder(agentCard)
463+
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()
464+
.httpClient(A2AHttpClientFactory.create())
465+
.build())
466+
.build();
467+
```
468+
469+
The returned card contains a v1.0 `AgentInterface` marked with protocol version `"0.3"`. The ordinary builder selects the matching compatibility adapter, while a native v1.0 interface continues to use the normal transport.
470+
471+
Existing applications that use v0.3 domain types directly can continue using the legacy API:
472+
473+
**1. Add the legacy compat client dependency:**
431474

432475
```xml
433476
<dependency>
@@ -441,7 +484,7 @@ The `server-conversion` module produces a test-jar containing shared test infras
441484
</dependency>
442485
```
443486

444-
**2. Find the v0.3 interface and create the client:**
487+
**2. Find the v0.3 interface and create the legacy client:**
445488

446489
```java
447490
AgentCard card = // ... fetch agent card from /.well-known/agent-card.json
@@ -452,13 +495,13 @@ AgentInterface v03Interface = card.supportedInterfaces().stream()
452495
.findFirst()
453496
.orElseThrow();
454497

455-
// Create the v0.3 compatibility client
498+
// Create the legacy v0.3 client
456499
Client_v0_3 client = ClientBuilder_v0_3.forUrl(v03Interface.url())
457500
.withTransport(JSONRPCTransport_v0_3.class, new JSONRPCTransportConfigBuilder_v0_3())
458501
.build();
459502
```
460503

461-
`Client_v0_3` exposes only operations available in v0.3. Return types are v0.3 `org.a2aproject.sdk.compat03.spec` domain objects.
504+
`Client_v0_3` exposes only operations available in v0.3, and its return types are v0.3 `org.a2aproject.sdk.compat03.spec` domain objects. This path is useful for existing integrations but requires applications to use the legacy API and types directly.
462505

463506
### Server: Serving v0.3 Clients
464507

@@ -540,7 +583,8 @@ For JSON-RPC and REST, multi-version convenience modules are also available that
540583
| `Convert_v0_3_To10RequestHandler` | Integration tests | Via transport handler tests using real v1.0 backend |
541584
| Transport handlers | Unit + Integration | Handler-level tests + end-to-end via reference servers |
542585
| Client transports | Unit tests | Mocked v0.3 endpoints |
543-
| `Client_v0_3` | Unit tests | API coverage, absence of v1.0-only methods |
586+
| Unified v1.0 `Client` compatibility adapters | Unit + integration tests | v1.0 client API against v0.3 endpoints, including synchronous, streaming, resubscription, push configuration, authentication, and error behavior |
587+
| `Client_v0_3` | Unit + integration tests | Legacy API coverage, absence of v1.0-only methods, and direct v0.3 request/response behavior |
544588
| Reference servers | Integration tests | Full request/response cycle with v0.3 client |
545589
| TCK | Conformance tests | Protocol conformance against v0.3 spec |
546590

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ For detailed architectural documentation:
122122
- **[Request Flows](.claude/architecture/eventqueue/FLOWS.md)**: Non-streaming vs streaming, cleanup patterns
123123
- **[Usage Scenarios](.claude/architecture/eventqueue/SCENARIOS.md)**: Real-world patterns and common pitfalls
124124
- **Compatibility with previous protocol versions**:
125-
- 0.3 protocol compatibility layer: `.claude/architecture/compatibility_0.3.md`
125+
- 0.3 protocol compatibility layer, including the unified client adapters and legacy `Client_v0_3` API: `.claude/architecture/compatibility_0.3.md`
126126

127127
> 💡 Deep-dive docs are loaded on-demand when working in related areas.
128128

‎boms/sdk/pom.xml‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,33 @@
120120
<version>${project.version}</version>
121121
</dependency>
122122

123+
<!-- Compat 0.3 client adaptation for the concrete v1.0 client -->
124+
<dependency>
125+
<groupId>${project.groupId}</groupId>
126+
<artifactId>a2a-java-sdk-compat-0.3-conversion</artifactId>
127+
<version>${project.version}</version>
128+
</dependency>
129+
<dependency>
130+
<groupId>${project.groupId}</groupId>
131+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter</artifactId>
132+
<version>${project.version}</version>
133+
</dependency>
134+
<dependency>
135+
<groupId>${project.groupId}</groupId>
136+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-jsonrpc</artifactId>
137+
<version>${project.version}</version>
138+
</dependency>
139+
<dependency>
140+
<groupId>${project.groupId}</groupId>
141+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-rest</artifactId>
142+
<version>${project.version}</version>
143+
</dependency>
144+
<dependency>
145+
<groupId>${project.groupId}</groupId>
146+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-grpc</artifactId>
147+
<version>${project.version}</version>
148+
</dependency>
149+
123150
<!-- Compat 0.3 Client modules -->
124151
<dependency>
125152
<groupId>${project.groupId}</groupId>

‎boms/sdk/src/it/sdk-usage-test/pom.xml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,28 @@
113113
<artifactId>a2a-java-sdk-compat-0.3-spec-grpc</artifactId>
114114
</dependency>
115115

116+
<!-- Compat 0.3 adapters for the concrete v1.0 client -->
117+
<dependency>
118+
<groupId>org.a2aproject.sdk</groupId>
119+
<artifactId>a2a-java-sdk-compat-0.3-conversion</artifactId>
120+
</dependency>
121+
<dependency>
122+
<groupId>org.a2aproject.sdk</groupId>
123+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter</artifactId>
124+
</dependency>
125+
<dependency>
126+
<groupId>org.a2aproject.sdk</groupId>
127+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-jsonrpc</artifactId>
128+
</dependency>
129+
<dependency>
130+
<groupId>org.a2aproject.sdk</groupId>
131+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-rest</artifactId>
132+
</dependency>
133+
<dependency>
134+
<groupId>org.a2aproject.sdk</groupId>
135+
<artifactId>a2a-java-sdk-compat-0.3-client-adapter-grpc</artifactId>
136+
</dependency>
137+
116138
<!-- Compat 0.3 Client modules -->
117139
<dependency>
118140
<groupId>org.a2aproject.sdk</groupId>

‎client/base/src/main/java/org/a2aproject/sdk/A2A.java‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collections;
44
import java.util.List;
55
import java.util.Map;
6+
import java.util.Set;
67

78
import org.a2aproject.sdk.client.http.A2ACardResolver;
89
import org.a2aproject.sdk.client.http.A2AHttpClient;
@@ -289,6 +290,11 @@ public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2A
289290
return getAgentCard(A2AHttpClientFactory.create(), agentUrl);
290291
}
291292

293+
public static AgentCard getAgentCard(String agentUrl, Set<String> supportedProtocolVersions)
294+
throws A2AClientError, A2AClientJSONError {
295+
return getAgentCard(A2AHttpClientFactory.create(), agentUrl, supportedProtocolVersions);
296+
}
297+
292298
/**
293299
* Retrieve the agent card using a custom HTTP client.
294300
* <p>
@@ -315,6 +321,11 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl)
315321
return getAgentCard(httpClient, agentUrl, null, null);
316322
}
317323

324+
public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl,
325+
Set<String> supportedProtocolVersions) throws A2AClientError, A2AClientJSONError {
326+
return getAgentCard(httpClient, agentUrl, null, null, supportedProtocolVersions);
327+
}
328+
318329
/**
319330
* Retrieve the agent card with custom path and authentication.
320331
* <p>
@@ -360,6 +371,13 @@ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, M
360371
return getAgentCard(A2AHttpClientFactory.create(), agentUrl, relativeCardPath, authHeaders);
361372
}
362373

374+
public static AgentCard getAgentCard(String agentUrl, String relativeCardPath,
375+
Map<String, String> authHeaders, Set<String> supportedProtocolVersions)
376+
throws A2AClientError, A2AClientJSONError {
377+
return getAgentCard(A2AHttpClientFactory.create(), agentUrl, relativeCardPath, authHeaders,
378+
supportedProtocolVersions);
379+
}
380+
363381
/**
364382
* Retrieve the agent card with full customization options.
365383
* <p>
@@ -393,11 +411,18 @@ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, M
393411
* @throws org.a2aproject.sdk.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema
394412
*/
395413
public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, String relativeCardPath, Map<String, String> authHeaders) throws A2AClientError, A2AClientJSONError {
414+
return getAgentCard(httpClient, agentUrl, relativeCardPath, authHeaders, Set.of("1.0"));
415+
}
416+
417+
public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, String relativeCardPath,
418+
Map<String, String> authHeaders, Set<String> supportedProtocolVersions)
419+
throws A2AClientError, A2AClientJSONError {
396420
A2ACardResolver resolver = A2ACardResolver.builder()
397421
.httpClient(httpClient)
398422
.baseUrl(agentUrl)
399423
.agentCardPath(relativeCardPath)
400424
.authHeaders(authHeaders)
425+
.supportedProtocolVersions(supportedProtocolVersions)
401426
.build();
402427
return resolver.getAgentCard();
403428
}

0 commit comments

Comments
 (0)