URL Pattern: {@code /tasks/{taskId}?historyLength=10} * * @param rc the Vert.x routing context (taskId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void getTask(RoutingContext rc) { + public void getTask(RoutingContext rc, RestHandler handler) { String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, GET_TASK_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else { Integer historyLength = null; if (rc.request().params().contains(HISTORY_LENGTH_PARAM)) { historyLength = Integer.valueOf(rc.request().params().get(HISTORY_LENGTH_PARAM)); } - response = jsonRestHandler.getTask(context, extractTenant(rc), taskId, historyLength); + response = handler.getTask(context, extractTenant(rc), taskId, historyLength); } } catch (NumberFormatException e) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad historyLength")); + response = handler.createErrorResponse(new InvalidParamsError("bad historyLength")); } catch (Throwable t) { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } finally { sendResponse(rc, response); } @@ -480,10 +513,11 @@ public void getTask(RoutingContext rc) { *
URL Pattern: {@code /tasks/{taskId}:cancel} * * @param rc the Vert.x routing context (taskId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void cancelTask(String body, RoutingContext rc) { - if (!validateContentTypeForOptionalBody(rc, body)) { + public void cancelTask(String body, RoutingContext rc, RestHandler handler) { + if (!validateContentTypeForOptionalBody(rc, body, handler)) { return; } String taskId = rc.pathParam("taskId"); @@ -491,16 +525,16 @@ public void cancelTask(String body, RoutingContext rc) { HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.cancelTask(context, extractTenant(rc), body, taskId); + response = handler.cancelTask(context, extractTenant(rc), body, taskId); } } catch (Throwable t) { if (t instanceof A2AError error) { - response = jsonRestHandler.createErrorResponse(error); + response = handler.createErrorResponse(error); } else { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } } finally { sendResponse(rc, response); @@ -547,18 +581,19 @@ private void sendResponse(RoutingContext rc, @Nullable HTTPRestResponse response * * * @param rc the Vert.x routing context (taskId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void subscribeToTask(RoutingContext rc) { + public void subscribeToTask(RoutingContext rc, RestHandler handler) { String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, SUBSCRIBE_TO_TASK_METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { if (taskId == null || taskId.isEmpty()) { - error = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + error = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - HTTPRestResponse response = jsonRestHandler.subscribeToTask(context, extractTenant(rc), taskId); + HTTPRestResponse response = handler.subscribeToTask(context, extractTenant(rc), taskId); if (response instanceof HTTPRestStreamingResponse hTTPRestStreamingResponse) { streamingResponse = hTTPRestStreamingResponse; } else { @@ -594,10 +629,11 @@ public void subscribeToTask(RoutingContext rc) { * * @param body the JSON request body with notification configuration * @param rc the Vert.x routing context (taskId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void createTaskPushNotificationConfiguration(String body, RoutingContext rc) { - if(!validateContentType(rc)) { + public void createTaskPushNotificationConfiguration(String body, RoutingContext rc, RestHandler handler) { + if(!validateContentType(rc, handler)) { return; } String taskId = rc.pathParam("taskId"); @@ -605,13 +641,13 @@ public void createTaskPushNotificationConfiguration(String body, RoutingContext HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.createTaskPushNotificationConfiguration(context, extractTenant(rc), body, taskId); + response = handler.createTaskPushNotificationConfiguration(context, extractTenant(rc), body, taskId); } } catch (Throwable t) { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } finally { sendResponse(rc, response); } @@ -626,24 +662,25 @@ public void createTaskPushNotificationConfiguration(String body, RoutingContext *
URL Pattern: {@code /tasks/{taskId}/pushNotificationConfigs/{configId}} * * @param rc the Vert.x routing context (taskId and configId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void getTaskPushNotificationConfiguration(RoutingContext rc) { + public void getTaskPushNotificationConfiguration(RoutingContext rc, RestHandler handler) { String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); ServerCallContext context = createCallContext(rc, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); - } else if (configId == null || configId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad configuration id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); + } else if (configId == null || configId.isEmpty()) { + response = handler.createErrorResponse(new InvalidParamsError("bad configuration id")); }else { - response = jsonRestHandler.getTaskPushNotificationConfiguration(context, extractTenant(rc), taskId, configId); + response = handler.getTaskPushNotificationConfiguration(context, extractTenant(rc), taskId, configId); } } catch (Throwable t) { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } finally { sendResponse(rc, response); } @@ -665,15 +702,16 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { * * * @param rc the Vert.x routing context (taskId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void listTaskPushNotificationConfigurations(RoutingContext rc) { + public void listTaskPushNotificationConfigurations(RoutingContext rc, RestHandler handler) { String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else { int pageSize = 0; if (rc.request().params().contains(PAGE_SIZE_PARAM)) { @@ -683,13 +721,13 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { if (rc.request().params().contains(PAGE_TOKEN_PARAM)) { pageToken = Utils.defaultIfNull(rc.request().params().get(PAGE_TOKEN_PARAM), ""); } - response = jsonRestHandler.listTaskPushNotificationConfigurations(context, extractTenant(rc), taskId, pageSize, pageToken); + response = handler.listTaskPushNotificationConfigurations(context, extractTenant(rc), taskId, pageSize, pageToken); } } catch (NumberFormatException e) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad " + PAGE_SIZE_PARAM)); + response = handler.createErrorResponse(new InvalidParamsError("bad " + PAGE_SIZE_PARAM)); } catch (Throwable t) { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } finally { sendResponse(rc, response); } @@ -706,24 +744,25 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { *
Response: HTTP 204 No Content on success * * @param rc the Vert.x routing context (taskId and configId extracted from path) + * @param handler the handler for the agent this request is addressed to */ @Authenticated - public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { + public void deleteTaskPushNotificationConfiguration(RoutingContext rc, RestHandler handler) { String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); ServerCallContext context = createCallContext(rc, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + response = handler.createErrorResponse(new InvalidParamsError("bad task id")); } else if (configId == null || configId.isEmpty()) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad config id")); + response = handler.createErrorResponse(new InvalidParamsError("bad config id")); } else { - response = jsonRestHandler.deleteTaskPushNotificationConfiguration(context, extractTenant(rc), taskId, configId); + response = handler.deleteTaskPushNotificationConfiguration(context, extractTenant(rc), taskId, configId); } } catch (Throwable t) { LOG.error("Internal error while processing request", t); - response = jsonRestHandler.createErrorResponse(new InternalError("Internal error")); + response = handler.createErrorResponse(new InternalError("Internal error")); } finally { sendResponse(rc, response); } @@ -772,10 +811,10 @@ private String extractTenant(RoutingContext rc) { * @param rc the routing context * @return true if the content type is application/json - false otherwise. */ - private boolean validateContentType(RoutingContext rc) { + private boolean validateContentType(RoutingContext rc, RestHandler handler) { String contentType = rc.request().getHeader(CONTENT_TYPE); if (contentType == null || !contentType.trim().startsWith(APPLICATION_JSON)) { - sendResponse(rc, jsonRestHandler.createErrorResponse(new ContentTypeNotSupportedError(null, null, null))); + sendResponse(rc, handler.createErrorResponse(new ContentTypeNotSupportedError(null, null, null))); return false; } return true; @@ -790,14 +829,14 @@ private boolean validateContentType(RoutingContext rc) { * @param body the request body (may be null or empty) * @return true if validation passes, false if Content-Type error should be returned */ - private boolean validateContentTypeForOptionalBody(RoutingContext rc, @Nullable String body) { + private boolean validateContentTypeForOptionalBody(RoutingContext rc, @Nullable String body, RestHandler handler) { // If body is null or empty, Content-Type is not required if (body == null || body.isBlank()) { return true; } // Body has content - validate Content-Type - return validateContentType(rc); + return validateContentType(rc, handler); } /** @@ -820,10 +859,11 @@ private boolean validateContentTypeForOptionalBody(RoutingContext rc, @Nullable * * * @param rc the Vert.x routing context + * @param handler the handler for the agent this request is addressed to */ @PermitAll - public void getAgentCard(RoutingContext rc) { - HTTPRestResponse response = jsonRestHandler.getAgentCard(); + public void getAgentCard(RoutingContext rc, RestHandler handler) { + HTTPRestResponse response = handler.getAgentCard(); sendResponse(rc, response); } @@ -838,10 +878,11 @@ public void getAgentCard(RoutingContext rc) { *
Authentication: Required (inherits {@code @Authenticated} from class)
*
* @param rc the Vert.x routing context
+ * @param handler the handler for the agent this request is addressed to
*/
@Authenticated
- public void getExtendedAgentCard(RoutingContext rc) {
- HTTPRestResponse response = jsonRestHandler.getExtendedAgentCard(createCallContext(rc, GET_EXTENDED_AGENT_CARD_METHOD), extractTenant(rc));
+ public void getExtendedAgentCard(RoutingContext rc, RestHandler handler) {
+ HTTPRestResponse response = handler.getExtendedAgentCard(createCallContext(rc, GET_EXTENDED_AGENT_CARD_METHOD), extractTenant(rc));
sendResponse(rc, response);
}
@@ -855,9 +896,10 @@ public void getExtendedAgentCard(RoutingContext rc) {
* instead of generic 404 HTML pages.
*
* @param rc the Vert.x routing context
+ * @param handler the handler for the agent this request is addressed to
*/
- public void methodNotFoundMessage(RoutingContext rc) {
- HTTPRestResponse response = jsonRestHandler.createErrorResponse(new MethodNotFoundError());
+ public void methodNotFoundMessage(RoutingContext rc, RestHandler handler) {
+ HTTPRestResponse response = handler.createErrorResponse(new MethodNotFoundError());
sendResponse(rc, response);
}
diff --git a/reference/rest/src/main/java/org/a2aproject/sdk/server/rest/quarkus/registry/MultiAgentRegistry.java b/reference/rest/src/main/java/org/a2aproject/sdk/server/rest/quarkus/registry/MultiAgentRegistry.java
new file mode 100644
index 000000000..78e1962a5
--- /dev/null
+++ b/reference/rest/src/main/java/org/a2aproject/sdk/server/rest/quarkus/registry/MultiAgentRegistry.java
@@ -0,0 +1,16 @@
+package org.a2aproject.sdk.server.rest.quarkus.registry;
+
+import java.util.Map;
+import org.a2aproject.sdk.transport.rest.handler.RestHandler;
+
+/**
+ * Registry for supporting multiple agents in a single Quarkus REST application.
+ * If a CDI bean implements this interface, the server will register routes for each
+ * agent in the registry under {@code /