Module adcp.server.spec_compat
Built-in spec-compatibility hooks for pre-v3 / pre-4.4 buyers.
Wire into serve(pre_validation_hooks=spec_compat_hooks()) to accept the
long tail of legacy clients without writing boilerplate in every adopter repo.
Adopters who want strict-4.4-only rejection of pre-v3/pre-4.4 buyers leave
this off and let the validator reject malformed requests.
The hooks in this module are intentionally pure-Python with no transport or framework dependencies so they compose cleanly with adopter-specific hooks::
from adcp.server import spec_compat_hooks, serve
# Standalone:
serve(handler, pre_validation_hooks=spec_compat_hooks())
# Combined with adopter-specific hooks:
serve(handler, pre_validation_hooks=compose_pre_validation_hooks(
spec_compat_hooks(),
{"create_media_buy": my_custom_hook},
))
# Selective opt-out (skip sync_creatives coercions entirely):
serve(handler, pre_validation_hooks=spec_compat_hooks(exclude={"sync_creatives"}))
# Private creative-format registry:
serve(handler, pre_validation_hooks=spec_compat_hooks(
creative_agent_url="https://creative.example.com"
))
Global variables
var CANONICAL_CREATIVE_AGENT_URL-
Canonical
agent_urlfor the AdCP standard creative-format registry.Injected when wrapping a bare
format_idstring from a pre-4.4 buyer. Override viaspec_compat_hooks(creative_agent_url=...)for private creative-format registries.
Functions
def compose_pre_validation_hooks(*hook_maps: Mapping[str, PreValidationHookChain] | None) ‑> dict[str, tuple[Callable[[str, dict[str, typing.Any]], dict[str, typing.Any]], ...]]-
Expand source code
def compose_pre_validation_hooks( *hook_maps: Mapping[str, PreValidationHookChain] | None, ) -> dict[str, tuple[PreValidationHook, ...]]: """Compose ordered pre-validation hook maps. Later maps append to earlier maps for overlapping tool names. Each tool's hooks run left-to-right, feeding the returned args from one hook into the next. """ composed: dict[str, list[PreValidationHook]] = {} for hook_map in hook_maps: if hook_map is None: continue for tool_name, chain in hook_map.items(): composed.setdefault(tool_name, []).extend(_flatten_pre_validation_hooks(chain)) return {tool_name: tuple(hooks) for tool_name, hooks in composed.items()}Compose ordered pre-validation hook maps.
Later maps append to earlier maps for overlapping tool names. Each tool's hooks run left-to-right, feeding the returned args from one hook into the next.
def spec_compat_hooks(*,
exclude: Collection[str] | None = None,
creative_agent_url: str = 'https://creative.adcontextprotocol.org') ‑> dict[str, collections.abc.Callable[[str, dict[str, typing.Any]], dict[str, typing.Any]] | collections.abc.Sequence[collections.abc.Callable[[str, dict[str, typing.Any]], dict[str, typing.Any]]]]-
Expand source code
def spec_compat_hooks( *, exclude: Collection[str] | None = None, creative_agent_url: str = CANONICAL_CREATIVE_AGENT_URL, ) -> PreValidationHooks: """Return built-in spec-compat hooks for pre-v3 / pre-4.4 buyers. .. deprecated:: 5.2 This heuristic-coercion approach is being replaced by the typed per-tool adapter registry under :mod:`adcp.compat.legacy`. The adapter path validates against the buyer's claimed ``adcp_version`` instead of guessing wire shapes, scales bounded (one module per tool) instead of growing the hook dict unboundedly, and matches the JS SDK's approach. For ``sync_creatives``, the adapter (now shipped) replaces the format_id wrap / asset_type inference / image-demotion logic that this hook used to provide. Buyers claiming ``adcp_major_version=2`` or ``adcp_version="2.5"`` automatically route through it. Calling this function emits a :class:`DeprecationWarning`. Migrate by removing the ``pre_validation_hooks=spec_compat_hooks()`` argument from your ``serve()`` call. Removal target: 6.0. """ import warnings as _warnings _warnings.warn( "spec_compat_hooks() is deprecated and will be removed in 6.0. " "Buyers claiming adcp_version='2.5' or adcp_major_version=2 now " "route through adcp.compat.legacy adapters automatically — drop " "the pre_validation_hooks=spec_compat_hooks() argument from " "serve(). See the adcp.compat.legacy docstring for the migration " "path.", DeprecationWarning, stacklevel=2, ) return _spec_compat_hooks_impl(exclude=exclude, creative_agent_url=creative_agent_url)Return built-in spec-compat hooks for pre-v3 / pre-4.4 buyers.
Deprecated since version: 5.2
This heuristic-coercion approach is being replaced by the typed per-tool adapter registry under :mod:
adcp.compat.legacy. The adapter path validates against the buyer's claimedadcp_versioninstead of guessing wire shapes, scales bounded (one module per tool) instead of growing the hook dict unboundedly, and matches the JS SDK's approach.For
sync_creatives, the adapter (now shipped) replaces the format_id wrap / asset_type inference / image-demotion logic that this hook used to provide. Buyers claimingadcp_major_version=2oradcp_version="2.5"automatically route through it.Calling this function emits a :class:
DeprecationWarning. Migrate by removing thepre_validation_hooks=spec_compat_hooks()argument from yourserve()call. Removal target: 6.0.
Classes
class PreValidationHookError (*, index: int, hook_name: str, message: str)-
Expand source code
class PreValidationHookError(Exception): """A single hook in an ordered pre-validation chain failed. Carries the zero-based ``index`` of the failing hook within its chain and the resolved ``hook_name`` so the dispatcher can surface both in the ``INVALID_REQUEST`` message — naming the exact callable instead of a generic "pre_validation_hook raised ...". """ def __init__(self, *, index: int, hook_name: str, message: str) -> None: self.index = index self.hook_name = hook_name super().__init__(f"pre_validation_hook[{index}] {hook_name} {message}")A single hook in an ordered pre-validation chain failed.
Carries the zero-based
indexof the failing hook within its chain and the resolvedhook_nameso the dispatcher can surface both in theINVALID_REQUESTmessage — naming the exact callable instead of a generic "pre_validation_hook raised …".Ancestors
- builtins.Exception
- builtins.BaseException