1818import io .a2a .client .transport .spi .interceptors .auth .AuthInterceptor ;
1919import io .a2a .common .A2AHeaders ;
2020import io .a2a .grpc .A2AServiceGrpc ;
21+ import io .a2a .grpc .A2AServiceGrpc .A2AServiceBlockingV2Stub ;
22+ import io .a2a .grpc .A2AServiceGrpc .A2AServiceStub ;
2123import io .a2a .grpc .CancelTaskRequest ;
2224import io .a2a .grpc .CreateTaskPushNotificationConfigRequest ;
2325import io .a2a .grpc .DeleteTaskPushNotificationConfigRequest ;
2830import io .a2a .grpc .SendMessageResponse ;
2931import io .a2a .grpc .StreamResponse ;
3032import io .a2a .grpc .TaskSubscriptionRequest ;
31-
33+ import io .a2a .grpc .utils .ProtoUtils .FromProto ;
34+ import io .a2a .grpc .utils .ProtoUtils .ToProto ;
3235import io .a2a .spec .A2AClientException ;
3336import io .a2a .spec .AgentCard ;
3437import io .a2a .spec .DeleteTaskPushNotificationConfigParams ;
4952import io .grpc .StatusRuntimeException ;
5053import io .grpc .stub .MetadataUtils ;
5154import io .grpc .stub .StreamObserver ;
55+ import org .jspecify .annotations .Nullable ;
5256
5357public class GrpcTransport implements ClientTransport {
5458
@@ -60,23 +64,24 @@ public class GrpcTransport implements ClientTransport {
6064 Metadata .ASCII_STRING_MARSHALLER );
6165 private final A2AServiceBlockingV2Stub blockingStub ;
6266 private final A2AServiceStub asyncStub ;
63- private final List <ClientCallInterceptor > interceptors ;
67+ private final @ Nullable List <ClientCallInterceptor > interceptors ;
6468 private AgentCard agentCard ;
6569
6670 public GrpcTransport (Channel channel , AgentCard agentCard ) {
6771 this (channel , agentCard , null );
6872 }
6973
70- public GrpcTransport (Channel channel , AgentCard agentCard , List <ClientCallInterceptor > interceptors ) {
74+ public GrpcTransport (Channel channel , AgentCard agentCard , @ Nullable List <ClientCallInterceptor > interceptors ) {
7175 checkNotNullParam ("channel" , channel );
76+ checkNotNullParam ("agentCard" , agentCard );
7277 this .asyncStub = A2AServiceGrpc .newStub (channel );
7378 this .blockingStub = A2AServiceGrpc .newBlockingV2Stub (channel );
7479 this .agentCard = agentCard ;
7580 this .interceptors = interceptors ;
7681 }
7782
7883 @ Override
79- public EventKind sendMessage (MessageSendParams request , ClientCallContext context ) throws A2AClientException {
84+ public EventKind sendMessage (MessageSendParams request , @ Nullable ClientCallContext context ) throws A2AClientException {
8085 checkNotNullParam ("request" , request );
8186
8287 SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest (request , context );
@@ -100,7 +105,7 @@ public EventKind sendMessage(MessageSendParams request, ClientCallContext contex
100105
101106 @ Override
102107 public void sendMessageStreaming (MessageSendParams request , Consumer <StreamingEventKind > eventConsumer ,
103- Consumer <Throwable > errorConsumer , ClientCallContext context ) throws A2AClientException {
108+ Consumer <Throwable > errorConsumer , @ Nullable ClientCallContext context ) throws A2AClientException {
104109 checkNotNullParam ("request" , request );
105110 checkNotNullParam ("eventConsumer" , eventConsumer );
106111 SendMessageRequest grpcRequest = createGrpcSendMessageRequest (request , context );
@@ -117,7 +122,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer<StreamingEv
117122 }
118123
119124 @ Override
120- public Task getTask (TaskQueryParams request , ClientCallContext context ) throws A2AClientException {
125+ public Task getTask (TaskQueryParams request , @ Nullable ClientCallContext context ) throws A2AClientException {
121126 checkNotNullParam ("request" , request );
122127
123128 GetTaskRequest .Builder requestBuilder = GetTaskRequest .newBuilder ();
@@ -138,7 +143,7 @@ public Task getTask(TaskQueryParams request, ClientCallContext context) throws A
138143 }
139144
140145 @ Override
141- public Task cancelTask (TaskIdParams request , ClientCallContext context ) throws A2AClientException {
146+ public Task cancelTask (TaskIdParams request , @ Nullable ClientCallContext context ) throws A2AClientException {
142147 checkNotNullParam ("request" , request );
143148
144149 CancelTaskRequest cancelTaskRequest = CancelTaskRequest .newBuilder ()
@@ -157,7 +162,7 @@ public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A
157162
158163 @ Override
159164 public TaskPushNotificationConfig setTaskPushNotificationConfiguration (TaskPushNotificationConfig request ,
160- ClientCallContext context ) throws A2AClientException {
165+ @ Nullable ClientCallContext context ) throws A2AClientException {
161166 checkNotNullParam ("request" , request );
162167
163168 String configId = request .pushNotificationConfig ().id ();
@@ -180,7 +185,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN
180185 @ Override
181186 public TaskPushNotificationConfig getTaskPushNotificationConfiguration (
182187 GetTaskPushNotificationConfigParams request ,
183- ClientCallContext context ) throws A2AClientException {
188+ @ Nullable ClientCallContext context ) throws A2AClientException {
184189 checkNotNullParam ("request" , request );
185190
186191 GetTaskPushNotificationConfigRequest grpcRequest = GetTaskPushNotificationConfigRequest .newBuilder ()
@@ -200,7 +205,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(
200205 @ Override
201206 public List <TaskPushNotificationConfig > listTaskPushNotificationConfigurations (
202207 ListTaskPushNotificationConfigParams request ,
203- ClientCallContext context ) throws A2AClientException {
208+ @ Nullable ClientCallContext context ) throws A2AClientException {
204209 checkNotNullParam ("request" , request );
205210
206211 ListTaskPushNotificationConfigRequest grpcRequest = ListTaskPushNotificationConfigRequest .newBuilder ()
@@ -221,7 +226,7 @@ public List<TaskPushNotificationConfig> listTaskPushNotificationConfigurations(
221226
222227 @ Override
223228 public void deleteTaskPushNotificationConfigurations (DeleteTaskPushNotificationConfigParams request ,
224- ClientCallContext context ) throws A2AClientException {
229+ @ Nullable ClientCallContext context ) throws A2AClientException {
225230 checkNotNullParam ("request" , request );
226231
227232 DeleteTaskPushNotificationConfigRequest grpcRequest = DeleteTaskPushNotificationConfigRequest .newBuilder ()
@@ -240,7 +245,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC
240245
241246 @ Override
242247 public void resubscribe (TaskIdParams request , Consumer <StreamingEventKind > eventConsumer ,
243- Consumer <Throwable > errorConsumer , ClientCallContext context ) throws A2AClientException {
248+ Consumer <Throwable > errorConsumer , @ Nullable ClientCallContext context ) throws A2AClientException {
244249 checkNotNullParam ("request" , request );
245250 checkNotNullParam ("eventConsumer" , eventConsumer );
246251
@@ -261,7 +266,7 @@ public void resubscribe(TaskIdParams request, Consumer<StreamingEventKind> event
261266 }
262267
263268 @ Override
264- public AgentCard getAgentCard (ClientCallContext context ) throws A2AClientException {
269+ public AgentCard getAgentCard (@ Nullable ClientCallContext context ) throws A2AClientException {
265270 // TODO: Determine how to handle retrieving the authenticated extended agent card
266271 return agentCard ;
267272 }
@@ -270,7 +275,7 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti
270275 public void close () {
271276 }
272277
273- private SendMessageRequest createGrpcSendMessageRequest (MessageSendParams messageSendParams , ClientCallContext context ) {
278+ private SendMessageRequest createGrpcSendMessageRequest (MessageSendParams messageSendParams , @ Nullable ClientCallContext context ) {
274279 SendMessageRequest .Builder builder = SendMessageRequest .newBuilder ();
275280 builder .setRequest (ToProto .message (messageSendParams .message ()));
276281 if (messageSendParams .configuration () != null ) {
@@ -285,8 +290,11 @@ private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messag
285290 /**
286291 * Creates gRPC metadata from ClientCallContext headers.
287292 * Extracts headers like X-A2A-Extensions and sets them as gRPC metadata.
293+ * @param context the client call context containing headers, may be null
294+ * @param payloadAndHeaders the payload and headers wrapper, may be null
295+ * @return the gRPC metadata
288296 */
289- private Metadata createGrpcMetadata (ClientCallContext context , PayloadAndHeaders payloadAndHeaders ) {
297+ private Metadata createGrpcMetadata (@ Nullable ClientCallContext context , @ Nullable PayloadAndHeaders payloadAndHeaders ) {
290298 Metadata metadata = new Metadata ();
291299
292300 if (context != null && context .getHeaders () != null ) {
@@ -328,7 +336,7 @@ private Metadata createGrpcMetadata(ClientCallContext context, PayloadAndHeaders
328336 * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors
329337 * @return blocking stub with metadata interceptor
330338 */
331- private A2AServiceBlockingV2Stub createBlockingStubWithMetadata (ClientCallContext context ,
339+ private A2AServiceBlockingV2Stub createBlockingStubWithMetadata (@ Nullable ClientCallContext context ,
332340 PayloadAndHeaders payloadAndHeaders ) {
333341 Metadata metadata = createGrpcMetadata (context , payloadAndHeaders );
334342 return blockingStub .withInterceptors (MetadataUtils .newAttachHeadersInterceptor (metadata ));
@@ -341,7 +349,7 @@ private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContex
341349 * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors
342350 * @return async stub with metadata interceptor
343351 */
344- private A2AServiceStub createAsyncStubWithMetadata (ClientCallContext context ,
352+ private A2AServiceStub createAsyncStubWithMetadata (@ Nullable ClientCallContext context ,
345353 PayloadAndHeaders payloadAndHeaders ) {
346354 Metadata metadata = createGrpcMetadata (context , payloadAndHeaders );
347355 return asyncStub .withInterceptors (MetadataUtils .newAttachHeadersInterceptor (metadata ));
@@ -351,7 +359,7 @@ private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigPa
351359 return getTaskPushNotificationConfigName (params .id (), params .pushNotificationConfigId ());
352360 }
353361
354- private String getTaskPushNotificationConfigName (String taskId , String pushNotificationConfigId ) {
362+ private String getTaskPushNotificationConfigName (String taskId , @ Nullable String pushNotificationConfigId ) {
355363 StringBuilder name = new StringBuilder ();
356364 name .append ("tasks/" );
357365 name .append (taskId );
@@ -366,7 +374,7 @@ private String getTaskPushNotificationConfigName(String taskId, String pushNotif
366374 }
367375
368376 private PayloadAndHeaders applyInterceptors (String methodName , Object payload ,
369- AgentCard agentCard , ClientCallContext clientCallContext ) {
377+ AgentCard agentCard , @ Nullable ClientCallContext clientCallContext ) {
370378 PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders (payload ,
371379 clientCallContext != null ? clientCallContext .getHeaders () : null );
372380 if (interceptors != null && ! interceptors .isEmpty ()) {
0 commit comments