@adcp/sdk 6.7.0 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/bin/adcp.js +15 -3
  2. package/dist/lib/adapters/derived-account-store.d.ts +152 -0
  3. package/dist/lib/adapters/derived-account-store.d.ts.map +1 -0
  4. package/dist/lib/adapters/derived-account-store.js +135 -0
  5. package/dist/lib/adapters/derived-account-store.js.map +1 -0
  6. package/dist/lib/adapters/implicit-account-store.d.ts +3 -1
  7. package/dist/lib/adapters/implicit-account-store.d.ts.map +1 -1
  8. package/dist/lib/adapters/implicit-account-store.js +3 -1
  9. package/dist/lib/adapters/implicit-account-store.js.map +1 -1
  10. package/dist/lib/adapters/index.d.ts +1 -0
  11. package/dist/lib/adapters/index.d.ts.map +1 -1
  12. package/dist/lib/adapters/index.js +7 -1
  13. package/dist/lib/adapters/index.js.map +1 -1
  14. package/dist/lib/adapters/oauth-passthrough-resolver.d.ts +3 -1
  15. package/dist/lib/adapters/oauth-passthrough-resolver.d.ts.map +1 -1
  16. package/dist/lib/adapters/oauth-passthrough-resolver.js +3 -1
  17. package/dist/lib/adapters/oauth-passthrough-resolver.js.map +1 -1
  18. package/dist/lib/adapters/roster-account-store.d.ts +85 -24
  19. package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
  20. package/dist/lib/adapters/roster-account-store.js +52 -30
  21. package/dist/lib/adapters/roster-account-store.js.map +1 -1
  22. package/dist/lib/index.d.ts +3 -3
  23. package/dist/lib/index.d.ts.map +1 -1
  24. package/dist/lib/index.js +13 -8
  25. package/dist/lib/index.js.map +1 -1
  26. package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts +81 -0
  27. package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts.map +1 -0
  28. package/dist/lib/mock-server/creative-ad-server/seed-data.js +200 -0
  29. package/dist/lib/mock-server/creative-ad-server/seed-data.js.map +1 -0
  30. package/dist/lib/mock-server/creative-ad-server/server.d.ts +39 -0
  31. package/dist/lib/mock-server/creative-ad-server/server.d.ts.map +1 -0
  32. package/dist/lib/mock-server/creative-ad-server/server.js +618 -0
  33. package/dist/lib/mock-server/creative-ad-server/server.js.map +1 -0
  34. package/dist/lib/mock-server/index.d.ts.map +1 -1
  35. package/dist/lib/mock-server/index.js +180 -24
  36. package/dist/lib/mock-server/index.js.map +1 -1
  37. package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts +66 -0
  38. package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts.map +1 -0
  39. package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js +193 -0
  40. package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js.map +1 -0
  41. package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts +33 -0
  42. package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts.map +1 -0
  43. package/dist/lib/mock-server/sales-non-guaranteed/server.js +782 -0
  44. package/dist/lib/mock-server/sales-non-guaranteed/server.js.map +1 -0
  45. package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts +50 -0
  46. package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts.map +1 -0
  47. package/dist/lib/mock-server/sponsored-intelligence/seed-data.js +133 -0
  48. package/dist/lib/mock-server/sponsored-intelligence/seed-data.js.map +1 -0
  49. package/dist/lib/mock-server/sponsored-intelligence/server.d.ts +13 -0
  50. package/dist/lib/mock-server/sponsored-intelligence/server.d.ts.map +1 -0
  51. package/dist/lib/mock-server/sponsored-intelligence/server.js +609 -0
  52. package/dist/lib/mock-server/sponsored-intelligence/server.js.map +1 -0
  53. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  54. package/dist/lib/protocols/mcp.js +1 -41
  55. package/dist/lib/protocols/mcp.js.map +1 -1
  56. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  57. package/dist/lib/server/account-mode.d.ts +113 -0
  58. package/dist/lib/server/account-mode.d.ts.map +1 -0
  59. package/dist/lib/server/account-mode.js +125 -0
  60. package/dist/lib/server/account-mode.js.map +1 -0
  61. package/dist/lib/server/adcp-server.js +41 -0
  62. package/dist/lib/server/adcp-server.js.map +1 -1
  63. package/dist/lib/server/auth.d.ts +35 -0
  64. package/dist/lib/server/auth.d.ts.map +1 -1
  65. package/dist/lib/server/auth.js.map +1 -1
  66. package/dist/lib/server/create-adcp-server.d.ts +26 -9
  67. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  68. package/dist/lib/server/create-adcp-server.js +46 -20
  69. package/dist/lib/server/create-adcp-server.js.map +1 -1
  70. package/dist/lib/server/ctx-metadata/store.d.ts +1 -1
  71. package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -1
  72. package/dist/lib/server/ctx-metadata/store.js +1 -0
  73. package/dist/lib/server/ctx-metadata/store.js.map +1 -1
  74. package/dist/lib/server/decisioning/account.d.ts +5 -0
  75. package/dist/lib/server/decisioning/account.d.ts.map +1 -1
  76. package/dist/lib/server/decisioning/account.js.map +1 -1
  77. package/dist/lib/server/decisioning/buyer-agent.d.ts +37 -4
  78. package/dist/lib/server/decisioning/buyer-agent.d.ts.map +1 -1
  79. package/dist/lib/server/decisioning/buyer-agent.js +12 -2
  80. package/dist/lib/server/decisioning/buyer-agent.js.map +1 -1
  81. package/dist/lib/server/decisioning/compose.d.ts +33 -2
  82. package/dist/lib/server/decisioning/compose.d.ts.map +1 -1
  83. package/dist/lib/server/decisioning/compose.js +13 -46
  84. package/dist/lib/server/decisioning/compose.js.map +1 -1
  85. package/dist/lib/server/decisioning/index.d.ts +2 -1
  86. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  87. package/dist/lib/server/decisioning/index.js +2 -1
  88. package/dist/lib/server/decisioning/index.js.map +1 -1
  89. package/dist/lib/server/decisioning/platform-helpers.d.ts +18 -0
  90. package/dist/lib/server/decisioning/platform-helpers.d.ts.map +1 -1
  91. package/dist/lib/server/decisioning/platform-helpers.js +20 -0
  92. package/dist/lib/server/decisioning/platform-helpers.js.map +1 -1
  93. package/dist/lib/server/decisioning/platform.d.ts +19 -21
  94. package/dist/lib/server/decisioning/platform.d.ts.map +1 -1
  95. package/dist/lib/server/decisioning/platform.js.map +1 -1
  96. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  97. package/dist/lib/server/decisioning/runtime/from-platform.js +334 -44
  98. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  99. package/dist/lib/server/decisioning/runtime/observed-modes.d.ts +40 -0
  100. package/dist/lib/server/decisioning/runtime/observed-modes.d.ts.map +1 -0
  101. package/dist/lib/server/decisioning/runtime/observed-modes.js +82 -0
  102. package/dist/lib/server/decisioning/runtime/observed-modes.js.map +1 -0
  103. package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +2 -2
  104. package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -1
  105. package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -1
  106. package/dist/lib/server/decisioning/runtime/validate-platform.js +9 -1
  107. package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -1
  108. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +125 -0
  109. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -0
  110. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js +52 -0
  111. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js.map +1 -0
  112. package/dist/lib/server/decisioning/tenant-registry.d.ts +16 -0
  113. package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -1
  114. package/dist/lib/server/decisioning/tenant-registry.js.map +1 -1
  115. package/dist/lib/server/index.d.ts +4 -1
  116. package/dist/lib/server/index.d.ts.map +1 -1
  117. package/dist/lib/server/index.js +9 -3
  118. package/dist/lib/server/index.js.map +1 -1
  119. package/dist/lib/server/serve.js +20 -2
  120. package/dist/lib/server/serve.js.map +1 -1
  121. package/dist/lib/server/test-controller.d.ts +3 -0
  122. package/dist/lib/server/test-controller.d.ts.map +1 -1
  123. package/dist/lib/server/test-controller.js +23 -20
  124. package/dist/lib/server/test-controller.js.map +1 -1
  125. package/dist/lib/testing/comply-controller.d.ts +23 -2
  126. package/dist/lib/testing/comply-controller.d.ts.map +1 -1
  127. package/dist/lib/testing/comply-controller.js +19 -2
  128. package/dist/lib/testing/comply-controller.js.map +1 -1
  129. package/dist/lib/testing/index.d.ts +1 -1
  130. package/dist/lib/testing/index.d.ts.map +1 -1
  131. package/dist/lib/testing/index.js.map +1 -1
  132. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  133. package/dist/lib/testing/storyboard/validations.js +36 -54
  134. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  135. package/dist/lib/testing/test-controller.d.ts +10 -4
  136. package/dist/lib/testing/test-controller.d.ts.map +1 -1
  137. package/dist/lib/testing/test-controller.js +9 -3
  138. package/dist/lib/testing/test-controller.js.map +1 -1
  139. package/dist/lib/types/index.d.ts +3 -2
  140. package/dist/lib/types/index.d.ts.map +1 -1
  141. package/dist/lib/types/index.js +3 -0
  142. package/dist/lib/types/index.js.map +1 -1
  143. package/dist/lib/utils/glob.d.ts +4 -2
  144. package/dist/lib/utils/glob.d.ts.map +1 -1
  145. package/dist/lib/utils/glob.js +4 -2
  146. package/dist/lib/utils/glob.js.map +1 -1
  147. package/dist/lib/version.d.ts +3 -3
  148. package/dist/lib/version.js +3 -3
  149. package/docs/llms.txt +2 -2
  150. package/examples/README.md +29 -13
  151. package/examples/hello-cluster.ts +62 -23
  152. package/examples/hello_creative_adapter_ad_server.ts +790 -0
  153. package/examples/hello_seller_adapter_guaranteed.ts +80 -22
  154. package/examples/hello_seller_adapter_non_guaranteed.ts +1020 -0
  155. package/examples/hello_si_adapter_brand.ts +572 -0
  156. package/package.json +3 -2
  157. package/skills/build-creative-agent/SKILL.md +103 -183
  158. package/skills/build-generative-seller-agent/SKILL.md +15 -9
  159. package/skills/build-governance-agent/SKILL.md +20 -11
  160. package/skills/build-retail-media-agent/SKILL.md +10 -8
  161. package/skills/build-seller-agent/SKILL.md +15 -13
  162. package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +9 -31
  163. package/skills/build-si-agent/SKILL.md +251 -196
  164. package/skills/build-signals-agent/SKILL.md +2 -0
  165. package/skills/call-adcp-agent/SKILL.md +7 -1
  166. package/skills/call-adcp-agent.previous/SKILL.md +0 -261
