fix(schemas): url-asset url.format uri → uri-template for macro support#2801
Merged
Conversation
The `sales-social/catalog_driven_dynamic_ads/sync_dpa_creative` fixture's
tracker URLs used raw `{SKU}` / `{GTIN}` / `{MEDIA_BUY_ID}` macros. The
step's own narrative already documents the canonical form:
"values substituted into URL contexts MUST be percent-encoded such that
only RFC 3986 unreserved characters remain unescaped" (per #2620)
Encoding `{` → `%7B` / `}` → `%7D` satisfies `format: uri` AND matches what
a conformant platform actually receives over the wire at impression time
(the platform decodes then substitutes). Raw braces were failing
`format: uri` on every branch of `creative-manifest.assets`' anyOf,
cascading to 60 spurious lint errors. The fix is one character-for-character
substitution.
Allowlist shrinks 13 → 12. The remaining 12 entries are all blocked on
upstream WG issues #2774-#2776.
…pport Pivots PR #2801 from the wrong direction (percent-encode the fixture) to the right direction (relax the schema to accept AdCP macros at sync time). docs/creative/universal-macros.mdx:575-585 explicitly says buyers MUST submit raw `{SKU}` / `{DEVICE_ID}` / `{MEDIA_BUY_ID}` templates at sync time; the ad server URL-encodes substituted values at impression time. Strict `format: uri` rejected those templates, contradicting the spec. `format: uri-template` (RFC 6570) accepts both plain URIs and level-1 macro templates, which is exactly what AdCP universal macros produce. Description on the `url` property updated to document the sync-time-raw / impression-time-encoded split. Fixture in `sales-social/catalog_driven_dynamic_ads/sync_dpa_creative` reverts to raw braces (the spec-canonical form) from the wrong percent-encoded fix. Allowlist stays at 12 entries (same outcome via the correct path). Protocol change — changeset is a `patch` on `adcontextprotocol`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…K note Protocol-expert review of PR #2801 flagged two non-blocking improvements: 1. universal-macros.mdx needed an explicit note that AdCP uses RFC 6570 Level 1 only — without that, an overeager implementer could ship `{+SKU}` or `{?SKU}` and still validate as a URI template even though AdCP's literal-substitution contract doesn't support operators. 2. The changeset should call out the SDK-side implication: any buyer-side SDK that defensively percent-encodes `{`/`}` at sync time should stop. Both added here. No schema or fixture changes this commit — just docs scoping and migration-note prose.
Merged
3 tasks
bokelley
added a commit
that referenced
this pull request
Apr 22, 2026
…l-asset templates) Folds origin/main into the release-docs branch. Adds: - Item 18: asset_type discriminator (#2776), refine[] prefixed ids + optional action (#2775), SI context→intent rename (#2774), governance plan scoping contract (#2777). - Item 19: url-asset url.format relaxed from uri to uri-template (#2801) so AdCP macros like {SKU}/{DEVICE_ID}/{MEDIA_BUY_ID} validate at sync time. - Breaking-changes table: asset_type const, refine[] product_id/proposal_id, SI context→intent, url-asset uri-template. - Migration notes: asset_type on payloads, refine rename, SI field rename, url-asset no-op guidance. Local compliance-assertion modules superseded by bundled @adcp/client 5.9 defaults (#2769) — accepted the upstream delete on merge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Pivots PR #2801 based on protocol-expert review. The fixture was right; the schema was wrong. This change relaxes
core/assets/url-asset.jsonto accept RFC 6570 URI templates, which is exactly what AdCP's universal macros produce at sync time.Background
The original PR encoded
{SKU}→%7BSKU%7Din thesales-social/sync_dpa_creativefixture to satisfyformat: urionurl-asset.json. Expert review caught that this contradicted the spec: `docs/creative/universal-macros.mdx:575-585` explicitly says:The percent-encoded form is never supposed to be on the wire — at sync time it's a non-conformant template; at impression time the braces are gone after substitution. The 60 lint errors were the signal, not the noise.
Fix
url-asset.jsonurl.formatchangeduri→uri-template(RFC 6570).uri-templateaccepts both plain URIs and level-1 macro templates ({var}), which is exactly what AdCP universal macros produce.Other asset schemas with `format: uri` (image-asset, video-asset, audio-asset, vast-asset, daast-asset, webhook-asset) are left as strict `uri` — those fields are for real asset delivery URLs that shouldn't carry macros. Only url-asset is for tracker/click URLs that conventionally do.
Ratchet
Allowlist unchanged at 12. Same outcome as the previous commit, now via the correct path. The remaining 12 entries are all blocked on upstream WG issues #2774-#2776.
Changeset
Marked as
patchonadcontextprotocolsince this is a published protocol schema change. The change is backward-compatible at the wire level:uri-templateis a superset ofuriin this context (plain URIs still validate; templated URIs now also validate).Test plan
npm run test:storyboard-sample-request-schema— passes (12 grandfathered, 0 new drift, 0 stale)npm run test:schemas— 7/7 passnpm run test:examples— 31/31 passnpm run test:unit— 631 passednpm run typecheck— clean🤖 Generated with Claude Code