Skip to content

Schema: adcp.idempotency.replay_ttl_seconds required-when-present creates an ergonomic trap #2429

Description

@bokelley

Summary

schemas/protocol/get-adcp-capabilities-response.json makes adcp.idempotency.replay_ttl_seconds REQUIRED when the idempotency block is present, but leaves the block itself optional. This creates a trap: SDK response builders (and every canonical "minimal" example in the docs) emit capabilities without the block at all, which is schema-valid but also silently means "I have said nothing about idempotency."

Sellers that do support idempotency have to know to include both the block and the TTL. Sellers that don't can skip the whole thing — but then there's no positive signal to buyers that this seller has no replay protection. The "required-when-present" shape ends up as the worst of both worlds.

Evidence

  • schemas/protocol/get-adcp-capabilities-response.json ~lines 21-40 — adcp.idempotency.replay_ttl_seconds required when idempotency object is present.
  • AdCP Python SDK v4.0.0-rc: capabilities_response(["media_buy"]) emits {"adcp": {"major_versions": [3]}, "supported_protocols": [...]} with no idempotency block. Only capabilities_response(["media_buy"], idempotency=store.capability()) emits the block. 4 of 5 reference skills don't call .capability() in their minimal example.

Question

Should AdCP treat idempotency as a positive declaration (required on the top-level response, with replay_ttl_seconds: 0 meaning "not supported"), or keep it optional?

Resolution options

  1. Require adcp.idempotency at the top level of the capabilities response; replay_ttl_seconds: 0 explicitly means "replay dedup not supported; callers should not retry with the same idempotency_key".
  2. Keep optional, but clarify in the docs that absence means "undefined behavior on replays" and require downstream SDKs to warn when a seller omits it.
  3. Mirror the existing request_signing.supported: bool pattern — add idempotency.supported: bool, decouple from replay_ttl_seconds.

Option (3) feels most consistent with how request_signing is already modeled.

Surfaced during pre-4.0 DX validation: adcontextprotocol/adcp-client-python#205

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