docs: PRD for A2A protocol 0.3 backward compatibility layer#785
docs: PRD for A2A protocol 0.3 backward compatibility layer#785jmesnil wants to merge 3 commits intoa2aproject:compat_0.3from
Conversation
Define the architecture and implementation plan for a compat-0.3 module that enables v1.0 SDK users to interoperate with v0.3 agents across all three transports (JSON-RPC, gRPC, REST). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…0.3 compat PRD Add sections covering how server operators enable dual v0.3/v1.0 support (separate URLs recommended), how clients select the right API based on protocolVersion, error hierarchy mapping between versions, and why server-common internal changes don't impact the compat wire-boundary layer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces an architecture document for a backward compatibility layer, enabling the A2A Java SDK to interoperate with v0.3 agents. The review feedback highlights the need for more precise logic regarding the mapping of AgentCard interfaces and suggests providing a more robust, version-preference-aware example for client instantiation to better guide developers.
| | `SendMessageConfiguration` | `a2a.v1.SendMessageConfiguration` | `return_immediately` ↔ `!blocking`, `task_push_notification_config` ↔ `push_notification` | | ||
| | `Task` | `a2a.v1.Task` | `CANCELED` ↔ `CANCELLED`, no `REJECTED` in v0.3 | | ||
| | `TaskState.TASK_STATE_REJECTED` | `TaskState.TASK_STATE_FAILED` | Map to FAILED + metadata `"original_state": "REJECTED"` | | ||
| | `AgentCard` | `a2a.v1.AgentCard` | `supported_interfaces` → `url` + `preferred_transport` + `additional_interfaces` | |
There was a problem hiding this comment.
The mapping from a v1.0 AgentCard to a v0.3 AgentCard is not fully specified. The v1.0 supported_interfaces is a list, while v0.3 has a single url and preferred_transport plus additional_interfaces. The document should clarify the logic for selecting which interface from the v1.0 list becomes the primary one in v0.3.
For example, does it use the first interface in the list? Or is there some other selection criteria? Clarifying this will prevent ambiguity during implementation.
| for (AgentInterface iface : card.supportedInterfaces()) { | ||
| if ("0.3".equals(iface.protocolVersion())) { | ||
| // Use the compat client for v0.3 agents | ||
| Compat03Client client = Compat03ClientBuilder.forUrl(iface.url()) | ||
| .transport("JSONRPC") | ||
| .build(); | ||
| } else if ("1.0".equals(iface.protocolVersion())) { | ||
| // Use the standard client for v1.0 agents | ||
| Client client = ClientBuilder.forUrl(iface.url()) | ||
| .transport("JSONRPC") | ||
| .build(); | ||
| } | ||
| } |
There was a problem hiding this comment.
The example code for choosing the client is a bit simplistic and could be misleading. It iterates through all interfaces and could potentially instantiate multiple clients if an agent supports both v0.3 and v1.0. A more realistic example would show how to select a preferred version (e.g., v1.0) and fall back to the other if necessary.
Consider improving the example to demonstrate a version preference logic, which would be more helpful for developers implementing this.
For example:
AgentCard card = // ... fetch agent card from /.well-known/agent-card.json
// Find the best available interface, preferring v1.0
Optional<AgentInterface> v1_iface = card.supportedInterfaces().stream()
.filter(iface -> "1.0".equals(iface.protocolVersion()) && "JSONRPC".equals(iface.protocolBinding()))
.findFirst();
Optional<AgentInterface> v03_iface = card.supportedInterfaces().stream()
.filter(iface -> "0.3".equals(iface.protocolVersion()) && "JSONRPC".equals(iface.protocolBinding()))
.findFirst();
if (v1_iface.isPresent()) {
// Use the standard client for v1.0 agents
Client client = ClientBuilder.forUrl(v1_iface.get().url())
.transport("JSONRPC")
.build();
// ... use v1.0 client
} else if (v03_iface.isPresent()) {
// Use the compat client for v0.3 agents
Compat03Client client = Compat03ClientBuilder.forUrl(v03_iface.get().url())
.transport("JSONRPC")
.build();
// ... use v0.3 client
} else {
// No compatible JSONRPC interface found
}Document that the agent card is produced in v1.0 format only, with pros and cons for the user experience. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
||
| **2. Declare both protocol versions in the AgentCard with separate URLs:** | ||
|
|
||
| Each protocol version should use a distinct URL to avoid any dispatch ambiguity. The recommended pattern is to use a `/v0.3` path prefix for compat endpoints: |
There was a problem hiding this comment.
0.3.0 requires /v1/* as the path prefix. Also 1.0 has an optional tenant which could be confused with this prefix
There was a problem hiding this comment.
@ehsavoie My understanding is that with 0.3.0, you append the /v1 prefix to the agent interface so this would not be a problem: you'd end up with: http://example.org/v0.3/v1/message:send for the HTTP+JSON endpoint.
Likewise for tenancy in the 1.0 spec.
There was a problem hiding this comment.
Well in the RI the routing is done on /v1/ so http://example.org/v0.3/v1/ wouldn't match (maybe if there is a virtual host).
JSONRPC routing is on / for the RI.
Why the tenant was introduced then for 1.0 if we could have used any prefix ?
There was a problem hiding this comment.
I'm not sure we are talking about the same thing. Nothing should prevent a remote agent to be hosted on any URL. It does not have to be at the root of a domain.
My agent interfaces could be http://example.org/agents/echo. Any resolution of the endpoints should be relative to this context (ie POST http://example.org/agents/echo/v1/message:send).
The quarkus reference can be limited but that's not mandated by the spec.
|
|
||
| ``` | ||
| compat-0.3/ | ||
| ├── pom.xml # Parent POM for all compat-0.3 submodules |
There was a problem hiding this comment.
We also will need the spec/ module
|
|
||
| ### Java Package Convention | ||
|
|
||
| All compat-0.3 code uses the `io.a2a.compat03` package root to avoid classpath collisions with the v1.0 modules: |
There was a problem hiding this comment.
Package should now be org.a2aproject.sdk.compat03
| </dependency> | ||
| ``` | ||
|
|
||
| The same pattern applies for gRPC and REST transports. |
There was a problem hiding this comment.
You are mentioning transports here, but the examples a bit above are for the reference/ modules (rather than transport/)
This relates to #762