You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parent: #662
MCP sibling shipped in #666 (SellerTestClient.invoke()).
Why
SellerTestClient eliminates JSON-RPC envelope + adcp_error extraction boilerplate for MCP adopters. A2A adopters need the same harness shape — but A2A's semantics are different enough that a shared invoke() would force-fit both.
What's different about A2A
A2A is task-state-machine, not call/return:
task.create(method, payload) → returns a task handle in submitted / working state
State transitions through TaskState.TASK_STATE_* (see src/adcp/server/a2a_server.py:467,482,547)
Terminal states: COMPLETED / FAILED / CANCELED
Async completion is delivered via push-notification webhooks the seller signs
A SellerTestClient.invoke() shape collapses all of that into a single call, hiding exactly the lifecycle adopters need to test.
An in-process push-notification sink (captures outbound webhooks instead of HTTP-POSTing them) so adopters can assert on signing, body shape, and ordering
Why this matters for the broader A2A adoption story
A2A adoption is currently deferred behind "pluggable TaskStore + push-notification + middleware hooks." An in-process test harness gives adopters something concrete to write against once those hooks land — and exercises the hooks themselves, which makes the harness a useful design forcing-function.
Scope notes
Nottransport=\"a2a\" on SellerTestClient. The lifecycle is different enough that one method surface forces a bad shape onto both.
Reuse ToolInvokeResult / AdcpErrorPayload for terminal-state results so the assertion grammar stays consistent.
Parent: #662
MCP sibling shipped in #666 (
SellerTestClient.invoke()).Why
SellerTestClienteliminates JSON-RPC envelope +adcp_errorextraction boilerplate for MCP adopters. A2A adopters need the same harness shape — but A2A's semantics are different enough that a sharedinvoke()would force-fit both.What's different about A2A
A2A is task-state-machine, not call/return:
task.create(method, payload)→ returns a task handle insubmitted/workingstateTaskState.TASK_STATE_*(seesrc/adcp/server/a2a_server.py:467,482,547)COMPLETED/FAILED/CANCELEDA
SellerTestClient.invoke()shape collapses all of that into a single call, hiding exactly the lifecycle adopters need to test.Proposed shape (not final — open to triage)
Surfaces the harness likely needs:
create_task(method, payload) → TaskHandleTaskHandle.wait_for(state=..., timeout=...) → ToolInvokeResultTaskHandle.cancel()Why this matters for the broader A2A adoption story
A2A adoption is currently deferred behind "pluggable TaskStore + push-notification + middleware hooks." An in-process test harness gives adopters something concrete to write against once those hooks land — and exercises the hooks themselves, which makes the harness a useful design forcing-function.
Scope notes
transport=\"a2a\"onSellerTestClient. The lifecycle is different enough that one method surface forces a bad shape onto both.ToolInvokeResult/AdcpErrorPayloadfor terminal-state results so the assertion grammar stays consistent.References
SellerTestClient.invoke())src/adcp/server/a2a_server.py:888(create_a2a_server)TaskState.TASK_STATE_*callsites ina2a_server.py