Skip to content

[Bug]: JSON-RPC server documents tenant-prefixed routing but registers no route that can match it #1034

Description

@omatheusmesmo

What happened?

Context: I found this while evaluating what the SDK can support, for an A2A server extension I am building for Quarkus LangChain4j. It started from a stalled PR by another contributor, who is not involved in this work. The goal is to land the extension in quarkus-langchain4j proper.

The JSON-RPC reference server documents tenant-prefixed routing and implements the extraction for it, but never registers a route that can match a tenant-prefixed path, so the documented behaviour is unreachable.

reference/jsonrpc/.../quarkus/A2AServerRoutes says in its class javadoc:

Tenant identification is extracted from the request path:

  • POST / → empty tenant
  • POST /tenant1 → tenant "tenant1"
  • POST /tenant1/ → tenant "tenant1" (trailing slash stripped)

and extractTenant(RoutingContext) implements exactly that. But setupRoutes registers only:

router.post("/")
router.get("/.well-known/agent-card.json")

In Vert.x a path that does not end in * is matched exactly, so POST /tenant1 never reaches the handler and extractTenant can only ever return the empty tenant.

The REST reference implementation does this correctly, with regex routes:

router.postWithRegex("^\\/(?<tenant>[^\\/]*\\/?)message:send$")

Reproduced empirically

A Quarkus application with a2a-java-sdk-reference-jsonrpc on the classpath, posting the same JSON-RPC body to each documented path:

Request Observed Documented
POST / 200 empty tenant, OK
POST /tenant1 405 Method Not Allowed tenant "tenant1"
POST /tenant1/ 405 Method Not Allowed tenant "tenant1"
GET /tenant1/.well-known/agent-card.json 404 n/a

The 405 rather than 404 comes from another GET-only route matching the path; no POST route matches a tenant-prefixed path at all.

Why the tests do not catch it

A2AServerRoutesTest verifies tenant extraction by mocking the routing context:

when(mockRoutingContext.normalizedPath()).thenReturn("/tenant1");

That exercises the extraction in isolation but never goes through the router, so the missing route is invisible. No JSON-RPC test issues a real HTTP request to a tenant-prefixed path.

Impact

Per Multi-Tenancy and Multi-Agent Routing, the tenant field is one of the three sanctioned routing mechanisms. Today it works over REST but not over JSON-RPC, even though the JSON-RPC implementation documents it as supported, so an agent's routing behaviour depends on which transport a client happens to pick.

Suggested fix

Register the JSON-RPC endpoint with a tenant-capable route, mirroring what REST already does, and add a test that performs a real HTTP request to a tenant-prefixed path.

Happy to send a PR if you agree with the direction.

Verified against 1.2.0.Final

The javadoc, extractTenant and the two exact-match route registrations are all unchanged from 1.1.0.Final, where the table above was measured.

Relevant log output

$ curl -s -o /dev/null -w '%{http_code}\n' -X POST http://localhost:8080/ \
    -H 'Content-Type: application/json' -H 'A2A-Version: 1.0' -d '{...}'
200

$ curl -s -o /dev/null -w '%{http_code}\n' -X POST http://localhost:8080/tenant1 \
    -H 'Content-Type: application/json' -H 'A2A-Version: 1.0' -d '{...}'
405

$ curl -s -o /dev/null -w '%{http_code}\n' -X POST http://localhost:8080/tenant1/ \
    -H 'Content-Type: application/json' -H 'A2A-Version: 1.0' -d '{...}'
405

Environment

  • a2a-java 1.2.0.Final (measured on 1.1.0.Final; route registration unchanged)
  • Quarkus 3.33.2

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions