feat: Add HTTP+JSON (REST) protocol binding client and fix server routes#335
feat: Add HTTP+JSON (REST) protocol binding client and fix server routes#335
Conversation
- A2AHttpJsonClient: full IA2AClient implementation using REST endpoints
per spec Section 5.3 (no /v1/ prefix; version is in AgentInterface.Url)
- Routes match spec: /message:send, /tasks/{id}, /extendedAgentCard, etc.
- SubscribeToTask uses POST per spec method mapping table
- A2AClientFactory: selects binding from AgentCard.SupportedInterfaces
based on caller preferences (default: HTTP+JSON first, JSONRPC fallback)
- A2AClientOptions: configurable PreferredBindings list
- ProtocolBindingNames: constants for HTTP+JSON and JSONRPC bindings
- 28 unit tests covering all REST client methods, factory selection,
error handling, query parameter building, and SSE streaming
Addresses #278
Co-authored-by: Copilot <[email protected]>
Server routes now match the A2A spec Section 5.3 method mapping: - Removed /v1/ prefix from all routes (version is in AgentInterface.Url) - Changed SubscribeToTask from MapGet to MapPost per spec - Updated XML docs to reflect spec-compliant routes Co-authored-by: Copilot <[email protected]>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the A2A client and server capabilities by introducing a new HTTP+JSON (REST) protocol binding client and a factory for intelligent client selection. It also refines the server-side REST routes to ensure full compliance with the A2A v1.0 specification, improving interoperability and adherence to standards. These changes allow for more flexible communication with A2A agents while maintaining compatibility with previous implementations. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive HTTP+JSON (REST) client and aligns the server-side routes with the A2A v1.0 specification. The changes are well-structured, with a new factory for client creation, separate client implementations for REST and JSON-RPC, and extensive unit tests. My review focuses on improving maintainability, consistency, and test robustness in the new client and factory code. I've suggested making a switch statement more explicit, using a helper function for consistency, adding documentation to a Dispose method, and enhancing test assertions.
Factory tests now make actual requests through a mock handler to verify: - HTTP+JSON client uses URL from matching AgentInterface - JSON-RPC client uses URL from matching AgentInterface - Default preference selects HTTP+JSON URL, not JSON-RPC URL - Explicit JSON-RPC preference selects RPC URL, not REST URL - Case-insensitive binding match uses correct URL Co-authored-by: Copilot <[email protected]>
rokonec
left a comment
There was a problem hiding this comment.
Review: PR #335 — HTTP+JSON REST Protocol Binding
The goal is correct and well-aligned with the spec — implementing the HTTP+JSON/REST binding (Section 11) is essential for v1.0. URL patterns, HTTP methods, SSE streaming, factory pattern, and test coverage are strong. Build is clean, all 33 new tests pass on both TFMs.
However, several spec-compliance issues need attention before merge.
Summary: 3 CRITICAL, 7 MAJOR, 5 MINOR
Pre-existing issues surfaced by this PR (not blocking, but worth separate follow-up):
- Server
MapA2AExceptionToHttpResultmapsTaskNotCancelable→ 400 (spec: 409),ContentTypeNotSupported→ 422 (spec: 415),InvalidAgentResponsefalls to 500 (spec: 502). See Section 5.4. - Server
Results.Problem()calls don't include RFC 9457typeURIs required by Section 11.6 (e.g.https://a2a-protocol.org/errors/task-not-found).
…ings The A2A spec defines protocolBinding as an open-form string with three built-in values (JSONRPC, GRPC, HTTP+JSON) and support for custom bindings. Replace the hardcoded switch with a registration dictionary: - Add A2AClientFactory.Register(binding, factory) for custom bindings - Built-in HTTP+JSON and JSONRPC bindings are pre-registered - Add ProtocolBindingNames.Grpc constant for the third spec binding - Matching is case-insensitive, consistent with existing behavior - Clear error message guides devs to Register when binding has no factory - Use BuildQueryString helper in GetTaskAsync for consistency - Add remarks to Dispose explaining no-op behavior Co-authored-by: Copilot <[email protected]>
rokonec
left a comment
There was a problem hiding this comment.
The goal is correct and well-aligned with the spec — implementing the HTTP+JSON/REST binding (Section 11) is essential for v1.0. URL patterns, HTTP methods, SSE streaming, factory pattern, and test coverage are strong. Build is clean, all 33 new tests pass on both TFMs.
However, several spec-compliance issues need attention before merge.
Pre-existing issues surfaced by this PR (not blocking, but worth separate follow-up):
Server MapA2AExceptionToHttpResult maps TaskNotCancelable → 400 (spec: 409), ContentTypeNotSupported → 422 (spec: 415), InvalidAgentResponse falls to 500 (spec: 502). See Section 5.4.
Server Results.Problem() calls don't include RFC 9457 type URIs required by Section 11.6 (e.g. https://a2a-protocol.org/errors/task-not-found).
Review fixes: - C1: Serialize TaskState using JsonStringEnumMemberName values (TASK_STATE_WORKING) instead of C# names (Working) for interop - N2: Add statusTimestampAfter and includeArtifacts to ListTasks query - M2: Send CancelTaskRequest.Metadata as JSON body when non-null - N4: Extract shared SSE streaming logic into PostStreamingCoreAsync - M6: Add error mapping tests for 409, 415, 502 status codes A2A-Version header: - Both A2AClient (JSON-RPC) and A2AHttpJsonClient (REST) now send A2A-Version: 1.0 on every outgoing HTTP request per spec Section 11.2 - Refactored all HTTP calls to use SendAsync with explicit request messages to enable consistent header injection Co-authored-by: Copilot <[email protected]>
- parse google.rpc.Status error responses per spec Section 11.6 (AIP-193) - respect agent's supportedInterfaces preference order per spec Section 8.3 - document /card endpoint as SDK-specific convenience (not in spec Section 11.3) - add A2AErrorResponse model registered in JsonContext for AOT support :test_tube: - Generated by Copilot
rokonec
left a comment
There was a problem hiding this comment.
It is good to go.
(I have addressed few missing concerns as well)
Summary
Adds a complete HTTP+JSON (REST) protocol binding client alongside the existing JSON-RPC client, and fixes server-side REST routes to match the A2A v1.0 specification.
Verified end-to-end against the AgentBin compatibility dashboard all HTTP+JSON tests now pass.
Closes #278
Usage
Using the factory (recommended)
The factory inspects the agent card's
SupportedInterfacesand creates the right client automatically:How the factory selects the client
PreferredBindingslist in order (default:["HTTP+JSON", "JSONRPC"])AgentInterface(case-insensitive)"HTTP+JSON"A2AHttpJsonClient(REST)"JSONRPC"A2AClient(JSON-RPC)A2AExceptionOverriding the preference
Using a specific client directly
If you know which binding you want, skip the factory:
New files
A2AHttpJsonClientFullIA2AClientimplementation using REST endpoints per spec Section 5.3A2AClientFactoryStatic factory that selects the protocol binding from anAgentCard's supported interfaces, with configurable preference orderingA2AClientOptionsOptions class withPreferredBindingslist (default: HTTP+JSON first, JSON-RPC fallback)ProtocolBindingNamesConstants for"HTTP+JSON"and"JSONRPC"Server-side fixes
/v1/prefix from all REST routes (version lives inAgentInterface.Url, not route paths)SubscribeToTaskfromGETtoPOSTper spec Section 5.3MapHttpA2A(handler, card, path: "/v1")Design decisions
A2AClient(JSON-RPC) andA2AHttpJsonClient(REST) both implementIA2AClient. Callers can use the factory for automatic selection or instantiate directly.A2AClientFactory.Create(agentCard, options?)matchesAgentCard.SupportedInterfacesagainst the preferred bindings list, case-insensitively.A2AClientis completely unchanged. Existing code continues to work as-is.REST endpoint mapping (spec Section 5.3)
Testing
All 1,244 existing tests pass (300 A2A.UnitTests + 60 AspNetCore + 262 V0_3, 2 TFMs). 33 new tests added.