diff --git a/pom.xml b/pom.xml
index a0ebc391f..5045215b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -632,6 +632,9 @@
tests/multitenancy
+
+ tests/multitenancy/jsonrpc
+
tests/multiversion/jsonrpc
tests/multiversion/rest
diff --git a/reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java
index 4ec4905ad..c18e8dc4d 100644
--- a/reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java
+++ b/reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java
@@ -440,7 +440,7 @@ public String getAgentCard(RoutingContext rc) throws JsonProcessingException {
* @throws JsonProcessingException if serialization fails
*/
public String getTenantAgentCard(RoutingContext rc) throws JsonProcessingException {
- String tenant = extractTenant(rc);
+ String tenant = rc.pathParam("tenant");
cacheMetadata.getHttpHeadersMap().forEach((k, v) -> rc.response().putHeader(k, v));
return JsonUtil.toJson(jsonRpcHandler.getAgentCard(tenant));
}
diff --git a/tests/multitenancy/jsonrpc/pom.xml b/tests/multitenancy/jsonrpc/pom.xml
new file mode 100644
index 000000000..86acbf835
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/pom.xml
@@ -0,0 +1,69 @@
+
+
+ 4.0.0
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-parent
+ 1.3.1.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ a2a-java-sdk-tests-multitenancy-jsonrpc
+
+ jar
+
+ Java A2A SDK Tests Multitenancy JSON-RPC
+ Integration tests for multi-tenant public agent card on a JSON-RPC-only server (no REST transport on classpath)
+
+
+ true
+
+
+
+
+ ${project.groupId}
+ a2a-java-sdk-reference-jsonrpc
+ test
+
+
+ ${project.groupId}
+ a2a-java-extras-multitenancy
+ test
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+ io.quarkus
+ quarkus-security
+ test
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+ true
+
+
+
+
diff --git a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentCardProducer.java b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentCardProducer.java
new file mode 100644
index 000000000..172458dcd
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentCardProducer.java
@@ -0,0 +1,75 @@
+package org.a2aproject.sdk.tests.multitenancy.jsonrpc;
+
+import java.util.List;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Singleton;
+
+import org.a2aproject.sdk.extras.multitenancy.Tenant;
+import org.a2aproject.sdk.server.ExtendedAgentCard;
+import org.a2aproject.sdk.server.PublicAgentCard;
+import org.a2aproject.sdk.spec.AgentCapabilities;
+import org.a2aproject.sdk.spec.AgentCard;
+import org.a2aproject.sdk.spec.AgentInterface;
+import org.a2aproject.sdk.spec.TransportProtocol;
+
+@Singleton
+public class AgentCardProducer {
+
+ private static final String BASE_URL = "http://localhost:8081";
+
+ @Produces
+ @PublicAgentCard
+ public AgentCard publicCard() {
+ return card("Default Agent");
+ }
+
+ @Produces
+ @ExtendedAgentCard
+ public AgentCard defaultExtendedCard() {
+ return card("Default Agent (extended)");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("acme")
+ public AgentCard acmePublicCard() {
+ return card("Acme Agent");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("acme")
+ @ExtendedAgentCard
+ public AgentCard acmeExtendedCard() {
+ return card("Acme Agent (extended)");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("beta")
+ public AgentCard betaPublicCard() {
+ return card("Beta Agent");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("beta")
+ @ExtendedAgentCard
+ public AgentCard betaExtendedCard() {
+ return card("Beta Agent (extended)");
+ }
+
+ private static AgentCard card(String name) {
+ return AgentCard.builder()
+ .name(name)
+ .description(name)
+ .version("1.0.0")
+ .defaultInputModes(List.of("text"))
+ .defaultOutputModes(List.of("text"))
+ .capabilities(AgentCapabilities.builder().streaming(true).extendedAgentCard(true).build())
+ .skills(List.of())
+ .supportedInterfaces(List.of(new AgentInterface(TransportProtocol.JSONRPC.asString(), BASE_URL)))
+ .build();
+ }
+}
diff --git a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentExecutorProducer.java b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentExecutorProducer.java
new file mode 100644
index 000000000..566b5a865
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/AgentExecutorProducer.java
@@ -0,0 +1,22 @@
+package org.a2aproject.sdk.tests.multitenancy.jsonrpc;
+
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
+
+import org.a2aproject.sdk.server.agentexecution.AgentExecutor;
+import org.a2aproject.sdk.spec.TextPart;
+
+@ApplicationScoped
+public class AgentExecutorProducer {
+
+ @Produces
+ public AgentExecutor defaultExecutor() {
+ return (context, emitter) -> {
+ emitter.startWork();
+ emitter.addArtifact(List.of(new TextPart("default")));
+ emitter.complete();
+ };
+ }
+}
diff --git a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java
new file mode 100644
index 000000000..c61009b70
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java
@@ -0,0 +1,53 @@
+package org.a2aproject.sdk.tests.multitenancy.jsonrpc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.path.json.JsonPath;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Verifies that the JSON-RPC transport serves the public agent card endpoints
+ * (/.well-known/agent-card.json and /.well-known/{tenant}/agent-card.json)
+ * when the REST transport is NOT on the classpath.
+ */
+@QuarkusTest
+public class MultiTenantJSONRPCPublicCardTest {
+
+ @Test
+ public void defaultPublicCardIsServed() {
+ String response = RestAssured.given()
+ .when().get("/.well-known/agent-card.json")
+ .then().statusCode(200)
+ .extract().asString();
+ assertEquals("Default Agent", JsonPath.from(response).getString("name"));
+ }
+
+ @Test
+ public void acmeTenantPublicCardIsServed() {
+ String response = RestAssured.given()
+ .when().get("/.well-known/acme/agent-card.json")
+ .then().statusCode(200)
+ .extract().asString();
+ assertEquals("Acme Agent", JsonPath.from(response).getString("name"));
+ }
+
+ @Test
+ public void betaTenantPublicCardIsServed() {
+ String response = RestAssured.given()
+ .when().get("/.well-known/beta/agent-card.json")
+ .then().statusCode(200)
+ .extract().asString();
+ assertEquals("Beta Agent", JsonPath.from(response).getString("name"));
+ }
+
+ @Test
+ public void unknownTenantPublicCardFallsBackToDefault() {
+ String response = RestAssured.given()
+ .when().get("/.well-known/unknown-corp/agent-card.json")
+ .then().statusCode(200)
+ .extract().asString();
+ assertEquals("Default Agent", JsonPath.from(response).getString("name"));
+ }
+}
diff --git a/tests/multitenancy/jsonrpc/src/test/resources/META-INF/beans.xml b/tests/multitenancy/jsonrpc/src/test/resources/META-INF/beans.xml
new file mode 100644
index 000000000..b0e6ff3e2
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/tests/multitenancy/jsonrpc/src/test/resources/application.properties b/tests/multitenancy/jsonrpc/src/test/resources/application.properties
new file mode 100644
index 000000000..44cbfd7a4
--- /dev/null
+++ b/tests/multitenancy/jsonrpc/src/test/resources/application.properties
@@ -0,0 +1 @@
+a2a.authorization.required=false