Skip to content

Commit eae33bc

Browse files
committed
fix(rest): support application/a2a+json content type
1 parent c257762 commit eae33bc

4 files changed

Lines changed: 33 additions & 25 deletions

File tree

‎common/src/main/java/org/a2aproject/sdk/common/MediaType.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ public interface MediaType {
55

66
/** MIME type for JSON content: {@code application/json}. */
77
String APPLICATION_JSON = "application/json";
8+
9+
/** MIME type the HTTP+JSON binding should use (A2A spec section 11): {@code application/a2a+json}. */
10+
String APPLICATION_A2A_JSON = "application/a2a+json";
811
}

‎reference/rest/src/main/java/org/a2aproject/sdk/server/rest/quarkus/A2AServerRoutes.java‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.a2aproject.sdk.server.rest.quarkus;
22

3+
import static org.a2aproject.sdk.common.MediaType.APPLICATION_A2A_JSON;
34
import static org.a2aproject.sdk.server.ServerCallContext.TRANSPORT_KEY;
45
import static org.a2aproject.sdk.spec.A2AMethods.CANCEL_TASK_METHOD;
56
import static org.a2aproject.sdk.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD;
@@ -774,13 +775,14 @@ private String extractTenant(RoutingContext rc) {
774775
}
775776

776777
/**
777-
* Check if the request content type is application/json.
778+
* Check if the request content type is application/json or application/a2a+json.
778779
* @param rc the routing context
779-
* @return true if the content type is application/json - false otherwise.
780+
* @return true if the content type is one of those - false otherwise.
780781
*/
781782
private boolean validateContentType(RoutingContext rc) {
782783
String contentType = rc.request().getHeader(CONTENT_TYPE);
783-
if (contentType == null || !contentType.trim().startsWith(APPLICATION_JSON)) {
784+
if (contentType == null || !(contentType.trim().startsWith(APPLICATION_JSON)
785+
|| contentType.trim().startsWith(APPLICATION_A2A_JSON))) {
784786
sendResponse(rc, jsonRestHandler.createErrorResponse(new ContentTypeNotSupportedError(null, null, null)));
785787
return false;
786788
}

‎transport/rest/src/main/java/org/a2aproject/sdk/transport/rest/handler/RestHandler.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.a2aproject.sdk.transport.rest.handler;
22

3+
import static org.a2aproject.sdk.common.MediaType.APPLICATION_A2A_JSON;
34
import static org.a2aproject.sdk.common.MediaType.APPLICATION_JSON;
45
import static org.a2aproject.sdk.server.util.async.AsyncUtils.createTubeConfig;
56

@@ -702,7 +703,7 @@ private HTTPRestResponse createSuccessResponse(int statusCode, com.google.protob
702703
try {
703704
// Include default value fields to ensure empty arrays, zeros, etc. are present in JSON
704705
String jsonBody = ProtoJsonUtils.toJson(JsonFormat.printer().alwaysPrintFieldsWithNoPresence(), builder);
705-
return new HTTPRestResponse(statusCode, APPLICATION_JSON, jsonBody);
706+
return new HTTPRestResponse(statusCode, APPLICATION_A2A_JSON, jsonBody);
706707
} catch (InvalidProtocolBufferException e) {
707708
LOGGER.log(Level.SEVERE, "Failed to serialize response", e);
708709
return createErrorResponse(new InternalError("Internal error"));
@@ -722,7 +723,7 @@ public HTTPRestResponse createErrorResponse(A2AError error) {
722723

723724
private HTTPRestResponse createErrorResponse(int statusCode, A2AError error) {
724725
String jsonBody = new HTTPRestErrorResponse(error).toJson();
725-
return new HTTPRestResponse(statusCode, APPLICATION_JSON, jsonBody);
726+
return new HTTPRestResponse(statusCode, APPLICATION_A2A_JSON, jsonBody);
726727
}
727728

728729
/**

‎transport/rest/src/test/java/org/a2aproject/sdk/transport/rest/handler/RestHandlerTest.java‎

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.a2aproject.sdk.transport.rest.handler;
22

3+
import static org.a2aproject.sdk.common.MediaType.APPLICATION_A2A_JSON;
34
import static org.a2aproject.sdk.common.MediaType.APPLICATION_JSON;
45

56
import java.util.Collections;
@@ -59,13 +60,13 @@ public void testGetTaskSuccess() {
5960
RestHandler.HTTPRestResponse response = handler.getTask(callContext, "", MINIMAL_TASK.id(), 0);
6061

6162
Assertions.assertEquals(200, response.getStatusCode());
62-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
63+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
6364
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
6465

6566
response = handler.getTask(callContext, "", MINIMAL_TASK.id(), 2);
6667

6768
Assertions.assertEquals(200, response.getStatusCode());
68-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
69+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
6970
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
7071
}
7172

@@ -98,7 +99,7 @@ public void testListTasksStatusWireString() {
9899
null, null, null);
99100

100101
Assertions.assertEquals(200, response.getStatusCode());
101-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
102+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
102103
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
103104
}
104105

@@ -140,7 +141,7 @@ public void testSendMessage() throws InvalidProtocolBufferException {
140141

141142
RestHandler.HTTPRestResponse response = handler.sendMessage(callContext, "", requestBody);
142143
Assertions.assertEquals(200, response.getStatusCode(), response.toString());
143-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
144+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
144145
Assertions.assertNotNull(response.getBody());
145146
}
146147

@@ -175,7 +176,7 @@ public void testSendMessageWrongValueBody() {
175176
RestHandler.HTTPRestResponse response = handler.sendMessage(callContext, "", requestBody);
176177

177178
Assertions.assertEquals(422, response.getStatusCode());
178-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
179+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
179180
JsonObject body = JsonParser.parseString(response.getBody()).getAsJsonObject();
180181
JsonObject error = body.getAsJsonObject("error");
181182
Assertions.assertEquals(422, error.get("code").getAsInt());
@@ -211,7 +212,7 @@ public void testCancelTaskSuccess() {
211212
RestHandler.HTTPRestResponse response = handler.cancelTask(callContext, "", requestBody, MINIMAL_TASK.id());
212213

213214
Assertions.assertEquals(200, response.getStatusCode());
214-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
215+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
215216
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
216217
}
217218

@@ -253,7 +254,7 @@ public void testCancelTaskWithMetadata() {
253254
RestHandler.HTTPRestResponse response = handler.cancelTask(callContext, "", requestBody, MINIMAL_TASK.id());
254255

255256
Assertions.assertEquals(200, response.getStatusCode());
256-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
257+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
257258
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
258259
}
259260

@@ -277,7 +278,7 @@ public void testCancelTaskWithEmptyMetadata() {
277278
RestHandler.HTTPRestResponse response = handler.cancelTask(callContext, "", requestBody, MINIMAL_TASK.id());
278279

279280
Assertions.assertEquals(200, response.getStatusCode());
280-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
281+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
281282
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
282283
}
283284

@@ -297,7 +298,7 @@ public void testCancelTaskWithNoMetadata() {
297298
RestHandler.HTTPRestResponse response = handler.cancelTask(callContext, "", requestBody, MINIMAL_TASK.id());
298299

299300
Assertions.assertEquals(200, response.getStatusCode());
300-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
301+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
301302
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
302303
}
303304

@@ -315,7 +316,7 @@ public void testCancelTaskWithNullBody() {
315316
RestHandler.HTTPRestResponse response = handler.cancelTask(callContext, "", null, MINIMAL_TASK.id());
316317

317318
Assertions.assertEquals(200, response.getStatusCode());
318-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
319+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
319320
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
320321
}
321322

@@ -390,7 +391,7 @@ public void testPushNotificationConfigSuccess() {
390391
RestHandler.HTTPRestResponse response = handler.createTaskPushNotificationConfiguration(callContext, "", requestBody, MINIMAL_TASK.id());
391392

392393
Assertions.assertEquals(201, response.getStatusCode(), response.toString());
393-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
394+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
394395
Assertions.assertNotNull(response.getBody());
395396
}
396397

@@ -431,11 +432,11 @@ public void testGetPushNotificationConfig() {
431432
}""".formatted(MINIMAL_TASK.id());
432433
RestHandler.HTTPRestResponse response = handler.createTaskPushNotificationConfiguration(callContext, "", createRequestBody, MINIMAL_TASK.id());
433434
Assertions.assertEquals(201, response.getStatusCode(), response.toString());
434-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
435+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
435436
// Now get it
436437
response = handler.getTaskPushNotificationConfiguration(callContext, "", MINIMAL_TASK.id(), "default-config-id");
437438
Assertions.assertEquals(200, response.getStatusCode(), response.toString());
438-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
439+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
439440
}
440441

441442
@Test
@@ -454,7 +455,7 @@ public void testListPushNotificationConfigs() {
454455
RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(callContext, "", MINIMAL_TASK.id(), 0, "");
455456

456457
Assertions.assertEquals(200, response.getStatusCode());
457-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
458+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
458459
Assertions.assertNotNull(response.getBody());
459460
}
460461

@@ -783,7 +784,7 @@ public void testRequiredExtensionProvidedSuccess() {
783784

784785
// Should succeed without error
785786
Assertions.assertEquals(200, response.getStatusCode());
786-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
787+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
787788
Assertions.assertNotNull(response.getBody());
788789
}
789790

@@ -984,7 +985,7 @@ public void testCompatibleVersionSuccess() {
984985

985986
// Should succeed without error
986987
Assertions.assertEquals(200, response.getStatusCode());
987-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
988+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
988989
Assertions.assertNotNull(response.getBody());
989990
}
990991

@@ -1070,7 +1071,7 @@ public void testListTasksProtobufEnumStatus() {
10701071
null, null, null);
10711072

10721073
Assertions.assertEquals(200, response.getStatusCode());
1073-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
1074+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
10741075
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
10751076
}
10761077

@@ -1084,7 +1085,7 @@ public void testListTasksEnumConstantStatus() {
10841085
null, null, null);
10851086

10861087
Assertions.assertEquals(200, response.getStatusCode());
1087-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
1088+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
10881089
Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id()));
10891090
}
10901091

@@ -1097,7 +1098,7 @@ public void testListTasksEmptyResultIncludesAllFields() {
10971098
null, null, null);
10981099

10991100
Assertions.assertEquals(200, response.getStatusCode());
1100-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
1101+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
11011102

11021103
String body = response.getBody();
11031104
// Verify all required fields are present (not missing)
@@ -1121,7 +1122,7 @@ void constructorDoesNotResolveAgentCardInstances() {
11211122
private static void assertProblemDetail(RestHandler.HTTPRestResponse response,
11221123
int expectedStatus, String expectedReason, String expectedMessage) {
11231124
Assertions.assertEquals(expectedStatus, response.getStatusCode());
1124-
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
1125+
Assertions.assertEquals(APPLICATION_A2A_JSON, response.getContentType());
11251126
JsonObject body = JsonParser.parseString(response.getBody()).getAsJsonObject();
11261127
Assertions.assertTrue(body.has("error"), "error wrapper should be present");
11271128
JsonObject error = body.getAsJsonObject("error");
@@ -1264,6 +1265,7 @@ public void testExtendedAgentCardWithRouterKnownTenant() {
12641265
RestHandler.HTTPRestResponse response = handler.getExtendedAgentCard(callContext, "acme");
12651266

12661267
Assertions.assertEquals(200, response.getStatusCode());
1268+
// The agent card is discovery over plain HTTP, not an A2A protocol response.
12671269
Assertions.assertEquals(APPLICATION_JSON, response.getContentType());
12681270
Assertions.assertTrue(response.getBody().contains("acme-card"));
12691271
}

0 commit comments

Comments
 (0)