Skip to content

fix(server): accept trailing-slash JSON-RPC endpoint; enqueue Task in TCK SUT - #1165

Open
kuangmi-bit wants to merge 3 commits into
a2aproject:mainfrom
kuangmi-bit:fix/jsonrpc-trailing-slash
Open

fix(server): accept trailing-slash JSON-RPC endpoint; enqueue Task in TCK SUT#1165
kuangmi-bit wants to merge 3 commits into
a2aproject:mainfrom
kuangmi-bit:fix/jsonrpc-trailing-slash

Conversation

@kuangmi-bit

Copy link
Copy Markdown

Summary

Two changes that let the A2A 1.0 TCK exercise the JSON-RPC SUT (part of the work tracked in #666):

  1. create_jsonrpc_routes accepts the trailing-slash endpoint variant. HTTP clients (httpx in particular) normalize an empty request path to a trailing slash, so POST /a2a/jsonrpc/ previously returned 404 even though /a2a/jsonrpc worked. The spec does not mandate one spelling over the other, and a 404 on the trailing-slash form breaks any spec-conformant client that does not strip it. Both variants now route to the same dispatcher.

  2. tck/sut_agent.py enqueues the Task itself before emitting TaskStatusUpdateEvents (via new_task_from_user_message). The SDK's active-task machinery requires this ordering (it raises InvalidAgentResponseError otherwise), and the 1.0 TCK CORE-SEND-* requirements assert it.

Verification

Ran the 1.0 TCK (a2a-tck tag 1.0.0.alpha2, jsonrpc transport, must level) against the SUT locally:

before: 53 failed, 16 passed
after:   6 failed, 67 passed

The remaining 6 failures are not transport issues — they are SUT feature gaps (artifact-carrying responses, DM-ART-001) and one SDK error-code mapping gap (ContentTypeNotSupportedError reported as ParseError). Those are follow-ups; this PR fixes the transport-level blockers.

Unit tests: tests/server/routes/ 96 passed. Ruff clean on touched files.

Related

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

No coverage changes.

Generated by coverage-comment.yml

@kuangmi-bit

Copy link
Copy Markdown
Author

Gentle ping on review. This is a small, TCK-unblocking server-side change (part of #666): create_jsonrpc_routes now accepts the trailing-slash endpoint variant — httpx normalizes an empty request path to a trailing slash, so POST /a2a/jsonrpc/ currently 404s — and enqueues the Task rather than failing the request. Coverage report attached, all checks green. Happy to address any review feedback.

… TCK SUT

Two changes that together let the 1.0 TCK exercise the JSON-RPC SUT:

1. create_jsonrpc_routes now registers both the exact rpc_url and its
   trailing-slash variant. HTTP clients (httpx in particular) normalize
   an empty request path to a trailing slash, so POST /a2a/jsonrpc/ was
   previously 404 even though /a2a/jsonrpc worked. This is a protocol
   compatibility fix: the spec does not mandate one spelling over the
   other, and a 404 on the trailing-slash form breaks any client that
   does not strip it.

2. tck/sut_agent.py now enqueues the Task itself (via
   new_task_from_user_message) before emitting TaskStatusUpdateEvents.
   The SDK's active-task machinery requires this ordering (InvalidAgentResponseError
   otherwise), and the 1.0 TCK CORE-SEND tests assert it.

Verified against a2a-tck 1.0.0.alpha2 (jsonrpc, must level):
53 failed -> 6 failed before this change, with the remaining failures
being SUT feature gaps (artifacts) and one SDK error-code mapping gap,
not transport issues.
…card

Two more 1.0 compatibility fixes surfaced by running the REST and gRPC
rows of the TCK:

- protocolBinding 'REST' -> 'HTTP+JSON': the 1.0 TCK's protocol binding
  map only recognizes JSONRPC / GRPC / HTTP+JSON. The old name made the
  whole REST transport untestable ("No usable transports after filtering").
- gRPC interface url 'http://localhost:50051' -> 'localhost:50051': the
  gRPC client treats the url as a channel target; the http:// prefix
  fails DNS resolution in grpcio.

Verified against a2a-tck 1.0.0.alpha2 (must level):
- jsonrpc: 6 failed / 67 passed
- http_json (REST): 5 failed / 61 passed
- grpc: 7 failed / 48 passed
Remaining failures are SUT feature gaps (artifact-carrying responses,
MessageResponse variants) plus two status/error-code mappings.
@kuangmi-bit
kuangmi-bit force-pushed the fix/jsonrpc-trailing-slash branch from f49d8e4 to 860bd65 Compare August 17, 2026 21:45

@mykytanetipa mykytanetipa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retracting my earlier approval - it was submitted in error while I was reviewing itk PRs in parallel. Switching to request-changes; see the points below.

Comment on lines +67 to +72
),
Route(
path=f'{rpc_url}/',
endpoint=dispatcher.handle_requests,
methods=['POST'],
),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

major: adding a duplicated route is a behavioral change for existing clients that rely on public create_jsonrpc_routes, I would avoid this.

the route mismatch issue should be fixed from the clients side e.g. by using follow_redirects=True on the httpx.Client.

)
),
Route(
path=f'{rpc_url}/',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will introduce malformed unreachable path in case of rpc_url='/'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants