@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.
- package/bin/adcp.js +15 -3
- package/dist/lib/adapters/derived-account-store.d.ts +152 -0
- package/dist/lib/adapters/derived-account-store.d.ts.map +1 -0
- package/dist/lib/adapters/derived-account-store.js +135 -0
- package/dist/lib/adapters/derived-account-store.js.map +1 -0
- package/dist/lib/adapters/implicit-account-store.d.ts +3 -1
- package/dist/lib/adapters/implicit-account-store.d.ts.map +1 -1
- package/dist/lib/adapters/implicit-account-store.js +3 -1
- package/dist/lib/adapters/implicit-account-store.js.map +1 -1
- package/dist/lib/adapters/index.d.ts +1 -0
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +7 -1
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.d.ts +3 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.d.ts.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.js +3 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.js.map +1 -1
- package/dist/lib/adapters/roster-account-store.d.ts +85 -24
- package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
- package/dist/lib/adapters/roster-account-store.js +52 -30
- package/dist/lib/adapters/roster-account-store.js.map +1 -1
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +13 -8
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts +81 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.js +200 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.js.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/server.d.ts +39 -0
- package/dist/lib/mock-server/creative-ad-server/server.d.ts.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/server.js +618 -0
- package/dist/lib/mock-server/creative-ad-server/server.js.map +1 -0
- package/dist/lib/mock-server/index.d.ts.map +1 -1
- package/dist/lib/mock-server/index.js +180 -24
- package/dist/lib/mock-server/index.js.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts +66 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js +193 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts +33 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.js +782 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.js.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts +50 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.js +133 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.js.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts +13 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.js +609 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.js.map +1 -0
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +1 -41
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-mode.d.ts +113 -0
- package/dist/lib/server/account-mode.d.ts.map +1 -0
- package/dist/lib/server/account-mode.js +125 -0
- package/dist/lib/server/account-mode.js.map +1 -0
- package/dist/lib/server/adcp-server.js +41 -0
- package/dist/lib/server/adcp-server.js.map +1 -1
- package/dist/lib/server/auth.d.ts +35 -0
- package/dist/lib/server/auth.d.ts.map +1 -1
- package/dist/lib/server/auth.js.map +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +26 -9
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +46 -20
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/ctx-metadata/store.d.ts +1 -1
- package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -1
- package/dist/lib/server/ctx-metadata/store.js +1 -0
- package/dist/lib/server/ctx-metadata/store.js.map +1 -1
- package/dist/lib/server/decisioning/account.d.ts +5 -0
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.d.ts +37 -4
- package/dist/lib/server/decisioning/buyer-agent.d.ts.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.js +12 -2
- package/dist/lib/server/decisioning/buyer-agent.js.map +1 -1
- package/dist/lib/server/decisioning/compose.d.ts +33 -2
- package/dist/lib/server/decisioning/compose.d.ts.map +1 -1
- package/dist/lib/server/decisioning/compose.js +13 -46
- package/dist/lib/server/decisioning/compose.js.map +1 -1
- package/dist/lib/server/decisioning/index.d.ts +2 -1
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +2 -1
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.d.ts +18 -0
- package/dist/lib/server/decisioning/platform-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.js +20 -0
- package/dist/lib/server/decisioning/platform-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/platform.d.ts +19 -21
- package/dist/lib/server/decisioning/platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +334 -44
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.d.ts +40 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.js +82 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +2 -2
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.js +9 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +125 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js +52 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts +16 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.js.map +1 -1
- package/dist/lib/server/index.d.ts +4 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +9 -3
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/serve.js +20 -2
- package/dist/lib/server/serve.js.map +1 -1
- package/dist/lib/server/test-controller.d.ts +3 -0
- package/dist/lib/server/test-controller.d.ts.map +1 -1
- package/dist/lib/server/test-controller.js +23 -20
- package/dist/lib/server/test-controller.js.map +1 -1
- package/dist/lib/testing/comply-controller.d.ts +23 -2
- package/dist/lib/testing/comply-controller.d.ts.map +1 -1
- package/dist/lib/testing/comply-controller.js +19 -2
- package/dist/lib/testing/comply-controller.js.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +36 -54
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/test-controller.d.ts +10 -4
- package/dist/lib/testing/test-controller.d.ts.map +1 -1
- package/dist/lib/testing/test-controller.js +9 -3
- package/dist/lib/testing/test-controller.js.map +1 -1
- package/dist/lib/types/index.d.ts +3 -2
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +3 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/utils/glob.d.ts +4 -2
- package/dist/lib/utils/glob.d.ts.map +1 -1
- package/dist/lib/utils/glob.js +4 -2
- package/dist/lib/utils/glob.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/docs/llms.txt +2 -2
- package/examples/README.md +29 -13
- package/examples/hello-cluster.ts +62 -23
- package/examples/hello_creative_adapter_ad_server.ts +790 -0
- package/examples/hello_seller_adapter_guaranteed.ts +80 -22
- package/examples/hello_seller_adapter_non_guaranteed.ts +1020 -0
- package/examples/hello_si_adapter_brand.ts +572 -0
- package/package.json +3 -2
- package/skills/build-creative-agent/SKILL.md +103 -183
- package/skills/build-generative-seller-agent/SKILL.md +15 -9
- package/skills/build-governance-agent/SKILL.md +20 -11
- package/skills/build-retail-media-agent/SKILL.md +10 -8
- package/skills/build-seller-agent/SKILL.md +15 -13
- package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +9 -31
- package/skills/build-si-agent/SKILL.md +251 -196
- package/skills/build-signals-agent/SKILL.md +2 -0
- package/skills/call-adcp-agent/SKILL.md +7 -1
- package/skills/call-adcp-agent.previous/SKILL.md +0 -261
|
@@ -23,6 +23,8 @@ A creative agent manages the creative lifecycle: accepts assets from buyers, sto
|
|
|
23
23
|
- Selling inventory (no creative management) → `skills/build-seller-agent/`
|
|
24
24
|
- Serving audience segments → `skills/build-signals-agent/`
|
|
25
25
|
|
|
26
|
+
**Often claimed alongside:** [`creative-generative`](../build-generative-seller-agent/SKILL.md) (hybrid creative platform — template-driven + AI-generated ads share the same library), [`sales-catalog-driven`](../build-retail-media-agent/SKILL.md) (retail-media dynamic-creative rendering). See [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
|
|
27
|
+
|
|
26
28
|
## Specialisms This Skill Covers
|
|
27
29
|
|
|
28
30
|
Creative specialisms are three distinct archetypes with materially different tool contracts. Pick the one that matches your platform — do not try to make one handler cover all three.
|
|
@@ -115,6 +117,7 @@ What happens when a creative is synced:
|
|
|
115
117
|
> accepted_media_types: ['image/jpeg', 'image/png'],
|
|
116
118
|
> }] }
|
|
117
119
|
> ```
|
|
120
|
+
>
|
|
118
121
|
> - `build_creative` response is `{ creative_manifest: { format_id, assets } }` (single) or `{ creative_manifests: [...] }` (multi). Platform-native fields at the top level (`tag_url`, `creative_id`, `media_type`) are **invalid** — use `buildCreativeResponse({ creative_manifest })` / `buildCreativeMultiResponse({ creative_manifests })` from `@adcp/sdk/server` to lock the shape at compile time.
|
|
119
122
|
> - Each asset in `creative_manifest.assets` requires an `asset_type` discriminator. Use the typed factories (`imageAsset`, `videoAsset`, `audioAsset`, `htmlAsset`, `urlAsset`, `textAsset`) so the discriminator is injected for you; a plain `{ serving_tag: { content: '<vast>...' } }` fails validation.
|
|
120
123
|
> - `preview_creative` renders have the same pattern — each `renders[]` entry is a oneOf on `output_format`. Use `urlRender({...})`, `htmlRender({...})`, or `bothRender({...})` to inject the discriminator and require the matching `preview_url` / `preview_html` field automatically.
|
|
@@ -124,14 +127,14 @@ What happens when a creative is synced:
|
|
|
124
127
|
|
|
125
128
|
**Handler bindings — read the Contract column entry before writing each return:**
|
|
126
129
|
|
|
127
|
-
| Tool | Handler | Contract (field list) | Gotchas
|
|
128
|
-
| ----------------------- | ------------------------------ | --------------------------------------------------------------------- |
|
|
129
|
-
| `get_adcp_capabilities` | auto-generated | n/a | Do not register manually.
|
|
130
|
+
| Tool | Handler | Contract (field list) | Gotchas |
|
|
131
|
+
| ----------------------- | ------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
132
|
+
| `get_adcp_capabilities` | auto-generated | n/a | Do not register manually. |
|
|
130
133
|
| `list_creative_formats` | `creative.listCreativeFormats` | [`#list_creative_formats`](../../docs/llms.txt#list_creative_formats) | Each `renders[]` entry MUST have `role` + exactly one of `dimensions` (object) OR `parameters_from_format_id: true` — `{width, height}` shorthand fails. Each `assets[]` entry MUST have `item_type: 'individual' as const` + `asset_id` + `asset_type` (image/video/audio/text/click_url/html) + `required: boolean`. See the strict-shape callout above the table. |
|
|
131
|
-
| `sync_creatives` | `creative.syncCreatives` | [`#sync_creatives`](../../docs/llms.txt#sync_creatives) | Echo `creative_id`; `action` ∈ `created \| updated \| unchanged \| failed \| deleted`.
|
|
132
|
-
| `list_creatives` | `creative.listCreatives` | [`#list_creatives`](../../docs/llms.txt#list_creatives) | Honor `args.filters?.format_ids` when present. `created_date` + `updated_date` on each row are required ISO timestamps.
|
|
133
|
-
| `preview_creative` | `creative.previewCreative` | [`#preview_creative`](../../docs/llms.txt#preview_creative) | `renders[].output_format` is a discriminator — use `urlRender({...})`, `htmlRender({...})`, or `bothRender({...})` so the discriminator is injected and the required `preview_url`/`preview_html` field is enforced at compile time.
|
|
134
|
-
| `build_creative` | `creative.buildCreative` | [`#build_creative`](../../docs/llms.txt#build_creative) | Check `args.target_format_id` → library lookup; fall back to `args.creative_id`. Response requires `creative_manifest.format_id` + `creative_manifest.assets`.
|
|
134
|
+
| `sync_creatives` | `creative.syncCreatives` | [`#sync_creatives`](../../docs/llms.txt#sync_creatives) | Echo `creative_id`; `action` ∈ `created \| updated \| unchanged \| failed \| deleted`. |
|
|
135
|
+
| `list_creatives` | `creative.listCreatives` | [`#list_creatives`](../../docs/llms.txt#list_creatives) | Honor `args.filters?.format_ids` when present. `created_date` + `updated_date` on each row are required ISO timestamps. |
|
|
136
|
+
| `preview_creative` | `creative.previewCreative` | [`#preview_creative`](../../docs/llms.txt#preview_creative) | `renders[].output_format` is a discriminator — use `urlRender({...})`, `htmlRender({...})`, or `bothRender({...})` so the discriminator is injected and the required `preview_url`/`preview_html` field is enforced at compile time. |
|
|
137
|
+
| `build_creative` | `creative.buildCreative` | [`#build_creative`](../../docs/llms.txt#build_creative) | Check `args.target_format_id` → library lookup; fall back to `args.creative_id`. Response requires `creative_manifest.format_id` + `creative_manifest.assets`. |
|
|
135
138
|
|
|
136
139
|
Asset values use type-specific shapes, not a generic `asset_type` discriminator:
|
|
137
140
|
|
|
@@ -151,21 +154,21 @@ Some schemas also define an `ext` field for vendor-namespaced extensions. If you
|
|
|
151
154
|
|
|
152
155
|
## SDK Quick Reference
|
|
153
156
|
|
|
154
|
-
| SDK piece
|
|
155
|
-
|
|
|
156
|
-
| `createAdcpServerFromPlatform(platform, opts)`
|
|
157
|
-
| `createAdcpServer(config)`
|
|
158
|
-
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp`
|
|
159
|
-
| `creative: { listCreativeFormats, syncCreatives, ... }`
|
|
160
|
-
| `ctx.store.put(collection, id, data)`
|
|
161
|
-
| `ctx.store.get(collection, id)`
|
|
162
|
-
| `ctx.store.list(collection)`
|
|
163
|
-
| `listCreativeFormatsResponse(data)`
|
|
164
|
-
| `syncCreativesResponse(data)`
|
|
165
|
-
| `listCreativesResponse(data)`
|
|
166
|
-
| `buildCreativeResponse(data)`
|
|
167
|
-
| `previewCreativeResponse(data)`
|
|
168
|
-
| `adcpError(code, { message })`
|
|
157
|
+
| SDK piece | Usage |
|
|
158
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
159
|
+
| `createAdcpServerFromPlatform(platform, opts)` | Create server from a typed `DecisioningPlatform` — compile-time specialism enforcement, auto-capabilities |
|
|
160
|
+
| `createAdcpServer(config)` _(legacy)_ | v5 handler-bag entry. Mid-migration / escape-hatch only; reach via `@adcp/sdk/server/legacy/v5` |
|
|
161
|
+
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp` |
|
|
162
|
+
| `creative: { listCreativeFormats, syncCreatives, ... }` | Domain group — register handlers by name |
|
|
163
|
+
| `ctx.store.put(collection, id, data)` | Persist state (creative library) across requests |
|
|
164
|
+
| `ctx.store.get(collection, id)` | Retrieve persisted state |
|
|
165
|
+
| `ctx.store.list(collection)` | List all items in a collection (for `list_creatives`) |
|
|
166
|
+
| `listCreativeFormatsResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
167
|
+
| `syncCreativesResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
168
|
+
| `listCreativesResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
169
|
+
| `buildCreativeResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
170
|
+
| `previewCreativeResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
171
|
+
| `adcpError(code, { message })` | Structured error |
|
|
169
172
|
|
|
170
173
|
Import: `import { createAdcpServerFromPlatform, serve, adcpError } from '@adcp/sdk/server';`
|
|
171
174
|
|
|
@@ -268,63 +271,63 @@ class MyCreative implements DecisioningPlatform {
|
|
|
268
271
|
|
|
269
272
|
creative: CreativeAdServerPlatform = {
|
|
270
273
|
listCreativeFormats: async (params, ctx) => {
|
|
271
|
-
|
|
272
|
-
|
|
274
|
+
return { formats };
|
|
275
|
+
},
|
|
273
276
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
277
|
+
syncCreatives: async (params, ctx) => {
|
|
278
|
+
const results = [];
|
|
279
|
+
for (const creative of params.creatives) {
|
|
280
|
+
const now = new Date().toISOString();
|
|
281
|
+
const existing = await ctx.store.get('creatives', creative.creative_id);
|
|
282
|
+
await ctx.store.put('creatives', creative.creative_id, {
|
|
283
|
+
...creative,
|
|
284
|
+
status: 'approved',
|
|
285
|
+
created_date: existing?.created_date ?? now,
|
|
286
|
+
updated_date: now,
|
|
287
|
+
});
|
|
288
|
+
results.push({
|
|
289
|
+
creative_id: creative.creative_id,
|
|
290
|
+
action: existing ? ('updated' as const) : ('created' as const),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return { creatives: results };
|
|
294
|
+
},
|
|
292
295
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
296
|
+
listCreatives: async (params, ctx) => {
|
|
297
|
+
// `ctx.store.list` returns `{ items, nextCursor? }` — destructure.
|
|
298
|
+
let { items: creatives } = await ctx.store.list('creatives');
|
|
299
|
+
if (params.filters?.format_ids) {
|
|
300
|
+
creatives = creatives.filter(c => params.filters!.format_ids!.some(fid => fid.id === c.format_id?.id));
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
query_summary: { total_matching: creatives.length, returned: creatives.length, filters_applied: [] },
|
|
304
|
+
creatives,
|
|
305
|
+
pagination: { has_more: false },
|
|
306
|
+
};
|
|
307
|
+
},
|
|
305
308
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
309
|
+
buildCreative: async (params, ctx) => {
|
|
310
|
+
// `ctx.store.list` returns `{ items, nextCursor? }` — destructure.
|
|
311
|
+
// Calling `.find`/`.map` on the raw result throws `TypeError` and
|
|
312
|
+
// the dispatcher wraps it as `SERVICE_UNAVAILABLE`.
|
|
313
|
+
const { items: creatives } = await ctx.store.list('creatives');
|
|
314
|
+
const match = params.target_format_id
|
|
315
|
+
? creatives.find(c => c.format_id?.id === params.target_format_id!.id)
|
|
316
|
+
: params.creative_id
|
|
317
|
+
? await ctx.store.get('creatives', params.creative_id)
|
|
318
|
+
: null;
|
|
319
|
+
// Return structured errors — don't throw. The dispatcher now unwraps
|
|
320
|
+
// thrown envelopes, but throwing still releases the idempotency claim
|
|
321
|
+
// before the throw is caught. If your handler mutated state before
|
|
322
|
+
// throwing, a retry with the same key re-executes the write. Returning
|
|
323
|
+
// an error envelope has the same claim-release semantics, so the rule
|
|
324
|
+
// holds for both paths: mutate last, or don't mutate at all on error.
|
|
325
|
+
if (!match) return adcpError('CREATIVE_NOT_FOUND', { message: 'No matching creative' });
|
|
326
|
+
return {
|
|
327
|
+
creative_manifest: { format_id: match.format_id, assets: match.assets ?? {} },
|
|
328
|
+
sandbox: true,
|
|
329
|
+
};
|
|
330
|
+
},
|
|
328
331
|
|
|
329
332
|
previewCreative: async params => {
|
|
330
333
|
return {
|
|
@@ -560,22 +563,22 @@ Common failure decoder:
|
|
|
560
563
|
|
|
561
564
|
## Common Mistakes
|
|
562
565
|
|
|
563
|
-
| Mistake | Fix
|
|
564
|
-
| ---------------------------------------------------------------------- |
|
|
565
|
-
| Using `createTaskCapableServer` + `server.tool()` | Use `createAdcpServerFromPlatform` with a typed `creative: CreativeAdServerPlatform` (or `CreativeBuilderPlatform`) field
|
|
566
|
+
| Mistake | Fix |
|
|
567
|
+
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
568
|
+
| Using `createTaskCapableServer` + `server.tool()` | Use `createAdcpServerFromPlatform` with a typed `creative: CreativeAdServerPlatform` (or `CreativeBuilderPlatform`) field |
|
|
566
569
|
| Calling `createAdcpServer` directly in new code | Reach for `createAdcpServerFromPlatform`; `createAdcpServer` lives at `@adcp/sdk/server/legacy/v5` for mid-migration / escape-hatch only |
|
|
567
|
-
| Manually registering `get_adcp_capabilities` | Auto-generated by `createAdcpServerFromPlatform` from your typed `DecisioningPlatform` — do not register it
|
|
568
|
-
| Calling `server.registerTool('preview_creative', ...)` | `AdcpServer` does not expose `registerTool` — put `previewCreative` in the `creative:` domain group like the other handlers
|
|
569
|
-
| Using module-level Maps for state | Use `ctx.store.put/get/list` — framework provides `InMemoryStateStore` by default
|
|
570
|
-
| Calling response builders manually in domain handlers | Handlers return raw data — response builders are auto-applied across the `creative:` group, including `preview_creative`
|
|
571
|
-
| `list_creatives` ignores format filter | Check `args.filters?.format_ids` and filter results
|
|
572
|
-
| `preview_creative` returns wrong response_type | Must be `'single'` for single creative previews
|
|
573
|
-
| `preview_creative` looks up by creative_id | Preview the `creative_manifest` from the request — no library lookup needed
|
|
574
|
-
| `build_creative` looks up by `args.creative_id` only | Storyboard sends `target_format_id` — find a synced creative matching that format
|
|
575
|
-
| `build_creative` missing creative_manifest | Required field — contains the built output
|
|
576
|
-
| `creative_manifest` includes `name` field | `CreativeManifest` has no `name` — only `format_id` and `assets`
|
|
577
|
-
| HTML asset uses `{ html: '...' }` | Use `{ content: '...' }` — the schema field is `content`, not `html`
|
|
578
|
-
| format_ids in list_creative_formats don't match what sellers reference | Sellers include your format_ids in their products — if the buyer can't look them up via list_creative_formats, creative sync breaks
|
|
570
|
+
| Manually registering `get_adcp_capabilities` | Auto-generated by `createAdcpServerFromPlatform` from your typed `DecisioningPlatform` — do not register it |
|
|
571
|
+
| Calling `server.registerTool('preview_creative', ...)` | `AdcpServer` does not expose `registerTool` — put `previewCreative` in the `creative:` domain group like the other handlers |
|
|
572
|
+
| Using module-level Maps for state | Use `ctx.store.put/get/list` — framework provides `InMemoryStateStore` by default |
|
|
573
|
+
| Calling response builders manually in domain handlers | Handlers return raw data — response builders are auto-applied across the `creative:` group, including `preview_creative` |
|
|
574
|
+
| `list_creatives` ignores format filter | Check `args.filters?.format_ids` and filter results |
|
|
575
|
+
| `preview_creative` returns wrong response_type | Must be `'single'` for single creative previews |
|
|
576
|
+
| `preview_creative` looks up by creative_id | Preview the `creative_manifest` from the request — no library lookup needed |
|
|
577
|
+
| `build_creative` looks up by `args.creative_id` only | Storyboard sends `target_format_id` — find a synced creative matching that format |
|
|
578
|
+
| `build_creative` missing creative_manifest | Required field — contains the built output |
|
|
579
|
+
| `creative_manifest` includes `name` field | `CreativeManifest` has no `name` — only `format_id` and `assets` |
|
|
580
|
+
| HTML asset uses `{ html: '...' }` | Use `{ content: '...' }` — the schema field is `content`, not `html` |
|
|
581
|
+
| format_ids in list_creative_formats don't match what sellers reference | Sellers include your format_ids in their products — if the buyer can't look them up via list_creative_formats, creative sync breaks |
|
|
579
582
|
|
|
580
583
|
## Storyboards
|
|
581
584
|
|
|
@@ -592,101 +595,18 @@ Common failure decoder:
|
|
|
592
595
|
|
|
593
596
|
Storyboard: `creative_ad_server`. Stateful — the library is pre-loaded; buyers do **not** push assets via `sync_creatives` at runtime. Pricing and billing round-trips are first-class.
|
|
594
597
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
```typescript
|
|
598
|
-
listCreatives: async (params, ctx) => {
|
|
599
|
-
const { items } = await ctx.store.list('creatives');
|
|
600
|
-
const creatives = items.map((c) => ({
|
|
601
|
-
creative_id: c.creative_id,
|
|
602
|
-
name: c.name,
|
|
603
|
-
format_id: c.format_id,
|
|
604
|
-
status: 'approved' as const,
|
|
605
|
-
created_date: c.created_date,
|
|
606
|
-
updated_date: c.updated_date,
|
|
607
|
-
...(params.include_pricing && {
|
|
608
|
-
pricing_options: [{
|
|
609
|
-
pricing_option_id: 'standard_cpm',
|
|
610
|
-
model: 'cpm' as const,
|
|
611
|
-
cpm: 2.5,
|
|
612
|
-
currency: 'USD',
|
|
613
|
-
}],
|
|
614
|
-
}),
|
|
615
|
-
}));
|
|
616
|
-
return {
|
|
617
|
-
query_summary: { total_matching: creatives.length, returned: creatives.length, filters_applied: [] },
|
|
618
|
-
creatives,
|
|
619
|
-
pagination: { has_more: false },
|
|
620
|
-
};
|
|
621
|
-
},
|
|
622
|
-
```
|
|
598
|
+
**Fork target**: [`examples/hello_creative_adapter_ad_server.ts`](../../examples/hello_creative_adapter_ad_server.ts) is the worked, passing reference adapter for this specialism. CI gates strict tsc + storyboard pass + upstream-traffic façade — all 7 storyboard steps pass on the worked build.
|
|
623
599
|
|
|
624
|
-
|
|
600
|
+
The adapter demonstrates the stateful-library deltas vs `creative-template`:
|
|
625
601
|
|
|
626
|
-
|
|
627
|
-
buildCreative
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
<Impression><![CDATA[https://adserver.example/imp?cb=[CACHEBUSTER]&mb=${params.media_buy_id}]]></Impression>
|
|
633
|
-
<Creatives><Creative>
|
|
634
|
-
<Linear><Duration>00:00:30</Duration>
|
|
635
|
-
<MediaFiles><MediaFile type="video/mp4"><![CDATA[${creative.video_url}]]></MediaFile></MediaFiles>
|
|
636
|
-
<VideoClicks><ClickThrough><![CDATA[[CLICK_URL]https://landing.example]]></ClickThrough></VideoClicks>
|
|
637
|
-
</Linear>
|
|
638
|
-
</Creative></Creatives>
|
|
639
|
-
</InLine></Ad>
|
|
640
|
-
</VAST>`;
|
|
641
|
-
|
|
642
|
-
return {
|
|
643
|
-
creative_manifest: {
|
|
644
|
-
format_id: params.target_format_id,
|
|
645
|
-
assets: { serving_tag: { content: vast } }, // HTML asset shape: { content: string }
|
|
646
|
-
},
|
|
647
|
-
pricing_option_id: params.pricing_option_id, // echo
|
|
648
|
-
vendor_cost: { amount: 0, currency: 'USD' }, // CPM at build time — billing is separate
|
|
649
|
-
sandbox: true,
|
|
650
|
-
};
|
|
651
|
-
},
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
**`report_usage`** is the billing reconciliation tool. Validate `idempotency_key` (return the same response for the same key) and echo `pricing_option_id` + `reporting_period`:
|
|
655
|
-
|
|
656
|
-
```typescript
|
|
657
|
-
reportUsage: async (params, ctx) => {
|
|
658
|
-
const existing = await ctx.store.get('usage_reports', params.idempotency_key);
|
|
659
|
-
if (existing) return existing; // idempotent
|
|
660
|
-
const report = {
|
|
661
|
-
idempotency_key: params.idempotency_key,
|
|
662
|
-
creative_id: params.creative_id,
|
|
663
|
-
pricing_option_id: params.pricing_option_id,
|
|
664
|
-
reporting_period: params.reporting_period,
|
|
665
|
-
billable_amount: { amount: params.impressions * 2.5 / 1000, currency: 'USD' },
|
|
666
|
-
status: 'accepted' as const,
|
|
667
|
-
};
|
|
668
|
-
await ctx.store.put('usage_reports', params.idempotency_key, report);
|
|
669
|
-
return report;
|
|
670
|
-
},
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
**`get_creative_delivery`** returns impressions/spend, optionally broken down by variant and filtered by `media_buy_ids`:
|
|
674
|
-
|
|
675
|
-
```typescript
|
|
676
|
-
getCreativeDelivery: async (params, ctx) => ({
|
|
677
|
-
reporting_period: params.reporting_period,
|
|
678
|
-
currency: 'USD', // required top-level per get-creative-delivery-response.json
|
|
679
|
-
creatives: (params.creative_ids ?? []).map((id) => ({
|
|
680
|
-
creative_id: id,
|
|
681
|
-
impressions: 12500,
|
|
682
|
-
spend: { amount: 31.25, currency: 'USD' },
|
|
683
|
-
by_variant: [],
|
|
684
|
-
})),
|
|
685
|
-
sandbox: true,
|
|
686
|
-
}),
|
|
687
|
-
```
|
|
602
|
+
- **Library shape** — `POST /v1/creatives` writes, `GET /v1/creatives` reads (cursor pagination + multi-id pass-through), `PATCH` mutates. Maps directly onto `CreativeAdServerPlatform.{syncCreatives, listCreatives}`. Library state is owned by the adopter's UI / API ingestion; the comply controller's `seed.creative` is the only test path that writes via the adapter.
|
|
603
|
+
- **Tag generation** — `buildCreative` looks up the creative by id, calls upstream `POST /v1/creatives/{id}/render` for macro substitution against a stored snippet template (`{click_url}`, `{impression_pixel}`, `{cb}`, `{advertiser_id}`, `{creative_id}`, `{asset_url}` …), and returns `BuildCreativeSuccess` with `creative_manifest.assets` carrying the rendered tag (HTML / VAST / native). The mock's `tag_url` points at a real iframe-embeddable `/serve/{id}` endpoint adopters can iframe in storyboards.
|
|
604
|
+
- **`previewCreative`** — `NoAccountCtx<TCtxMeta>` no-account tool; returns the same iframe URL as a `preview_url`. The resolver synthesizes a default-listing network so `ctx.account` resolves cleanly inside the no-account handler. See migration recipe #11.
|
|
605
|
+
- **`listCreativeFormats`** — also `NoAccountCtx`. Project upstream catalog to closed-shape `Format.renders[]` (display fixed dims, video/CTV at 1080p baseline, parameterized with `accepts_parameters[]`) per the typed builders from `@adcp/sdk` (#1325).
|
|
606
|
+
- **Per-creative pricing** — when the adopter bills through AdCP, `listCreatives` with `include_pricing=true` surfaces `pricing_options`, `buildCreative` echoes the applied `pricing_option_id`, and `reportUsage` closes the loop. Adopters that bill out of band (CM360-style flat license / SaaS contract) omit these fields and surface `report_usage` records as `not_accepted` rather than fake-accepting.
|
|
607
|
+
- **`getCreativeDelivery`** — multi-id pass-through per #1342 + #1410; required top-level `currency` + `reporting_period` per the response schema.
|
|
688
608
|
|
|
689
|
-
Output formats returned by `list_creative_formats` for ad servers are **serving-tag formats** (VAST 4.2, display tag HTML, native JSON payload), not input visual formats.
|
|
609
|
+
Output formats returned by `list_creative_formats` for ad servers are **serving-tag formats** (VAST 4.2, display tag HTML, native JSON payload), not input visual formats. Replace the `// SWAP:` markers with calls to your real backend.
|
|
690
610
|
|
|
691
611
|
### <a name="specialism-creative-template"></a>creative-template
|
|
692
612
|
|
|
@@ -23,6 +23,8 @@ A generative seller that sells programmatic inventory MUST also accept standard
|
|
|
23
23
|
- Standalone creative agent (renders but doesn't sell) → creative agent
|
|
24
24
|
- Signals/audience data → `skills/build-signals-agent/`
|
|
25
25
|
|
|
26
|
+
**Often claimed alongside:** [`creative-template`](../build-creative-agent/SKILL.md) — hybrid creative platforms (Celtra, Bannerflow) claim both; template-driven and AI-driven generation share the same creative library. See [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
|
|
27
|
+
|
|
26
28
|
## Specialisms This Skill Covers
|
|
27
29
|
|
|
28
30
|
A generative seller inherits every sales specialism it supports (usually `sales-non-guaranteed`, optionally `sales-catalog-driven`) **plus** `creative-generative`. Declare all three in your `get_adcp_capabilities` response so buyers can filter correctly.
|
|
@@ -349,14 +351,14 @@ Validate with: `adcp storyboard run <agent> deterministic_testing --json`
|
|
|
349
351
|
|
|
350
352
|
## SDK Quick Reference
|
|
351
353
|
|
|
352
|
-
| SDK piece
|
|
353
|
-
|
|
|
354
|
-
| `createAdcpServerFromPlatform(platform, opts)`
|
|
355
|
-
| `createAdcpServer(config)`
|
|
356
|
-
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp`
|
|
357
|
-
| `ctx.store`
|
|
358
|
-
| `adcpError(code, { message })`
|
|
359
|
-
| `registerTestController(server, store)`
|
|
354
|
+
| SDK piece | Usage |
|
|
355
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
356
|
+
| `createAdcpServerFromPlatform(platform, opts)` | Create server from a typed `DecisioningPlatform` — compile-time specialism enforcement, auto-generated capabilities, ctx_metadata round-trip |
|
|
357
|
+
| `createAdcpServer(config)` _(legacy)_ | v5 handler-bag entry. Mid-migration / escape-hatch only; reach via `@adcp/sdk/server/legacy/v5` |
|
|
358
|
+
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp` |
|
|
359
|
+
| `ctx.store` | State persistence — `get/put/patch/delete/list` domain objects |
|
|
360
|
+
| `adcpError(code, { message })` | Structured error |
|
|
361
|
+
| `registerTestController(server, store)` | Add `comply_test_controller` for deterministic testing |
|
|
360
362
|
|
|
361
363
|
Response builders (`productsResponse`, `mediaBuyResponse`, `syncCreativesResponse`, etc.) are auto-applied by the framework. Handlers return raw data objects — the framework wraps them.
|
|
362
364
|
|
|
@@ -477,7 +479,11 @@ class MyGenerativeSeller implements DecisioningPlatform {
|
|
|
477
479
|
buildCreative: async (req, ctx) => {
|
|
478
480
|
// Generative path — read brief, call your model, return assets.
|
|
479
481
|
// Framework auto-stores the build for refine/lookup via ctx_metadata.
|
|
480
|
-
return {
|
|
482
|
+
return {
|
|
483
|
+
creative_manifest: {
|
|
484
|
+
/* generated assets */
|
|
485
|
+
},
|
|
486
|
+
};
|
|
481
487
|
},
|
|
482
488
|
previewCreative: async (req, ctx) => {
|
|
483
489
|
return { preview_url: '...' };
|
|
@@ -21,6 +21,8 @@ A governance agent sits between buyers and sellers, evaluating proposed media bu
|
|
|
21
21
|
- Serving audience segments → `skills/build-signals-agent/`
|
|
22
22
|
- Managing brand identity and licensing → `skills/build-brand-rights-agent/`
|
|
23
23
|
|
|
24
|
+
**Often claimed alongside:** `measurement-verification` _(preview)_ + `content-standards` — governance vendors (IAS, DV) typically claim all three together. See [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
|
|
25
|
+
|
|
24
26
|
## Specialisms This Skill Covers
|
|
25
27
|
|
|
26
28
|
Your compliance obligations come from the specialisms you claim in `get_adcp_capabilities`. Each maps to a storyboard at `compliance/cache/latest/specialisms/<id>/`:
|
|
@@ -460,13 +462,13 @@ Some schemas also define an `ext` field for vendor-namespaced extensions. If you
|
|
|
460
462
|
|
|
461
463
|
## SDK Quick Reference
|
|
462
464
|
|
|
463
|
-
| SDK piece
|
|
464
|
-
|
|
|
465
|
-
| `createAdcpServerFromPlatform(platform, opts)`
|
|
466
|
-
| `createAdcpServer(config)`
|
|
467
|
-
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp`
|
|
468
|
-
| `ctx.store`
|
|
469
|
-
| `adcpError(code, { message })`
|
|
465
|
+
| SDK piece | Usage |
|
|
466
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
467
|
+
| `createAdcpServerFromPlatform(platform, opts)` | Create server from a typed `DecisioningPlatform` — compile-time specialism enforcement, auto-generated capabilities, ctx_metadata round-trip |
|
|
468
|
+
| `createAdcpServer(config)` _(legacy)_ | v5 handler-bag entry. Mid-migration / escape-hatch only; reach via `@adcp/sdk/server/legacy/v5` |
|
|
469
|
+
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp` |
|
|
470
|
+
| `ctx.store` | State persistence — `get/put/patch/delete/list` domain objects |
|
|
471
|
+
| `adcpError(code, { message })` | Structured error |
|
|
470
472
|
|
|
471
473
|
Handlers return raw data objects. The framework auto-wraps responses and auto-generates `get_adcp_capabilities` from registered handlers.
|
|
472
474
|
|
|
@@ -568,9 +570,15 @@ class MyGovernance implements DecisioningPlatform {
|
|
|
568
570
|
|
|
569
571
|
propertyLists: PropertyListsPlatform = {
|
|
570
572
|
listPropertyLists: async (req, ctx) => ({ property_lists: [] }),
|
|
571
|
-
createPropertyList: async (req, ctx) => ({
|
|
572
|
-
|
|
573
|
-
|
|
573
|
+
createPropertyList: async (req, ctx) => ({
|
|
574
|
+
/* ... */
|
|
575
|
+
}),
|
|
576
|
+
updatePropertyList: async (req, ctx) => ({
|
|
577
|
+
/* ... */
|
|
578
|
+
}),
|
|
579
|
+
deletePropertyList: async (req, ctx) => ({
|
|
580
|
+
/* ... */
|
|
581
|
+
}),
|
|
574
582
|
};
|
|
575
583
|
}
|
|
576
584
|
|
|
@@ -611,7 +619,8 @@ const idempotency = createIdempotencyStore({
|
|
|
611
619
|
});
|
|
612
620
|
|
|
613
621
|
const server = createAdcpServerFromPlatform(platform, {
|
|
614
|
-
name: '...',
|
|
622
|
+
name: '...',
|
|
623
|
+
version: '...',
|
|
615
624
|
idempotency,
|
|
616
625
|
// MUST never return undefined — or every mutating request rejects as
|
|
617
626
|
// SERVICE_UNAVAILABLE. A constant works for a demo; production uses
|
|
@@ -21,6 +21,8 @@ A retail media agent sells advertising on a retailer's properties (sponsored pro
|
|
|
21
21
|
- Generative seller (AI creative from briefs) → `skills/build-generative-seller-agent/`
|
|
22
22
|
- Signals/audience data → `skills/build-signals-agent/`
|
|
23
23
|
|
|
24
|
+
**Often claimed alongside:** [`audience-sync`](../build-seller-agent/SKILL.md) (first-party audience push), [`creative-template`](../build-creative-agent/SKILL.md) (dynamic-creative rendering from catalog). Together these form the canonical retail-media bundle — see [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
|
|
25
|
+
|
|
24
26
|
Despite the name, **this skill also covers non-retail catalog-driven sales** — restaurants, travel, local commerce, any platform where the ad unit is rendered from a feed of products/listings/menu items. The compliance storyboard `media_buy_catalog_creative` uses a steakhouse protagonist, not a retailer.
|
|
25
27
|
|
|
26
28
|
## Specialisms This Skill Covers
|
|
@@ -254,14 +256,14 @@ Validate with: `adcp storyboard run <agent> deterministic_testing --json`
|
|
|
254
256
|
|
|
255
257
|
## SDK Quick Reference
|
|
256
258
|
|
|
257
|
-
| SDK piece
|
|
258
|
-
|
|
|
259
|
-
| `createAdcpServerFromPlatform(platform, opts)`
|
|
260
|
-
| `createAdcpServer(config)`
|
|
261
|
-
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp`
|
|
262
|
-
| `ctx.store`
|
|
263
|
-
| `adcpError(code, { message })`
|
|
264
|
-
| `registerTestController(server, store)`
|
|
259
|
+
| SDK piece | Usage |
|
|
260
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
261
|
+
| `createAdcpServerFromPlatform(platform, opts)` | Create server from a typed `DecisioningPlatform` — compile-time specialism enforcement, ctx_metadata round-trip, auto-generated capabilities |
|
|
262
|
+
| `createAdcpServer(config)` _(legacy)_ | v5 handler-bag entry. Mid-migration / escape-hatch only; reach via `@adcp/sdk/server/legacy/v5` |
|
|
263
|
+
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp` |
|
|
264
|
+
| `ctx.store` | State persistence — `get/put/patch/delete/list` domain objects |
|
|
265
|
+
| `adcpError(code, { message })` | Structured error |
|
|
266
|
+
| `registerTestController(server, store)` | Add `comply_test_controller` for deterministic testing |
|
|
265
267
|
|
|
266
268
|
Response builders (`productsResponse`, `mediaBuyResponse`, `deliveryResponse`, etc.) are auto-applied by the framework. Handlers return raw data objects — the framework wraps them.
|
|
267
269
|
|
|
@@ -23,25 +23,27 @@ A seller agent receives briefs from buyers, returns products with pricing, accep
|
|
|
23
23
|
- Serving audience segments → `skills/build-signals-agent/`
|
|
24
24
|
- Rendering creatives from briefs → that's a creative agent
|
|
25
25
|
|
|
26
|
+
**Often claimed alongside:** [`audience-sync`](../build-seller-agent/SKILL.md) (walled-garden social + identity provider patterns), [`signal-marketplace`](../build-signals-agent/SKILL.md) (DSP-side data surface), [`sales-catalog-driven`](../build-retail-media-agent/SKILL.md) (retail-media catalog + dynamic-creative). See [Common multi-specialism bundles](../../examples/README.md#common-multi-specialism-bundles).
|
|
27
|
+
|
|
26
28
|
## <a name="the-baseline-what-every-sales--agent-must-implement"></a>The baseline: what every sales-\* agent MUST implement
|
|
27
29
|
|
|
28
30
|
Every sales-_ specialism (including `sales-social`, `sales-broadcast-tv`, `sales-retail-media`, `sales-catalog-driven`, etc.) is **additive on top of this baseline**. If you claim any `sales-_` specialism, you implement these tools regardless of the specialism-specific deltas below.
|
|
29
31
|
|
|
30
32
|
**Required tools** (tested by the `media_buy_seller` storyboard bundle at `compliance/cache/3.0.0/protocols/media-buy/`):
|
|
31
33
|
|
|
32
|
-
| Tool | Purpose
|
|
33
|
-
| ------------------------ |
|
|
34
|
-
| `get_adcp_capabilities` | Declare protocols + specialisms + features
|
|
35
|
-
| `sync_accounts` | Advertiser onboarding, per-tenant account creation
|
|
36
|
-
| `list_accounts` | Account lookup by brand/operator; buyers listing their accounts on your platform
|
|
37
|
-
| `get_products` | Product catalog discovery from a brief; returns `{ products: [...] }`. **Surface `Product.forecast: DeliveryForecast`** when you have per-targeting estimates — see [`sales-guaranteed`](specialisms/sales-guaranteed.md#forecast-productforecast) and [`sales-social`](specialisms/sales-social.md#planning-surface) for projection patterns.
|
|
38
|
-
| `list_creative_formats` | Formats your agent accepts
|
|
39
|
-
| `create_media_buy` | Accept a campaign with packages, budget, flight dates
|
|
40
|
-
| `update_media_buy` | Bid, budget, status, package mutations over the campaign lifecycle
|
|
41
|
-
| `get_media_buys` | Read campaigns back with full state (status, budget, packages, targeting overlays)
|
|
42
|
-
| `sync_creatives` | Accept creative assets and return per-asset status
|
|
43
|
-
| `list_creatives` | Read the creative library back with pagination
|
|
44
|
-
| `get_media_buy_delivery` | Delivery + spend reporting with `reporting_period`, per-package billing rows
|
|
34
|
+
| Tool | Purpose | `SalesPlatform` method |
|
|
35
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
|
|
36
|
+
| `get_adcp_capabilities` | Declare protocols + specialisms + features | auto (framework) |
|
|
37
|
+
| `sync_accounts` | Advertiser onboarding, per-tenant account creation | `accounts.upsert` |
|
|
38
|
+
| `list_accounts` | Account lookup by brand/operator; buyers listing their accounts on your platform | `accounts.list` |
|
|
39
|
+
| `get_products` | Product catalog discovery from a brief; returns `{ products: [...] }`. **Surface `Product.forecast: DeliveryForecast`** when you have per-targeting estimates — see [`sales-guaranteed`](specialisms/sales-guaranteed.md#forecast-productforecast) and [`sales-social`](specialisms/sales-social.md#planning-surface) for projection patterns. | `sales.getProducts` |
|
|
40
|
+
| `list_creative_formats` | Formats your agent accepts | `sales.listCreativeFormats` |
|
|
41
|
+
| `create_media_buy` | Accept a campaign with packages, budget, flight dates | `sales.createMediaBuy` |
|
|
42
|
+
| `update_media_buy` | Bid, budget, status, package mutations over the campaign lifecycle | `sales.updateMediaBuy` |
|
|
43
|
+
| `get_media_buys` | Read campaigns back with full state (status, budget, packages, targeting overlays) | `sales.getMediaBuys` |
|
|
44
|
+
| `sync_creatives` | Accept creative assets and return per-asset status | `sales.syncCreatives` |
|
|
45
|
+
| `list_creatives` | Read the creative library back with pagination | `sales.listCreatives` |
|
|
46
|
+
| `get_media_buy_delivery` | Delivery + spend reporting with `reporting_period`, per-package billing rows | `sales.getMediaBuyDelivery` |
|
|
45
47
|
|
|
46
48
|
> **`sales-guaranteed` minimum tool surface** — register ALL of these or storyboard scenarios will cascade-skip with `skip_reason: missing_tool`:
|
|
47
49
|
> `get_adcp_capabilities`, `sync_accounts`, `list_accounts`, `get_products`, `list_creative_formats`, `create_media_buy`, `update_media_buy`, `get_media_buys`, `sync_creatives`, `get_media_buy_delivery`
|