@@ -23,6 +23,8 @@ A signals agent serves audience segments to buyers for campaign targeting. Two t
23
23
  - Rendering creatives from briefs → that's a creative agent
24
24
  - Building a client that _calls_ a signals agent → see `docs/getting-started.md`
25
25
 
26
+ **Often claimed alongside:** [`sales-social`](../build-seller-agent/SKILL.md) (walled-garden audience push), [`sales-non-guaranteed`](../build-seller-agent/SKILL.md) (DSP data marketplace as a side surface), [`audience-sync`](../build-seller-agent/SKILL.md) (identity-provider owned-graph activation). See [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
27
+
26
28
  ## Specialisms This Skill Covers
27
29
 
28
30
  | Specialism | Status | Delta | See |
@@ -118,8 +118,12 @@ Every validation failure produces:
118
118
  - `issues[].pointer` — RFC 6901 JSON Pointer to the field.
119
119
  - `issues[].keyword` — AJV keyword (`required`, `type`, `oneOf`, `anyOf`, `additionalProperties`, `format`, `enum`).
120
120
  - `issues[].variants` — when the keyword is `oneOf` or `anyOf`, each entry lists one variant's `required` + declared `properties`. **Pick ONE variant**, send only its `required` fields. This is the fastest recovery path when you didn't know the field was a union.
121
+ - `issues[].discriminator` — when an SDK ≥6.7 picks a "best surviving variant" of a const-discriminated union, this is the `[{field, value}, …]` pairs that variant requires. Reads as the validator's verdict on which branch you were inferred to be targeting. Example: `discriminator: [{field: 'type', value: 'key_value'}]` plus `pointer: '/deployments/0/activation_key/key'` and `keyword: 'required'` means "you picked the `key_value` activation_key variant and it requires top-level `key` and `value`." Compound discriminators like `audience-selector`'s `(type, value_type)` produce two-entry arrays.
122
+ - `issues[].schemaId` — `$id` of the rejecting schema. For tools served from the bundled tree this is usually the response root; for flat-tree tools it can land on the deeper sub-schema. Diagnostic only; the actionable lever is `discriminator` + `variants` + `pointer`.
123
+ - `issues[].allowedValues` — closed enum lists for `keyword: 'enum'` issues. Picking from this list closes the case in one round.
124
+ - `issues[].hint` — SDK ≥6.8 only. One-sentence curated recipe for known shape gotchas: discriminator nesting (`activation_key`, VAST `delivery_type`), shape mismatches (`format_id` object, `budget` number, `signal_ids` provenance objects), and discriminator merging (`account`). When present, the hint is the most-direct fix path; read it before walking variants. Absent on the long tail — no hint just means there's no curated rule for the pattern.
121
125
 
122
- Patch the pointers, don't re-guess what the skill or the `variants` already told you, resend. Three attempts should cover every field.
126
+ **Recovery order**: read `hint` first (when present, it's the validated fix path); then `discriminator` (names which branch to fix); then `variants` (lists every option if you're not in a branch); then `pointer` + `keyword` + `message` for the leaf fix. Patch and resend. Three attempts should cover every field.
123
127
 
124
128
  ## Minimal working examples
125
129
 
@@ -225,6 +229,8 @@ Quick lookup before reading the full envelope. Match what you see in `adcp_error
225
229
  | Symptom | What it means | Fix |
226
230
  |---|---|---|
227
231
  | `keyword: 'oneOf'` with `variants[]` | Discriminated union — you sent fields from multiple variants, or none | Pick ONE variant from `variants[]`. Send only its `required` fields. |
232
+ | `discriminator: [{field, value}]` on a `required` issue | Validator inferred which branch you targeted; you missed required fields IN that branch | Read the `discriminator` pair, fill the missing required fields at the same level (don't nest under the discriminator field name). |
233
+ | `hint:` field present on the issue | SDK matched a curated shape-gotcha rule | Apply the hint directly — it's the validated fix path. |
228
234
  | 2-3 `additionalProperties` errors at the same pointer | You merged `oneOf` variants ({account_id, brand, operator, …}) | Drop to one variant. Don't keep "extra" fields "for completeness". |
229
235
  | `keyword: 'required'`, `pointer: '/idempotency_key'` | Mutating tool, no UUID | Generate fresh UUID per logical operation. Reuse it on retries. |
230
236
  | `keyword: 'type'` or `additionalProperties` at `/budget` | Sent `{amount, currency}` | `budget` is a number. Currency is implied by `pricing_option_id`. |
@@ -1,261 +0,0 @@
1
- ---
2
- name: call-adcp-agent
3
- description: Wire-level invariants for any AdCP buyer call — idempotency_key replay semantics, account `oneOf` variants, async `status:'submitted'`+`task_id` polling, error recovery from `adcp_error.issues[]`. Load before any per-protocol task skill (adcp-media-buy, adcp-creative, adcp-signals, adcp-governance, adcp-si, adcp-brand) when calling an AdCP agent as a buyer.
4
- adcp_version: "3.x"
5
- type: cross-cutting
6
- ---
7
-
8
- # Call an AdCP agent
9
-
10
- ## Overview
11
-
12
- AdCP (Ad Context Protocol) agents expose a fixed tool surface (`get_products`, `create_media_buy`, `get_signals`, …) over MCP or A2A. Tool names come from `get_adcp_capabilities`; exact request/response shapes come from `get_schema(tool_name)` when the agent exposes it, otherwise from the bundled JSON Schemas your SDK ships (the layout differs by SDK — see "Discovery chain" below). This skill teaches the invariants that don't live cleanly in any schema: cross-tool patterns, async flow, error recovery.
13
-
14
- ## When to Use
15
-
16
- - User wants to call a publisher / SSP / retail media network over AdCP
17
- - Tool names like `get_products`, `create_media_buy`, `sync_creatives`, `get_signals` appear in the available-tools list
18
- - Agent card advertises `protocolVersion: '0.3.0'` with `skills` listing AdCP tool names
19
- - **Not this skill:** building an AdCP seller agent (see `@adcp/client/skills/build-seller-agent/` and analogous SDK skills)
20
-
21
- ## Discovery chain
22
-
23
- Walk these in order on first contact:
24
-
25
- 1. **Agent card** (A2A) or **`tools/list`** (MCP): returns tool NAMES. AdCP MCP servers no longer publish per-tool parameter schemas in `tools/list` — everything shows `{type: 'object', properties: {}}`. Don't try to infer shape from here.
26
- 2. **`get_adcp_capabilities`**: returns supported protocols (`media_buy`, `signals`, `creative`, …), AdCP major versions, feature flags. Tells you WHICH tools this agent supports, not how to call them.
27
- 3. **`get_schema(tool_name)`** *(when the agent exposes it — pending standardization in [#3057](https://github.com/adcontextprotocol/adcp/issues/3057), not yet universal)*: returns the JSON Schema for a tool's request/response. Preferred over reading bundled schemas when available.
28
- 4. **Bundled schemas** (offline, authoritative): every SDK ships the AdCP JSON Schemas locally. Path differs by SDK — spec repo source uses `dist/schemas/<version>/bundled/`, `@adcp/client` puts them at `schemas/cache/<version>/bundled/` after `npm run sync-schemas`, Python and Go SDKs use their own conventions. **Don't hardcode a path** — let the SDK's loader find them, or ask the developer. Each schema is `<protocol>/<tool>-{request,response}.json` once you locate the bundle. The canonical source for every SDK is `https://adcontextprotocol.org/protocol/<version>.tgz`.
29
-
30
- ## Non-obvious rules every buyer must follow
31
-
32
- ### `idempotency_key` is required on every mutating tool
33
-
34
- UUID format. The key is your retry-safety guarantee — and the most common way naive callers create duplicate media buys is by misunderstanding it:
35
-
36
- - **Same key on retry → replay.** The server returns the SAME response — same `task_id`, same `media_buy_id`, same shape, byte-for-byte. Use this for transport-level retries (timeout, 5xx, dropped connection).
37
- - **Fresh key on retry → NEW operation.** Generating a new UUID because the previous attempt failed is how you double-book. Reuse the key until you've seen a terminal response (success, error, or non-retryable error).
38
- - **Same key, different body → server-defined.** Most agents return the original cached response and ignore the body change. Don't rely on it — pick a fresh key only when you genuinely want a new operation.
39
- - For async flows, the replayed response carries the **same `task_id`**, so polling continues against the same task instead of forking a duplicate.
40
-
41
- Required on: `create_media_buy`, `update_media_buy`, `sync_creatives`, `sync_audiences`, `sync_accounts`, `sync_catalogs`, `sync_event_sources`, `sync_plans`, `sync_governance`, `activate_signal`, `acquire_rights`, `log_event`, `report_usage`, `provide_performance_feedback`, `report_plan_outcome`, `create_property_list`, `update_property_list`, `delete_property_list`, `create_collection_list`, `update_collection_list`, `delete_collection_list`, `create_content_standards`, `update_content_standards`, `calibrate_content`, `si_initiate_session`, `si_send_message`.
42
-
43
- Missing the key → `adcp_error.code: 'VALIDATION_ERROR'` with `/idempotency_key` in `issues`.
44
-
45
- ### `account` is a `oneOf` — pick ONE variant, send ONLY its fields
46
-
47
- Probably the single most common stumble for naive LLMs. `account` is a discriminated union. Per AdCP 3.0, two variants on `create_media_buy` / `update_media_buy`:
48
-
49
- ```json
50
- // variant 0: by seller-assigned id (from sync_accounts or list_accounts)
51
- "account": { "account_id": "seller_assigned_id" }
52
-
53
- // variant 1: by natural key (brand + operator, optional sandbox)
54
- "account": { "brand": { "domain": "acme.com" }, "operator": "sales.example" }
55
- ```
56
-
57
- **Do NOT merge required fields across variants** — `additionalProperties: false` on each variant means `{account_id, brand}` fails BOTH. Pick one variant and send only its fields. Always check the specific tool's schema because other tools (e.g. `sync_creatives`) may accept a superset.
58
-
59
- ### `brand` takes `{domain}` — not `{brand_id}`
60
-
61
- ```json
62
- "brand": { "domain": "acme.example" }
63
- ```
64
-
65
- ### Async responses: `status: 'submitted'` means "queued, poll later"
66
-
67
- A mutating tool can return one of three shapes:
68
-
69
- ```json
70
- // Success (sync): the work is done
71
- { "media_buy_id": "mb_123", "packages": [...], "confirmed_at": "..." }
72
-
73
- // Submitted (async): the work is queued
74
- { "status": "submitted", "task_id": "tk_abc", "message": "Awaiting IO signature" }
75
-
76
- // Error: don't retry without fixing
77
- { "errors": [{ "code": "PRODUCT_NOT_FOUND", "message": "..." }] }
78
- ```
79
-
80
- When you see `status: 'submitted'`, the work is NOT complete. Poll via `tasks/get` (A2A) or the MCP async task extension, using the `task_id`. Over A2A the AdCP `task_id` also rides on `artifact.metadata.adcp_task_id` — both work.
81
-
82
- ### `packages[*]` on media buys
83
-
84
- ```json
85
- "packages": [
86
- { "buyer_ref": "pkg_1", "product_id": "p_from_catalog", "budget": 10000, "pricing_option_id": "po_xyz" }
87
- ]
88
- ```
89
-
90
- `budget` is a **number** (not `{amount, currency}` — currency is implied by the pricing option). Required per package: `product_id`, `budget`, `pricing_option_id`. `buyer_ref` is optional but strongly recommended as a buyer-side correlation id across retries and reporting.
91
-
92
- ## Error envelope — read `issues[]` to recover
93
-
94
- Every validation failure produces:
95
-
96
- ```json
97
- {
98
- "adcp_error": {
99
- "code": "VALIDATION_ERROR",
100
- "recovery": "correctable",
101
- "field": "/first/offending/pointer",
102
- "issues": [
103
- {
104
- "pointer": "/account",
105
- "keyword": "oneOf",
106
- "message": "must match exactly one schema in oneOf",
107
- "variants": [
108
- { "index": 0, "required": ["account_id"], "properties": ["account_id"] },
109
- { "index": 1, "required": ["brand", "operator"], "properties": ["brand", "operator", "sandbox"] }
110
- ]
111
- },
112
- { "pointer": "/brand/domain", "keyword": "required", "message": "must have required property 'domain'" }
113
- ]
114
- }
115
- }
116
- ```
117
-
118
- - `issues[].pointer` — RFC 6901 JSON Pointer to the field.
119
- - `issues[].keyword` — AJV keyword (`required`, `type`, `oneOf`, `anyOf`, `additionalProperties`, `format`, `enum`).
120
- - `issues[].variants` — when the keyword is `oneOf` or `anyOf`, each entry lists one variant's `required` + declared `properties`. **Pick ONE variant**, send only its `required` fields. This is the fastest recovery path when you didn't know the field was a union.
121
- - `issues[].discriminator` — when an SDK ≥6.7 picks a "best surviving variant" of a const-discriminated union, this is the `[{field, value}, …]` pairs that variant requires. Reads as the validator's verdict on which branch you were inferred to be targeting. Example: `discriminator: [{field: 'type', value: 'key_value'}]` plus `pointer: '/deployments/0/activation_key/key'` and `keyword: 'required'` means "you picked the `key_value` activation_key variant and it requires top-level `key` and `value`." Compound discriminators like `audience-selector`'s `(type, value_type)` produce two-entry arrays.
122
- - `issues[].schemaId` — `$id` of the rejecting schema. For tools served from the bundled tree this is usually the response root; for flat-tree tools it can land on the deeper sub-schema. Diagnostic only; the actionable lever is `discriminator` + `variants` + `pointer`.
123
- - `issues[].allowedValues` — closed enum lists for `keyword: 'enum'` issues. Picking from this list closes the case in one round.
124
- - `issues[].hint` — SDK ≥6.8 only. One-sentence curated recipe for known shape gotchas: discriminator nesting (`activation_key`, VAST `delivery_type`), shape mismatches (`format_id` object, `budget` number, `signal_ids` provenance objects), and discriminator merging (`account`). When present, the hint is the most-direct fix path; read it before walking variants. Absent on the long tail — no hint just means there's no curated rule for the pattern.
125
-
126
- **Recovery order**: read `hint` first (when present, it's the validated fix path); then `discriminator` (names which branch to fix); then `variants` (lists every option if you're not in a branch); then `pointer` + `keyword` + `message` for the leaf fix. Patch and resend. Three attempts should cover every field.
127
-
128
- ## Minimal working examples
129
-
130
- ### get_products
131
-
132
- ```json
133
- {
134
- "buying_mode": "brief",
135
- "brief": "premium CTV sports inventory for live NBA finals in major US markets"
136
- }
137
- ```
138
-
139
- Returns `{ products: [{ product_id, name, description, delivery_type, pricing_options, ... }] }`.
140
-
141
- ### create_media_buy
142
-
143
- ```json
144
- {
145
- "idempotency_key": "<uuid>",
146
- "account": { "account_id": "seller_assigned_id" },
147
- "brand": { "domain": "acme.example" },
148
- "start_time": "2026-05-01T00:00:00Z",
149
- "end_time": "2026-05-31T23:59:59Z",
150
- "packages": [
151
- {
152
- "buyer_ref": "pkg_1",
153
- "product_id": "<product_id from get_products>",
154
- "budget": 10000,
155
- "pricing_option_id": "<pricing_option_id from product.pricing_options>"
156
- }
157
- ]
158
- }
159
- ```
160
-
161
- If you don't have a `seller_assigned_id`, use the natural-key variant instead:
162
- `"account": { "brand": { "domain": "acme.example" }, "operator": "sales.example" }`.
163
-
164
- Returns **either** `{ media_buy_id, packages: [...], confirmed_at }` (sync) **or** `{ status: 'submitted', task_id, message }` (async — guaranteed / IO-signed flows).
165
-
166
- ### sync_creatives
167
-
168
- ```json
169
- {
170
- "idempotency_key": "<uuid>",
171
- "account": { "account_id": "seller_assigned_id" },
172
- "creatives": [
173
- {
174
- "creative_id": "cr_1",
175
- "name": "My Creative",
176
- "format_id": { "agent_url": "https://creatives.adcontextprotocol.org", "id": "video_1920x1080" },
177
- "assets": {}
178
- }
179
- ]
180
- }
181
- ```
182
-
183
- Per-creative required: `creative_id`, `name`, `format_id: { agent_url, id }`, `assets` (shape depends on `format_id`; start with `{}` then fill required asset keys per format spec). Returns `{ creatives: [{ creative_id, action, status }] }` — items may fail individually without failing the batch.
184
-
185
- ### get_signals
186
-
187
- ```json
188
- {
189
- "signal_spec": "female professionals 25-54 in major US metros"
190
- }
191
- ```
192
-
193
- Returns `{ signals: [{ signal_agent_segment_id, match_rate, pricing, ... }] }`. Note: the identifier field is `signal_agent_segment_id` (not `signal_id`) — used as input to `activate_signal` below.
194
-
195
- ### activate_signal
196
-
197
- ```json
198
- {
199
- "idempotency_key": "<uuid>",
200
- "signal_agent_segment_id": "sig_premium_ctv_sports",
201
- "destinations": [
202
- { "type": "platform", "platform": "the-trade-desk" }
203
- ]
204
- }
205
- ```
206
-
207
- `destinations[]` is a `oneOf`: either `{type: 'platform', platform, account?}` OR `{type, agent_url, account?}`. Pick one shape per destination.
208
-
209
- ## Transport notes
210
-
211
- - **MCP**: `tools/call` with `{ name: 'tool_name', arguments: {...} }`. Returns `{ content, structuredContent, isError? }`. Read `structuredContent` for the typed response.
212
- - **A2A**: `message/send` with a `DataPart` of shape `{ skill: 'tool_name', input: {...} }` (the legacy key `parameters` is also accepted). Returns an A2A `Task`; the typed response is at `task.artifacts[0].parts[0].data`.
213
-
214
- Both transports share: idempotency, error shape, schema enforcement, and handler semantics. If a call works on one, the equivalent call works on the other.
215
-
216
- ## Gotchas I keep seeing
217
-
218
- 1. **Merging `oneOf` variants**: see the account section above. If you see three `additionalProperties` errors under one pointer, you merged. Drop to one variant.
219
- 2. **`budget` as an object**: it's a number. Currency comes from the `pricing_option`.
220
- 3. **`brand.brand_id` instead of `brand.domain`**: spec uses `domain`.
221
- 4. **Forgetting `idempotency_key`**: required on every mutating tool; see the list above.
222
- 5. **Treating A2A `Task.state: 'completed'` as AdCP completion**: A2A task state = transport call lifecycle. AdCP-level completion is in the artifact's payload (`structuredContent.status` or `data.status`). A `completed` A2A task can still carry a `submitted` AdCP response.
223
- 6. **`format_id` as a string**: `format_id` is always an object `{ agent_url, id }` (and sometimes `{ width, height, duration_ms }` for dimensions). Sending `"format_id": "video_1920x1080"` fails with an `additionalProperties` / `type` error — pass the object.
224
-
225
- ## Symptom → fix
226
-
227
- Quick lookup before reading the full envelope. Match what you see in `adcp_error.issues[*]`, apply the fix:
228
-
229
- | Symptom | What it means | Fix |
230
- |---|---|---|
231
- | `keyword: 'oneOf'` with `variants[]` | Discriminated union — you sent fields from multiple variants, or none | Pick ONE variant from `variants[]`. Send only its `required` fields. |
232
- | `discriminator: [{field, value}]` on a `required` issue | Validator inferred which branch you targeted; you missed required fields IN that branch | Read the `discriminator` pair, fill the missing required fields at the same level (don't nest under the discriminator field name). |
233
- | `hint:` field present on the issue | SDK matched a curated shape-gotcha rule | Apply the hint directly — it's the validated fix path. |
234
- | 2-3 `additionalProperties` errors at the same pointer | You merged `oneOf` variants ({account_id, brand, operator, …}) | Drop to one variant. Don't keep "extra" fields "for completeness". |
235
- | `keyword: 'required'`, `pointer: '/idempotency_key'` | Mutating tool, no UUID | Generate fresh UUID per logical operation. Reuse it on retries. |
236
- | `keyword: 'type'` or `additionalProperties` at `/budget` | Sent `{amount, currency}` | `budget` is a number. Currency is implied by `pricing_option_id`. |
237
- | `additionalProperties` at `/format_id` (string passed) | Sent `"format_id": "video_..."` | `format_id` is `{agent_url, id}` — always an object. |
238
- | `keyword: 'enum'` at `/destinations/*/type` | Made-up destination type | Use `'platform'` (with `platform`) or `'agent'` (with `agent_url`). |
239
- | Response carries `status: 'submitted'` and `task_id` | Async — work is queued, NOT done | Poll via `tasks/get` (A2A) or the MCP async task extension using `task_id`. |
240
- | `recovery: 'transient'` (rate limit, 5xx, timeout) | Server-side, retry-safe | Retry with the **same** `idempotency_key`. |
241
- | `recovery: 'correctable'` | Buyer-side fix | Read `issues[]`, patch the pointers, resend. Most cases close in one attempt. |
242
- | `recovery: 'terminal'` (account suspended, payment required, …) | Requires human action | Don't retry. Surface to the user. |
243
- | HTTP 401 with `WWW-Authenticate` header | Missing or expired credential | Add `Authorization` per the agent's auth spec; re-auth if applicable. |
244
-
245
- If your symptom isn't here, fall through to the next section.
246
-
247
- ## If you get stuck
248
-
249
- Priority order:
250
-
251
- 1. Re-read the failure's `issues[]`. The pointer list plus this skill covers 80% of cases.
252
- 2. Call `get_schema(tool_name)` if the agent exposes it (see [#3057](https://github.com/adcontextprotocol/adcp/issues/3057) for the pending standard).
253
- 3. Read the bundled JSON Schema for `<protocol>/<tool>-request.json` — see Discovery chain step 4 for path resolution. If you can't locate the SDK's schema cache, ask the developer or fall back to `get_schema()`.
254
- 4. Consult the per-protocol skill (`adcp-media-buy`, `adcp-creative`, …) for specialism-specific patterns.
255
-
256
- ## Related
257
-
258
- - [Calling an agent (docs)](https://adcontextprotocol.org/docs/protocol/calling-an-agent) — human-readable narrative form of this skill
259
- - `skills/adcp-media-buy/`, `skills/adcp-creative/`, `skills/adcp-signals/`, `skills/adcp-governance/`, `skills/adcp-si/`, `skills/adcp-brand/` — per-protocol task skills (layered on top of this one)
260
- - `@adcp/client/skills/build-seller-agent/SKILL.md` — building agents on the other side of the call
261
- - Bundled JSON Schemas — canonical for every tool, version-pinned. Path differs by SDK (see Discovery chain step 4). Pulled from the protocol tarball at `/protocol/<version>.tgz`.