Skip to content

fix: make server dispatch automatically adapt responses by requested AdCP version #868

@bokelley

Description

@bokelley

Summary

After #855, the Python SDK exposes resolve_requested_adcp_version(), which is useful, but seller applications still have to wire the versioning contract manually inside tool handlers.

That leaves generic protocol behavior in every adopter:

  • Resolve the buyer's requested AdCP release from request payload fields.
  • Default omitted/legacy buyers to the correct 3.0 compatibility behavior.
  • Convert unsupported versions into a protocol error.
  • Project media-buy responses into the requested release shape.
  • Keep MCP and A2A behavior consistent.

This should be automatic in the SDK/server dispatch layer. Seller handlers should be able to return the canonical current response model, and the server should serialize the correct wire shape for the buyer.

Current adopter-side code

Prebid Sales Agent currently has to carry local code equivalent to:

SUPPORTED_ADCP_VERSIONS = ("3.0", "3.1-beta.3")

requested_adcp_version = resolve_requested_adcp_version(
    request_payload,
    supported=SUPPORTED_ADCP_VERSIONS,
)

if requested_adcp_version == "3.0" and response.status == "completed":
    response.status = response.media_buy_status
    del response.media_buy_status

The real code is more defensive, but the important point is that it is protocol negotiation and response projection, not seller business logic.

Expected

The SDK/server layer should own version negotiation and response serialization:

  • Resolve the requested version once during dispatch.
  • Reject unsupported versions before invoking the platform handler, or expose a standard exception mapping.
  • Expose the resolved version on RequestContext for legitimate business-logic cases.
  • Normalize versioned response shapes after the handler returns, for both MCP and A2A.
  • Keep this behavior aligned with advertised supported_versions.

Then a seller can implement create_media_buy/update_media_buy once and return the current canonical model:

{
  "status": "completed",
  "media_buy_status": "pending_creatives"
}

The SDK would emit legacy 3.0 wire shape for 3.0 buyers:

{
  "status": "pending_creatives"
}

and 3.1 shape for explicit 3.1 buyers:

{
  "status": "completed",
  "media_buy_status": "pending_creatives"
}

Why this matters

If this stays in seller code, every adopter has to rediscover the same edge cases and keep MCP/A2A behavior aligned by hand. That is exactly the kind of SDK workaround that will fragment implementations.

Related follow-ups from the same integration pass:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions