@adcp/client 4.23.0 → 4.24.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 (120) hide show
  1. package/README.md +23 -9
  2. package/bin/adcp.js +83 -18
  3. package/dist/lib/index.d.ts +2 -4
  4. package/dist/lib/index.d.ts.map +1 -1
  5. package/dist/lib/index.js +16 -12
  6. package/dist/lib/index.js.map +1 -1
  7. package/dist/lib/server/index.d.ts +5 -1
  8. package/dist/lib/server/index.d.ts.map +1 -1
  9. package/dist/lib/server/index.js +10 -1
  10. package/dist/lib/server/index.js.map +1 -1
  11. package/dist/lib/server/postgres-task-store.d.ts +105 -0
  12. package/dist/lib/server/postgres-task-store.d.ts.map +1 -0
  13. package/dist/lib/server/postgres-task-store.js +267 -0
  14. package/dist/lib/server/postgres-task-store.js.map +1 -0
  15. package/dist/lib/server/responses.d.ts +1 -0
  16. package/dist/lib/server/responses.d.ts.map +1 -1
  17. package/dist/lib/server/responses.js +1 -0
  18. package/dist/lib/server/responses.js.map +1 -1
  19. package/dist/lib/server/test-controller.d.ts +88 -0
  20. package/dist/lib/server/test-controller.d.ts.map +1 -0
  21. package/dist/lib/server/test-controller.js +227 -0
  22. package/dist/lib/server/test-controller.js.map +1 -0
  23. package/dist/lib/testing/agent-tester.d.ts +1 -1
  24. package/dist/lib/testing/agent-tester.d.ts.map +1 -1
  25. package/dist/lib/testing/agent-tester.js +13 -1
  26. package/dist/lib/testing/agent-tester.js.map +1 -1
  27. package/dist/lib/testing/compliance/comply.d.ts +2 -0
  28. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  29. package/dist/lib/testing/compliance/comply.js +76 -1
  30. package/dist/lib/testing/compliance/comply.js.map +1 -1
  31. package/dist/lib/testing/compliance/index.d.ts +1 -1
  32. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  33. package/dist/lib/testing/compliance/platform-storyboards.d.ts.map +1 -1
  34. package/dist/lib/testing/compliance/platform-storyboards.js +2 -0
  35. package/dist/lib/testing/compliance/platform-storyboards.js.map +1 -1
  36. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
  37. package/dist/lib/testing/compliance/storyboard-tracks.js +11 -2
  38. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
  39. package/dist/lib/testing/compliance/types.d.ts +22 -1
  40. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  41. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  42. package/dist/lib/testing/orchestrator.js +5 -1
  43. package/dist/lib/testing/orchestrator.js.map +1 -1
  44. package/dist/lib/testing/scenarios/brand-rights.d.ts +19 -1
  45. package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -1
  46. package/dist/lib/testing/scenarios/brand-rights.js +138 -1
  47. package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
  48. package/dist/lib/testing/scenarios/deterministic.js +7 -7
  49. package/dist/lib/testing/scenarios/deterministic.js.map +1 -1
  50. package/dist/lib/testing/scenarios/index.d.ts +1 -1
  51. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  52. package/dist/lib/testing/scenarios/index.js +4 -2
  53. package/dist/lib/testing/scenarios/index.js.map +1 -1
  54. package/dist/lib/testing/scenarios/media-buy.js +4 -4
  55. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  56. package/dist/lib/testing/storyboard/loader.d.ts +1 -0
  57. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
  58. package/dist/lib/testing/storyboard/loader.js +14 -0
  59. package/dist/lib/testing/storyboard/loader.js.map +1 -1
  60. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
  61. package/dist/lib/testing/storyboard/request-builder.js +88 -11
  62. package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
  63. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  64. package/dist/lib/testing/storyboard/runner.js +83 -5
  65. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  66. package/dist/lib/testing/storyboard/task-map.d.ts +2 -0
  67. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  68. package/dist/lib/testing/storyboard/task-map.js +23 -9
  69. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  70. package/dist/lib/testing/storyboard/types.d.ts +6 -2
  71. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  72. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  73. package/dist/lib/testing/storyboard/validations.js +21 -4
  74. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  75. package/dist/lib/testing/types.d.ts +1 -1
  76. package/dist/lib/testing/types.d.ts.map +1 -1
  77. package/dist/lib/types/core.generated.d.ts +1 -1
  78. package/dist/lib/types/core.generated.d.ts.map +1 -1
  79. package/dist/lib/types/core.generated.js +1 -1
  80. package/dist/lib/types/schemas.generated.d.ts +16 -13
  81. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  82. package/dist/lib/types/schemas.generated.js +7 -4
  83. package/dist/lib/types/schemas.generated.js.map +1 -1
  84. package/dist/lib/types/tools.generated.d.ts +14 -5
  85. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  86. package/dist/lib/utils/capabilities.d.ts +2 -2
  87. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  88. package/dist/lib/utils/capabilities.js +9 -3
  89. package/dist/lib/utils/capabilities.js.map +1 -1
  90. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  91. package/dist/lib/utils/response-schemas.js +9 -0
  92. package/dist/lib/utils/response-schemas.js.map +1 -1
  93. package/docs/llms.txt +10 -2
  94. package/package.json +8 -2
  95. package/skills/adcp/SKILL.md +118 -33
  96. package/skills/build-creative-agent/SKILL.md +221 -0
  97. package/skills/build-generative-seller-agent/SKILL.md +288 -0
  98. package/skills/build-retail-media-agent/SKILL.md +237 -0
  99. package/skills/build-seller-agent/SKILL.md +313 -0
  100. package/skills/build-signals-agent/SKILL.md +203 -0
  101. package/storyboards/campaign_governance_conditions.yaml +2 -2
  102. package/storyboards/campaign_governance_delivery.yaml +1 -1
  103. package/storyboards/campaign_governance_denied.yaml +1 -0
  104. package/storyboards/creative_generative.yaml +317 -0
  105. package/storyboards/creative_sales_agent.yaml +2 -2
  106. package/storyboards/creative_template.yaml +2 -1
  107. package/storyboards/deterministic_testing.yaml +271 -245
  108. package/storyboards/media_buy_catalog_creative.yaml +2 -1
  109. package/storyboards/media_buy_generative_seller.yaml +581 -0
  110. package/storyboards/media_buy_governance_escalation.yaml +1 -1
  111. package/storyboards/media_buy_guaranteed_approval.yaml +14 -14
  112. package/storyboards/media_buy_non_guaranteed.yaml +2 -2
  113. package/storyboards/media_buy_proposal_mode.yaml +5 -5
  114. package/storyboards/media_buy_seller.yaml +10 -10
  115. package/storyboards/media_buy_state_machine.yaml +4 -4
  116. package/storyboards/schema.yaml +2 -1
  117. package/storyboards/signal_marketplace.yaml +3 -0
  118. package/storyboards/signal_owned.yaml +1 -0
  119. package/storyboards/test-kits/acme-outdoor.yaml +118 -0
  120. package/storyboards/test-kits/nova-motors.yaml +134 -0
@@ -0,0 +1,288 @@
1
+ ---
2
+ name: build-generative-seller-agent
3
+ description: Use when building an AdCP generative seller — an AI ad network, generative DSP, or platform that sells inventory AND generates creatives from briefs.
4
+ ---
5
+
6
+ # Build a Generative Seller Agent
7
+
8
+ ## Overview
9
+
10
+ A generative seller does everything a standard seller does (products, media buys, delivery) plus generates creatives from briefs. The buyer sends a creative brief instead of uploading pre-built assets. Your platform resolves the brand identity, generates the creative, and serves it.
11
+
12
+ A generative seller that sells programmatic inventory MUST also accept standard IAB formats (display images, VAST tags, HTML banners). The generative capability is additive — buyers who already have creatives need to upload them directly.
13
+
14
+ ## When to Use
15
+
16
+ - User wants to build a generative DSP or AI ad network
17
+ - User's platform both sells inventory and creates/generates creatives
18
+ - User mentions "creative from brief", "AI-generated ads", or "generative"
19
+
20
+ **Not this skill:**
21
+ - Standard seller (no creative generation) → `skills/build-seller-agent/`
22
+ - Standalone creative agent (renders but doesn't sell) → creative agent
23
+ - Signals/audience data → `skills/build-signals-agent/`
24
+
25
+ ## Before Writing Code
26
+
27
+ Determine these things. Ask the user — don't guess.
28
+
29
+ ### 1. What kind of platform?
30
+
31
+ - **AI ad network** — sells inventory across publishers, generates creatives from briefs
32
+ - **Generative DSP** — programmatic buying + AI creative generation
33
+ - **Retail media with creative** — retail inventory + dynamic ad generation from catalogs
34
+
35
+ ### 2. Products and pricing
36
+
37
+ Same as standard seller. Each product needs: name, channel, delivery_type, pricing_options.
38
+
39
+ ### 3. Generative formats
40
+
41
+ What creative formats does your platform generate?
42
+ - **Display** — generated static images (300x250, 728x90, etc.)
43
+ - **Video** — generated video ads (15s, 30s pre-roll)
44
+ - **HTML** — generated interactive/rich media
45
+
46
+ Each generative format needs a brief asset slot. Standard formats need traditional asset slots (image, video, VAST).
47
+
48
+ ### 4. What inputs does the brief accept?
49
+
50
+ At minimum: `name`, `objective`, `tone`, `messaging` (headline, cta, key_messages).
51
+ Optional: `audience`, `territory`, `compliance` (required_disclosures, prohibited_claims).
52
+
53
+ ### 5. Brand resolution
54
+
55
+ The buyer's brand domain should be resolvable (via AgenticAdvertising.org or brand.json). If the brand domain is invalid, reject the creative — don't generate with unknown brand identity.
56
+
57
+ ## Tools and Required Response Shapes
58
+
59
+ Everything from the standard seller skill applies. The delta is in `list_creative_formats` and `sync_creatives`.
60
+
61
+ **`get_adcp_capabilities`** — register first, empty `{}` schema
62
+ ```
63
+ capabilitiesResponse({
64
+ adcp: { major_versions: [3] },
65
+ supported_protocols: ['media_buy'],
66
+ })
67
+ ```
68
+
69
+ **`sync_accounts`** — `SyncAccountsRequestSchema.shape`
70
+ ```
71
+ taskToolResponse({
72
+ accounts: [{
73
+ account_id: string,
74
+ brand: { domain: string },
75
+ operator: string,
76
+ action: 'created' | 'updated',
77
+ status: 'active' | 'pending_approval',
78
+ }]
79
+ })
80
+ ```
81
+
82
+ **`get_products`** — `GetProductsRequestSchema.shape`
83
+ ```
84
+ productsResponse({
85
+ products: Product[], // each needs product_id, delivery_type, pricing_options
86
+ sandbox: true,
87
+ })
88
+ ```
89
+
90
+ **`create_media_buy`** — `CreateMediaBuyRequestSchema.shape`
91
+ ```
92
+ mediaBuyResponse({
93
+ media_buy_id: string,
94
+ packages: [{ package_id, product_id, pricing_option_id, budget }],
95
+ })
96
+ ```
97
+
98
+ **`list_creative_formats`** — `ListCreativeFormatsRequestSchema.shape`
99
+
100
+ Return BOTH generative and standard formats:
101
+ ```
102
+ taskToolResponse({
103
+ formats: [
104
+ // Generative format — accepts brief input
105
+ {
106
+ format_id: { agent_url: string, id: 'display_300x250_generative' },
107
+ name: 'Generated Display 300x250',
108
+ description: 'AI-generated display ad from creative brief',
109
+ renders: [{ width: 300, height: 250 }],
110
+ assets: [{
111
+ item_type: 'individual',
112
+ asset_id: 'brief',
113
+ asset_type: 'brief',
114
+ required: true,
115
+ description: 'Creative brief with messaging and brand guidelines',
116
+ }],
117
+ },
118
+ // Standard format — accepts pre-built assets
119
+ {
120
+ format_id: { agent_url: string, id: 'display_300x250' },
121
+ name: 'Display 300x250',
122
+ description: 'Standard IAB display banner',
123
+ renders: [{ width: 300, height: 250 }],
124
+ assets: [{
125
+ item_type: 'individual',
126
+ asset_id: 'image',
127
+ asset_type: 'image',
128
+ required: true,
129
+ accepted_media_types: ['image/jpeg', 'image/png'],
130
+ }],
131
+ },
132
+ ],
133
+ })
134
+ ```
135
+
136
+ **`sync_creatives`** — `SyncCreativesRequestSchema.shape`
137
+
138
+ Handle both brief-based and standard creatives:
139
+ ```
140
+ taskToolResponse({
141
+ creatives: [{
142
+ creative_id: string, // echo from request
143
+ action: 'created' | 'updated', // required
144
+ status: 'accepted' | 'pending_review', // pending_review if generation is async
145
+ }],
146
+ })
147
+ ```
148
+
149
+ For invalid brand domains, return rejection:
150
+ ```
151
+ taskToolResponse({
152
+ creatives: [{
153
+ creative_id: string,
154
+ action: 'created',
155
+ status: 'rejected',
156
+ errors: ['Brand domain not found: nonexistent-brand.example'],
157
+ }],
158
+ })
159
+ ```
160
+
161
+ **`get_media_buys`** — `GetMediaBuysRequestSchema.shape`
162
+ ```
163
+ taskToolResponse({
164
+ media_buys: [{
165
+ media_buy_id: string,
166
+ status: 'active' | 'pending_start' | ...,
167
+ currency: 'USD',
168
+ packages: [{ package_id: string }],
169
+ }]
170
+ })
171
+ ```
172
+
173
+ **`get_media_buy_delivery`** — `GetMediaBuyDeliveryRequestSchema.shape`
174
+ ```
175
+ deliveryResponse({
176
+ reporting_period: { start: string, end: string },
177
+ media_buy_deliveries: [{
178
+ media_buy_id: string,
179
+ status: 'active',
180
+ totals: { impressions: number, spend: number },
181
+ by_package: [],
182
+ }]
183
+ })
184
+ ```
185
+
186
+ ## Compliance Testing (Optional)
187
+
188
+ Add `registerTestController` so the comply framework can deterministically test your state machines. One function call — the SDK handles request parsing, status validation, and response formatting.
189
+
190
+ ```
191
+ import { registerTestController, TestControllerError } from '@adcp/client';
192
+ import type { TestControllerStore } from '@adcp/client';
193
+
194
+ const store: TestControllerStore = {
195
+ async forceAccountStatus(accountId, status) {
196
+ const prev = accounts.get(accountId);
197
+ if (!prev) throw new TestControllerError('NOT_FOUND', `Account ${accountId} not found`);
198
+ accounts.set(accountId, status);
199
+ return { success: true, previous_state: prev, current_state: status };
200
+ },
201
+ async forceMediaBuyStatus(mediaBuyId, status) { /* same pattern */ },
202
+ async forceCreativeStatus(creativeId, status) { /* same pattern */ },
203
+ // simulateDelivery, simulateBudgetSpend — implement as needed
204
+ };
205
+
206
+ registerTestController(server, store);
207
+ ```
208
+
209
+ Declare `compliance_testing` in `supported_protocols` in your `get_adcp_capabilities` response. Only implement the store methods for scenarios your agent supports — unimplemented methods are excluded from `list_scenarios` automatically.
210
+
211
+ Validate with: `adcp storyboard run <agent> deterministic_testing --json`
212
+
213
+ ## SDK Quick Reference
214
+
215
+ | SDK piece | Usage |
216
+ |-----------|-------|
217
+ | `serve(createAgent)` | Start HTTP server on `:3001/mcp` |
218
+ | `createTaskCapableServer(name, version, { taskStore })` | Create MCP server with task support |
219
+ | `server.tool(name, Schema.shape, handler)` | Register tool — `.shape` unwraps Zod |
220
+ | `capabilitiesResponse(data)` | Build `get_adcp_capabilities` response |
221
+ | `productsResponse(data)` | Build `get_products` response |
222
+ | `mediaBuyResponse(data)` | Build `create_media_buy` response |
223
+ | `deliveryResponse(data)` | Build `get_media_buy_delivery` response |
224
+ | `taskToolResponse(data, summary)` | Build generic tool response |
225
+ | `adcpError(code, { message })` | Structured error |
226
+ | `registerTestController(server, store)` | Add `comply_test_controller` for deterministic testing |
227
+
228
+ Import everything from `@adcp/client`. Types from `@adcp/client` with `import type`.
229
+
230
+ ## Implementation
231
+
232
+ 1. Single `.ts` file — all tools in one file
233
+ 2. Always register `get_adcp_capabilities` as the **first** tool with empty `{}` schema
234
+ 3. Use `Schema.shape` (not `Schema`) when registering tools
235
+ 4. Use response builders — never return raw JSON
236
+ 5. Set `sandbox: true` on all mock/demo responses
237
+ 6. Use `ServeContext` pattern: `function createAgent({ taskStore }: ServeContext)`
238
+
239
+ The skill contains everything you need. Do not read additional docs before writing code.
240
+
241
+ ### Key implementation detail: sync_creatives handler
242
+
243
+ The sync_creatives handler must check the format_id to decide how to process:
244
+ - If the format is generative (e.g., id contains "generative"): read the `brief` asset from the creative's assets
245
+ - If the format is standard: read the image/video/html asset
246
+ - Validate the brand domain from the account — reject if invalid
247
+ - Return `pending_review` for generative (async generation) or `accepted` for standard
248
+
249
+ ## Validation
250
+
251
+ **After writing the agent, validate it. Fix failures. Repeat.**
252
+
253
+ **Full validation** (if you can bind ports):
254
+ ```bash
255
+ npx tsx agent.ts &
256
+ npx @adcp/client storyboard run http://localhost:3001/mcp media_buy_generative_seller --json
257
+ ```
258
+
259
+ **Sandbox validation** (if ports are blocked):
260
+ ```bash
261
+ npx tsc --noEmit agent.ts
262
+ ```
263
+
264
+ When storyboard output shows failures, fix each one:
265
+ - `response_schema` → response doesn't match Zod schema
266
+ - `field_present` → required field missing
267
+ - MCP error → check tool registration (schema, name)
268
+
269
+ **Keep iterating until all steps pass.**
270
+
271
+ ## Common Mistakes
272
+
273
+ | Mistake | Fix |
274
+ |---------|-----|
275
+ | Only generative formats, no standard IAB | Programmatic sellers must accept pre-built assets too |
276
+ | Ignore brand domain on brief sync | Validate brand, reject if unresolvable |
277
+ | Same handler for brief and standard creatives | Check format_id to decide processing path |
278
+ | Skip `get_adcp_capabilities` | Must be the first tool registered |
279
+ | Pass `Schema` instead of `Schema.shape` | MCP SDK needs unwrapped Zod fields |
280
+ | `sandbox: false` on mock data | Buyers may treat mock data as real |
281
+
282
+ ## Reference
283
+
284
+ - `storyboards/media_buy_generative_seller.yaml` — full generative seller storyboard
285
+ - `storyboards/media_buy_seller.yaml` — base seller storyboard (for standard seller parts)
286
+ - `skills/build-seller-agent/SKILL.md` — standard seller skill (generative extends this)
287
+ - `docs/guides/BUILD-AN-AGENT.md` — SDK patterns
288
+ - `docs/llms.txt` — full protocol reference
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: build-retail-media-agent
3
+ description: Use when building an AdCP retail media network agent — a platform that sells on-site placements, supports product catalogs, tracks conversions, and reports performance.
4
+ ---
5
+
6
+ # Build a Retail Media Agent
7
+
8
+ ## Overview
9
+
10
+ A retail media agent sells advertising on a retailer's properties (sponsored products, homepage banners, search results). It extends the standard seller with catalog sync, event tracking, and performance feedback. Buyers sync product catalogs, the platform renders dynamic ads from the feed, and conversion data flows back for optimization.
11
+
12
+ ## When to Use
13
+
14
+ - User wants to build a retail media network, commerce media platform, or sponsored products agent
15
+ - User mentions catalogs, product feeds, conversion tracking, or performance feedback
16
+ - User references `sync_catalogs`, `log_event`, or `provide_performance_feedback`
17
+
18
+ **Not this skill:**
19
+ - Standard seller without catalogs → `skills/build-seller-agent/`
20
+ - Generative seller (AI creative from briefs) → `skills/build-generative-seller-agent/`
21
+ - Signals/audience data → `skills/build-signals-agent/`
22
+
23
+ ## Before Writing Code
24
+
25
+ Same domain decisions as the seller skill, plus:
26
+
27
+ ### 1. Products and pricing
28
+ Same as seller. Get specific inventory: names, channels, pricing model, min spend.
29
+
30
+ ### 2. Catalog support
31
+ What product catalogs does the platform accept?
32
+ - Feed format: JSON, CSV, XML
33
+ - What fields: product_id, title, price, image_url, category
34
+ - How does the catalog connect to ad rendering?
35
+
36
+ ### 3. Event tracking
37
+ What conversion events does the platform track?
38
+ - Purchase, add_to_cart, page_view, search
39
+ - How are events attributed to catalog items?
40
+
41
+ ### 4. Performance feedback
42
+ Does the buyer send performance metrics back for optimization?
43
+
44
+ ## Tools and Required Response Shapes
45
+
46
+ All standard seller tools apply (see `skills/build-seller-agent/SKILL.md`). The additional tools:
47
+
48
+ **`get_adcp_capabilities`** — register first, empty `{}` schema
49
+ ```
50
+ capabilitiesResponse({
51
+ adcp: { major_versions: [3] },
52
+ supported_protocols: ['media_buy'],
53
+ })
54
+ ```
55
+
56
+ **`sync_accounts`** — `SyncAccountsRequestSchema.shape`
57
+ ```
58
+ taskToolResponse({
59
+ accounts: [{
60
+ account_id: string,
61
+ brand: { domain: string },
62
+ operator: string,
63
+ action: 'created' | 'updated',
64
+ status: 'active' | 'pending_approval',
65
+ }]
66
+ })
67
+ ```
68
+
69
+ **`get_products`** — `GetProductsRequestSchema.shape`
70
+ ```
71
+ productsResponse({ products: Product[], sandbox: true })
72
+ ```
73
+
74
+ **`create_media_buy`** — `CreateMediaBuyRequestSchema.shape`
75
+ ```
76
+ mediaBuyResponse({
77
+ media_buy_id: string,
78
+ packages: [{ package_id, product_id, pricing_option_id, budget }],
79
+ })
80
+ ```
81
+
82
+ **`list_creative_formats`** — `ListCreativeFormatsRequestSchema.shape`
83
+ ```
84
+ taskToolResponse({
85
+ formats: [{
86
+ format_id: { agent_url: string, id: string },
87
+ name: string,
88
+ }]
89
+ })
90
+ ```
91
+
92
+ **`sync_catalogs`** — `SyncCatalogsRequestSchema.shape`
93
+
94
+ Accept product catalog feeds. Return per-catalog status with item counts.
95
+ ```
96
+ taskToolResponse({
97
+ catalogs: [{
98
+ catalog_id: string, // required — echo from request
99
+ action: 'created' | 'updated', // required
100
+ item_count: number, // total items in catalog
101
+ items_approved: number, // items that passed validation
102
+ }],
103
+ sandbox: true,
104
+ })
105
+ ```
106
+
107
+ **`sync_event_sources`** — `SyncEventSourcesRequestSchema.shape`
108
+
109
+ Register event tracking integrations.
110
+ ```
111
+ taskToolResponse({
112
+ event_sources: [{
113
+ event_source_id: string, // required — echo from request
114
+ action: 'created' | 'updated', // required
115
+ }],
116
+ sandbox: true,
117
+ })
118
+ ```
119
+
120
+ **`log_event`** — `LogEventRequestSchema.shape`
121
+
122
+ Accept conversion events.
123
+ ```
124
+ taskToolResponse({
125
+ events_received: number, // required — how many events in the request
126
+ events_processed: number, // required — how many were successfully processed
127
+ sandbox: true,
128
+ })
129
+ ```
130
+
131
+ **`provide_performance_feedback`** — `ProvidePerformanceFeedbackRequestSchema.shape`
132
+
133
+ Accept performance metrics from the buyer.
134
+ ```
135
+ taskToolResponse({
136
+ success: true,
137
+ sandbox: true,
138
+ })
139
+ ```
140
+
141
+ **`get_media_buy_delivery`** — `GetMediaBuyDeliveryRequestSchema.shape`
142
+ ```
143
+ deliveryResponse({
144
+ reporting_period: { start: string, end: string },
145
+ media_buy_deliveries: [{
146
+ media_buy_id: string,
147
+ status: 'active',
148
+ totals: { impressions: number, spend: number },
149
+ by_package: [],
150
+ }]
151
+ })
152
+ ```
153
+
154
+ ## Compliance Testing (Optional)
155
+
156
+ Add `registerTestController` so the comply framework can deterministically test your state machines. One function call — the SDK handles request parsing, status validation, and response formatting.
157
+
158
+ ```
159
+ import { registerTestController, TestControllerError } from '@adcp/client';
160
+ import type { TestControllerStore } from '@adcp/client';
161
+
162
+ const store: TestControllerStore = {
163
+ async forceAccountStatus(accountId, status) {
164
+ const prev = accounts.get(accountId);
165
+ if (!prev) throw new TestControllerError('NOT_FOUND', `Account ${accountId} not found`);
166
+ accounts.set(accountId, status);
167
+ return { success: true, previous_state: prev, current_state: status };
168
+ },
169
+ async forceMediaBuyStatus(mediaBuyId, status) { /* same pattern */ },
170
+ async forceCreativeStatus(creativeId, status) { /* same pattern */ },
171
+ // simulateDelivery, simulateBudgetSpend — implement as needed
172
+ };
173
+
174
+ registerTestController(server, store);
175
+ ```
176
+
177
+ Declare `compliance_testing` in `supported_protocols` in your `get_adcp_capabilities` response. Only implement the store methods for scenarios your agent supports — unimplemented methods are excluded from `list_scenarios` automatically.
178
+
179
+ Validate with: `adcp storyboard run <agent> deterministic_testing --json`
180
+
181
+ ## SDK Quick Reference
182
+
183
+ | SDK piece | Usage |
184
+ |-----------|-------|
185
+ | `serve(createAgent)` | Start HTTP server on `:3001/mcp` |
186
+ | `createTaskCapableServer(name, version, { taskStore })` | Create MCP server with task support |
187
+ | `server.tool(name, Schema.shape, handler)` | Register tool — `.shape` unwraps Zod |
188
+ | `capabilitiesResponse(data)` | Build `get_adcp_capabilities` response |
189
+ | `productsResponse(data)` | Build `get_products` response |
190
+ | `mediaBuyResponse(data)` | Build `create_media_buy` response |
191
+ | `deliveryResponse(data)` | Build `get_media_buy_delivery` response |
192
+ | `taskToolResponse(data, summary)` | Build generic tool response |
193
+ | `adcpError(code, { message })` | Structured error |
194
+ | `registerTestController(server, store)` | Add `comply_test_controller` for deterministic testing |
195
+
196
+ Schemas: `GetProductsRequestSchema`, `CreateMediaBuyRequestSchema`, `GetMediaBuyDeliveryRequestSchema`, `SyncAccountsRequestSchema`, `ListCreativeFormatsRequestSchema`, `SyncCatalogsRequestSchema`, `SyncEventSourcesRequestSchema`, `LogEventRequestSchema`, `ProvidePerformanceFeedbackRequestSchema`.
197
+
198
+ Import everything from `@adcp/client`. Types from `@adcp/client` with `import type`.
199
+
200
+ ## Implementation
201
+
202
+ 1. Single `.ts` file — all tools in one file
203
+ 2. Always register `get_adcp_capabilities` as the **first** tool with empty `{}` schema
204
+ 3. Use `Schema.shape` (not `Schema`) when registering tools
205
+ 4. Use response builders — never return raw JSON
206
+ 5. Set `sandbox: true` on all mock/demo responses
207
+ 6. Use `ServeContext` pattern: `function createAgent({ taskStore }: ServeContext)`
208
+
209
+ The skill contains everything you need. Do not read additional docs before writing code.
210
+
211
+ ## Validation
212
+
213
+ **After writing the agent, validate it. Fix failures. Repeat.**
214
+
215
+ ```bash
216
+ npx tsx agent.ts &
217
+ npx @adcp/client storyboard run http://localhost:3001/mcp media_buy_catalog_creative --json
218
+ ```
219
+
220
+ **Keep iterating until all steps pass.**
221
+
222
+ ## Common Mistakes
223
+
224
+ | Mistake | Fix |
225
+ |---------|-----|
226
+ | Skip `get_adcp_capabilities` | Must be the first tool registered |
227
+ | Pass `Schema` instead of `Schema.shape` | MCP SDK needs unwrapped Zod fields |
228
+ | sync_catalogs missing `item_count` / `items_approved` | Required fields for catalog validation results |
229
+ | log_event missing `events_received` / `events_processed` | Required counters |
230
+ | `sandbox: false` on mock data | Buyers may treat mock data as real |
231
+
232
+ ## Reference
233
+
234
+ - `skills/build-seller-agent/SKILL.md` — base seller skill (retail media extends this)
235
+ - `storyboards/media_buy_catalog_creative.yaml` — full catalog creative storyboard
236
+ - `docs/guides/BUILD-AN-AGENT.md` — SDK patterns
237
+ - `docs/llms.txt` — full protocol reference