diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index c3c8c648f..ae0d9b40b 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -390,17 +390,41 @@ public abstract void subscribeToTask(@NonNull TaskIdParams request, * @throws A2AClientException if retrieving the extended agent card fails for any reason */ public AgentCard getExtendedAgentCard() throws A2AClientException { - return getExtendedAgentCard(null); + return getExtendedAgentCard(null, null); } /** * Retrieve the extended AgentCard. * + * @param tenant Optional tenant + * @return the extended AgentCard + * @throws A2AClientException if retrieving the extended agent card fails for any reason + */ + public AgentCard getExtendedAgentCard(@Nullable String tenant) throws A2AClientException { + return getExtendedAgentCard(tenant, null); + } + + /** + * Retrieve the extended AgentCard. + * + * @param context optional client call context for the request (may be {@code null}) + * @return the extended AgentCard + * @throws A2AClientException if retrieving the extended agent card fails for any reason + */ + public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { + return getExtendedAgentCard(null, context); + } + + /** + * Retrieve the extended AgentCard. + * + * @param tenant Optional tenant * @param context optional client call context for the request (may be {@code null}) * @return the extended AgentCard * @throws A2AClientException if retrieving the extended agent card fails for any reason */ - public abstract AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException; + public abstract AgentCard getExtendedAgentCard(@Nullable String tenant, + @Nullable ClientCallContext context) throws A2AClientException; /** * Close the transport and release any associated resources. diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index e60cac4e5..a603a8515 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -17,6 +17,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -601,19 +602,21 @@ public void subscribeToTask(@NonNull TaskIdParams request, *

* Example: *

{@code
-     * AgentCard updatedCard = client.getExtendedAgentCard(null);
+     * AgentCard updatedCard = client.getExtendedAgentCard();
      * System.out.println("Agent version: " + updatedCard.version());
      * System.out.println("Skills: " + updatedCard.skills().size());
      * }
* + * @param tenant Optional tenant * @param context custom call context for request interceptors (optional) * @return the agent's extended agent card * @throws A2AClientException if the extended agent card cannot be retrieved * @see AgentCard */ @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { - agentCard = clientTransport.getExtendedAgentCard(context); + public AgentCard getExtendedAgentCard(@Nullable String tenant, @Nullable ClientCallContext context) throws A2AClientException { + GetExtendedAgentCardParams params = new GetExtendedAgentCardParams(tenant); + agentCard = clientTransport.getExtendedAgentCard(params, context); return agentCard; } diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 7824e83c0..cd8fa67a9 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -35,6 +35,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -356,9 +357,12 @@ private MessageSendParams createRequestWithTenant(MessageSendParams request) { } @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { - GetExtendedAgentCardRequest request = GetExtendedAgentCardRequest.newBuilder() - .build(); + public AgentCard getExtendedAgentCard(GetExtendedAgentCardParams params, @Nullable ClientCallContext context) throws A2AClientException { + GetExtendedAgentCardRequest.Builder builder = GetExtendedAgentCardRequest.newBuilder(); + if (params.tenant() != null) { + builder.setTenant(params.tenant()); + } + GetExtendedAgentCardRequest request = builder.build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, request, agentCard, context); try { diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 9b8af4ea2..a26600fe3 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -51,6 +51,7 @@ import io.a2a.spec.AgentInterface; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -284,13 +285,13 @@ public void subscribeToTask(TaskIdParams request, Consumer e } @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { + public AgentCard getExtendedAgentCard(GetExtendedAgentCardParams params, @Nullable ClientCallContext context) throws A2AClientException { try { PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, - ProtoUtils.ToProto.extendedAgentCard(), agentCard, context); + ProtoUtils.ToProto.extendedAgentCard(params), agentCard, context); try { - String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, ""), payloadAndHeaders, GET_EXTENDED_AGENT_CARD_METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, params.tenant()), payloadAndHeaders, GET_EXTENDED_AGENT_CARD_METHOD); GetExtendedAgentCardResponse response = unmarshalResponse(httpResponseBody, GET_EXTENDED_AGENT_CARD_METHOD); return response.getResult(); } catch (IOException | InterruptedException | JsonProcessingException e) { diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index e4983142b..a5fa5d813 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -50,6 +50,7 @@ import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; @@ -361,7 +362,8 @@ public void testA2AClientGetExtendedAgentCard() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - AgentCard agentCard = client.getExtendedAgentCard(null); + GetExtendedAgentCardParams params = new GetExtendedAgentCardParams(null); + AgentCard agentCard = client.getExtendedAgentCard(params, null); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard).url()); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 09e68b88a..0e3c95d6b 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -45,6 +45,7 @@ import io.a2a.spec.AgentInterface; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -414,15 +415,13 @@ public void subscribeToTask(TaskIdParams request, Consumer e } @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { + public AgentCard getExtendedAgentCard(GetExtendedAgentCardParams params, @Nullable ClientCallContext context) throws A2AClientException { try { PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, null, agentCard, context); - String url = Utils.buildBaseUrl(agentInterface, "") + "/extendedAgentCard"; + String url = Utils.buildBaseUrl(agentInterface, params.tenant()) + "/extendedAgentCard"; A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); - if (payloadAndHeaders.getHeaders() != null) { - for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { getBuilder.addHeader(entry.getKey(), entry.getValue()); - } } A2AHttpResponse response = getBuilder.get(); if (!response.success()) { diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 82fe40996..3ecad6019 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -8,6 +8,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -139,11 +140,12 @@ void subscribeToTask(TaskIdParams request, Consumer eventCon /** * Retrieve the extended AgentCard. * + * @param params the parameters to get the extended agent card. * @param context optional client call context for the request (may be {@code null}) * @return the extended agent card * @throws A2AClientException if retrieving the agent card fails for any reason */ - AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException; + AgentCard getExtendedAgentCard(GetExtendedAgentCardParams params, @Nullable ClientCallContext context) throws A2AClientException; /** * Close the transport and release any associated resources. diff --git a/extras/opentelemetry/client-propagation/src/main/java/io/a2a/extras/opentelemetry/client/propagation/OpenTelemetryClientPropagatorTransport.java b/extras/opentelemetry/client-propagation/src/main/java/io/a2a/extras/opentelemetry/client/propagation/OpenTelemetryClientPropagatorTransport.java index 53cd134bd..52854d3ef 100644 --- a/extras/opentelemetry/client-propagation/src/main/java/io/a2a/extras/opentelemetry/client/propagation/OpenTelemetryClientPropagatorTransport.java +++ b/extras/opentelemetry/client-propagation/src/main/java/io/a2a/extras/opentelemetry/client/propagation/OpenTelemetryClientPropagatorTransport.java @@ -7,6 +7,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -112,8 +113,8 @@ public void subscribeToTask(TaskIdParams request, Consumer e } @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { - return delegate.getExtendedAgentCard(propagateContext(context)); + public AgentCard getExtendedAgentCard(GetExtendedAgentCardParams request, @Nullable ClientCallContext context) throws A2AClientException { + return delegate.getExtendedAgentCard(request, propagateContext(context)); } @Override diff --git a/extras/opentelemetry/client/src/main/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransport.java b/extras/opentelemetry/client/src/main/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransport.java index ed0db190d..98627c258 100644 --- a/extras/opentelemetry/client/src/main/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransport.java +++ b/extras/opentelemetry/client/src/main/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransport.java @@ -21,6 +21,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -393,13 +394,13 @@ public void subscribeToTask(TaskIdParams request, Consumer e } @Override - public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException { + public AgentCard getExtendedAgentCard(GetExtendedAgentCardParams params, @Nullable ClientCallContext context) throws A2AClientException { ClientCallContext clientContext = createContext(context); SpanBuilder spanBuilder = tracer.spanBuilder(A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD).setSpanKind(SpanKind.CLIENT); spanBuilder.setAttribute(GENAI_OPERATION_NAME, A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD); Span span = spanBuilder.startSpan(); try (Scope scope = span.makeCurrent()) { - AgentCard result = delegate.getExtendedAgentCard(clientContext); + AgentCard result = delegate.getExtendedAgentCard(params, clientContext); if (result != null && extractResponse()) { span.setAttribute(GENAI_RESPONSE, result.toString()); } diff --git a/extras/opentelemetry/client/src/test/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransportTest.java b/extras/opentelemetry/client/src/test/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransportTest.java index e930c18e1..d8a977424 100644 --- a/extras/opentelemetry/client/src/test/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransportTest.java +++ b/extras/opentelemetry/client/src/test/java/io/a2a/extras/opentelemetry/client/OpenTelemetryClientTransportTest.java @@ -12,6 +12,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -323,9 +324,10 @@ void testSubscribeToTask() throws A2AClientException { void testGetAgentCard_Success() throws A2AClientException { AgentCard expectedResult = mock(AgentCard.class); when(expectedResult.toString()).thenReturn("response-string"); - when(delegate.getExtendedAgentCard(any(ClientCallContext.class))).thenReturn(expectedResult); + when(delegate.getExtendedAgentCard(any(GetExtendedAgentCardParams.class), any(ClientCallContext.class))).thenReturn(expectedResult); - AgentCard result = transport.getExtendedAgentCard(context); + GetExtendedAgentCardParams params = mock(GetExtendedAgentCardParams.class); + AgentCard result = transport.getExtendedAgentCard(params, context); assertEquals(expectedResult, result); verify(tracer).spanBuilder(A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD); @@ -337,9 +339,10 @@ void testGetAgentCard_Success() throws A2AClientException { @Test void testGetAgentCard_NullResponse() throws A2AClientException { - when(delegate.getExtendedAgentCard(any(ClientCallContext.class))).thenReturn(null); + when(delegate.getExtendedAgentCard(any(GetExtendedAgentCardParams.class), any(ClientCallContext.class))).thenReturn(null); - AgentCard result = transport.getExtendedAgentCard(context); + GetExtendedAgentCardParams params = mock(GetExtendedAgentCardParams.class); + AgentCard result = transport.getExtendedAgentCard(params, context); assertNull(result); verify(tracer).spanBuilder(A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index d4b1ca55f..ebd859909 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -27,6 +27,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetExtendedAgentCardParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.ListTaskPushNotificationConfigParams; @@ -55,8 +56,12 @@ public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { return AgentCardMapper.INSTANCE.toProto(agentCard); } - public static io.a2a.grpc.GetExtendedAgentCardRequest extendedAgentCard() { - return GetExtendedAgentCardRequest.newBuilder().build(); + public static io.a2a.grpc.GetExtendedAgentCardRequest extendedAgentCard(GetExtendedAgentCardParams params) { + GetExtendedAgentCardRequest.Builder builder = GetExtendedAgentCardRequest.newBuilder(); + if (params.tenant() != null) { + builder.setTenant(params.tenant()); + } + return builder.build(); } public static io.a2a.grpc.GetTaskRequest getTaskRequest(TaskQueryParams params) { diff --git a/spec/src/main/java/io/a2a/spec/GetExtendedAgentCardParams.java b/spec/src/main/java/io/a2a/spec/GetExtendedAgentCardParams.java new file mode 100644 index 000000000..c103c6a2a --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/GetExtendedAgentCardParams.java @@ -0,0 +1,12 @@ +package io.a2a.spec; + +import org.jspecify.annotations.Nullable; + +/** + * Parameters to get the extended agent card. + * + * @param tenant optional tenant, provided as a path parameter. + * @see A2A Protocol Specification + */ +public record GetExtendedAgentCardParams(@Nullable String tenant) { +}