feat(server): add spec_compat_hooks() for pre-v3 / pre-4.4 buyer compatibility#648
Merged
Conversation
Ships a built-in `spec_compat_hooks()` factory in `adcp.server` that
returns `dict[str, Callable]` compatible with `serve(pre_validation_hooks=...)`.
Eliminates ~150 LOC of boilerplate that every adopter accepting legacy
buyers currently writes independently.
Four spec-mandated coercions:
- get_products: default buying_mode='brief' when omitted (pre-v3)
- sync_creatives: wrap bare format_id string as {agent_url, id} (pre-4.4)
- sync_creatives: infer asset_type from key (exact match) or field presence
- sync_creatives: demote image→url when width/height absent but url present
Closes #646
https://claude.ai/code/session_01FmPS6TP29PNk9PuedFDTbm
…rl demotion Covers the case where an asset has lone width (no height) — the demotion still fires and strips the stray dim before changing asset_type to 'url'. https://claude.ai/code/session_01FmPS6TP29PNk9PuedFDTbm
…nused params Pre-PR review nits: - Rename `router` to `handler` in module docstring examples (correct param name) - Annotate PreValidationHooks with TypeAlias for IDE hover ergonomics - Add noqa ARG001 to tool_name params that are required by the hook signature but intentionally unused (hooks are registered per-tool in the dict) https://claude.ai/code/session_01FmPS6TP29PNk9PuedFDTbm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #646
Summary
Ships a built-in
spec_compat_hooks()factory inadcp.serverthat eliminates ~150 LOC of boilerplate every Python seller writes independently when accepting pre-v3 / pre-4.4 buyers. Returns adict[str, Callable]compatible withserve(pre_validation_hooks=...).Four spec-mandated hooks included:
get_productsbuying_mode='brief'when absentbuying_modeSHOULD default to'brief'sync_creatives(1)format_idstring as{agent_url, id}format_idfrom string to structured objectsync_creatives(2)asset_typefrom exact key match or field presenceasset_typediscriminatorsync_creatives(3)asset_type='image'→'url'whenwidth/heightabsent buturlpresentDesign decisions (from expert review):
asset_typeinference uses exact key match only —"hero_image"is an asset ID, not a type hint; substring matches are intentionally excludedurlis present; structurally invalid assets (nourleither) are left unchanged for schema validation to reportsync_creativeshooks 2+3+4 are always bundled under one key (one callable per tool name) — adopters needing granular control copy logic fromadcp.server.spec_compatAlso exports:
PreValidationHooks: TypeAlias— for adopter type annotationsCANONICAL_CREATIVE_AGENT_URL— for test fixtures / private-registry overridesWhat was tested
pytest tests/test_spec_compat_hooks.py -v→ 35 passed (all four hooks, exact-key vs substring inference, partial-dims strip, image→url safety, factory composition, integration throughcreate_tool_caller)pytest tests/ -q --ignore=tests/integration -k "not test_real_tls"→ 4350 passed, 0 new failuresruff check src/adcp/server/spec_compat.py→ all checks passedspec_compat.pyor__init__.pyNits surfaced by pre-PR review (not fixed — left for reviewer discretion):
serve.py/ServeConfiguse a looserCallable[..., Any]annotation thanPreValidationHooks; aligning all three is a follow-up__all__ordering:# Spec compatibilitysection currently follows# Test controller; could move to after# MCP integrationfor discoverabilityexcludewith unknown names is silently ignored; awarnings.warncould help with typosPre-PR review
router→handlerin docstring,TypeAliasannotation added)Session: https://claude.ai/code/session_01FmPS6TP29PNk9PuedFDTbm
Generated by Claude Code