Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,6 @@ public void sendMessage(org.a2aproject.sdk.grpc.SendMessageRequest request,
StreamObserver<org.a2aproject.sdk.grpc.SendMessageResponse> responseObserver) {
try {
ServerCallContext context = createCallContext(responseObserver);
A2AVersionValidator.validateProtocolVersion(getAgentCardInternal(), context);
A2AExtensions.validateRequiredExtensions(getAgentCardInternal(), context);
MessageSendParams params = FromProto.messageSendParams(request);
EventKind taskOrMessage = getRequestHandler().onMessageSend(params, context);
org.a2aproject.sdk.grpc.SendMessageResponse response = ToProto.taskOrMessage(taskOrMessage);
Expand Down Expand Up @@ -396,8 +394,6 @@ public void sendStreamingMessage(org.a2aproject.sdk.grpc.SendMessageRequest requ
try {
ServerCallContext context = createCallContext(responseObserver);
installForkedContextWrapper(context);
A2AVersionValidator.validateProtocolVersion(getAgentCardInternal(), context);
A2AExtensions.validateRequiredExtensions(getAgentCardInternal(), context);
MessageSendParams params = FromProto.messageSendParams(request);
Flow.Publisher<StreamingEventKind> publisher = getRequestHandler().onMessageSendStream(params, context);
convertToStreamResponse(publisher, responseObserver, context);
Expand Down Expand Up @@ -570,6 +566,7 @@ public void getExtendedAgentCard(org.a2aproject.sdk.grpc.GetExtendedAgentCardReq
handleError(responseObserver, new UnsupportedOperationError());
return;
}
ServerCallContext context = createCallContext(responseObserver);
AgentCard extendedAgentCard = getExtendedAgentCard();
if (extendedAgentCard != null) {
responseObserver.onNext(ToProto.agentCard(extendedAgentCard));
Expand Down Expand Up @@ -645,6 +642,7 @@ public void deleteTaskPushNotificationConfig(org.a2aproject.sdk.grpc.DeleteTaskP
*/
private <V> ServerCallContext createCallContext(StreamObserver<V> responseObserver) {
CallContextFactory factory = getCallContextFactory();
ServerCallContext context;
if (factory == null) {
// Default implementation when no custom CallContextFactory is provided
// This handles both CDI injection scenarios and test scenarios where callContextFactory is null
Expand Down Expand Up @@ -701,12 +699,16 @@ private <V> ServerCallContext createCallContext(StreamObserver<V> responseObserv
requestedExtensions = A2AExtensions.getRequestedExtensions(List.of(extensionsHeader));
}

return new ServerCallContext(user, state, requestedExtensions, requestedVersion);
context = new ServerCallContext(user, state, requestedExtensions, requestedVersion);
} else {
// TODO: CallContextFactory interface expects ServerCall + Metadata, but we only have StreamObserver
// This is another manifestation of the architectural limitation mentioned above
return factory.create(responseObserver); // Fall back to basic create() method for now
context = factory.create(responseObserver); // Fall back to basic create() method for now
}

A2AVersionValidator.validateProtocolVersion(getAgentCardInternal(), context);
A2AExtensions.validateRequiredExtensions(getAgentCardInternal(), context);
return context;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,52 @@ public <V> ServerCallContext create(StreamObserver<V> streamObserver) {
assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED);
}

@Test
public void testVersionNotSupportedErrorOnGetTask() throws Exception {
// Regression test: getTask previously skipped A2A protocol version
// and extension validation, unlike sendMessage/sendStreamingMessage.
AgentCard agentCard = AgentCard.builder()
.name("test-card")
.description("Test card with version 1.0")
.supportedInterfaces(Collections.singletonList(new AgentInterface("GRPC", "http://localhost:9999")))
.version("1.0.0")
.capabilities(AgentCapabilities.builder()
.streaming(true)
.pushNotifications(false)
.build())
.defaultInputModes(List.of("text"))
.defaultOutputModes(List.of("text"))
.skills(List.of())
.build();

// Create handler that provides incompatible version 2.0 in the context
GrpcHandler handler = new TestGrpcHandler(agentCard, requestHandler, internalExecutor) {
@Override
protected CallContextFactory getCallContextFactory() {
return new CallContextFactory() {
@Override
public <V> ServerCallContext create(StreamObserver<V> streamObserver) {
return new ServerCallContext(
UnauthenticatedUser.INSTANCE,
Map.of("grpc_response_observer", streamObserver),
new HashSet<>(),
"2.0" // Incompatible version
);
}
};
}
};

GetTaskRequest request = GetTaskRequest.newBuilder()
.setId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id())
.build();
StreamRecorder<Task> streamRecorder = StreamRecorder.create();
handler.getTask(request, streamRecorder);
streamRecorder.awaitCompletion(5, TimeUnit.SECONDS);

assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED);
}

@Test
public void testCompatibleVersionSuccess() throws Exception {
// Create AgentCard with protocol version 1.0
Expand Down
Loading