Skip to content

design: AdCP tasks/get tool vs A2A native tasks/get JSON-RPC method — collision and lifecycle semantics #997

@bokelley

Description

@bokelley

Context

Discovered while reviewing PR #968 (closed). The SDK currently routes tasks/get through ProtocolClient.callTool for both MCP and A2A (src/lib/core/TaskExecutor.ts:1293). For A2A, this dispatches as message/send { skill: 'tasks/get' }.

Tension

AdCP 3.0 defines tasks/get as a buyer-callable tool with bundled request/response schemas at schemas/cache/<v>/bundled/core/tasks-get-{request,response}.json. The tool tracks AdCP work lifecycle (submitted/working/completed/failed/etc on the actual buyer task), which is what buyers need.

A2A 0.3.0 defines tasks/get as a native JSON-RPC method (alongside message/send, tasks/cancel, etc). On the wire, native A2A tasks/get polls TRANSPORT-call lifecycle — for AdCP submitted-arm sellers, Task.state is always 'completed' because the message was successfully accepted; the work lifecycle lives in the artifact.

These are different surfaces tracking different lifecycles. The names collide.

Failure modes

  1. Seller built on @a2a-js/sdk's DefaultRequestHandler: the SDK routes JSON-RPC method names directly. A buyer sending message/send { skill: 'tasks/get' } may surface as a method-not-found from the seller's tool registry, even though the AdCP-tool implementation exists in the seller's handler chain.

  2. Buyer using A2A native tasks/get: gets transport-state, not AdCP work-state. Submitted arms always look "completed" — wrong polling lifecycle, breaks SubmittedContinuation.waitForCompletion().

Possible resolutions

  • (A) Sellers register the same handler under both surfaces (AdCP tool + A2A native method). The SDK could probe via tools/list on first call and prefer the AdCP-tool path when present.
  • (B) AdCP spec asserts that A2A sellers MUST route the native tasks/get JSON-RPC to the AdCP work-lifecycle handler (i.e., make the native method an AdCP-defined surface). Rename to disambiguate downstream.
  • (C) The SDK probes capability and uses native A2A tasks/get only when the seller advertises it as a polling method (e.g., capability flag), defaulting to the AdCP-tool path.
  • (D) Status quo: tool-only path on ProtocolClient.callTool, document that A2A sellers MUST register tasks/get as a buyer-callable tool over message/send.

Prior art

PR #968 (closed) implemented (C)-ish but only the native-A2A side, without the capability probe. The expert reviews flagged the rationale was sound but the path bypassed AdCP's work-lifecycle semantics.

Action

  1. Discuss with adcontextprotocol/adcp maintainers — this is upstream design.
  2. Once resolved, update getTaskStatus to match the chosen path.
  3. Defensive fix worth applying independently of the design question: cherry-pick the typeof-guard from PR fix(protocols): use native A2A tasks/get JSON-RPC for polling (#963) #968's ca513b27 to mapTasksGetResponseToTaskInfo so non-string status values don't silently mis-compare against ADCP_STATUS.*.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions