@adcp/client 4.22.1 → 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 (144) hide show
  1. package/README.md +23 -9
  2. package/bin/adcp.js +83 -18
  3. package/dist/lib/index.d.ts +3 -5
  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 +24 -5
  28. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  29. package/dist/lib/testing/compliance/comply.js +318 -277
  30. package/dist/lib/testing/compliance/comply.js.map +1 -1
  31. package/dist/lib/testing/compliance/index.d.ts +2 -1
  32. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  33. package/dist/lib/testing/compliance/index.js +6 -1
  34. package/dist/lib/testing/compliance/index.js.map +1 -1
  35. package/dist/lib/testing/compliance/platform-storyboards.d.ts +44 -0
  36. package/dist/lib/testing/compliance/platform-storyboards.d.ts.map +1 -0
  37. package/dist/lib/testing/compliance/platform-storyboards.js +232 -0
  38. package/dist/lib/testing/compliance/platform-storyboards.js.map +1 -0
  39. package/dist/lib/testing/compliance/storyboard-tracks.d.ts +2 -9
  40. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
  41. package/dist/lib/testing/compliance/storyboard-tracks.js +15 -46
  42. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
  43. package/dist/lib/testing/compliance/types.d.ts +22 -1
  44. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  45. package/dist/lib/testing/index.d.ts +1 -1
  46. package/dist/lib/testing/index.d.ts.map +1 -1
  47. package/dist/lib/testing/index.js +6 -1
  48. package/dist/lib/testing/index.js.map +1 -1
  49. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  50. package/dist/lib/testing/orchestrator.js +5 -1
  51. package/dist/lib/testing/orchestrator.js.map +1 -1
  52. package/dist/lib/testing/scenarios/brand-rights.d.ts +19 -1
  53. package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -1
  54. package/dist/lib/testing/scenarios/brand-rights.js +138 -1
  55. package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
  56. package/dist/lib/testing/scenarios/deterministic.js +7 -7
  57. package/dist/lib/testing/scenarios/deterministic.js.map +1 -1
  58. package/dist/lib/testing/scenarios/index.d.ts +1 -1
  59. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  60. package/dist/lib/testing/scenarios/index.js +4 -2
  61. package/dist/lib/testing/scenarios/index.js.map +1 -1
  62. package/dist/lib/testing/scenarios/media-buy.js +4 -4
  63. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  64. package/dist/lib/testing/storyboard/loader.d.ts +1 -0
  65. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
  66. package/dist/lib/testing/storyboard/loader.js +14 -0
  67. package/dist/lib/testing/storyboard/loader.js.map +1 -1
  68. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
  69. package/dist/lib/testing/storyboard/request-builder.js +88 -11
  70. package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
  71. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  72. package/dist/lib/testing/storyboard/runner.js +83 -5
  73. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  74. package/dist/lib/testing/storyboard/task-map.d.ts +2 -0
  75. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  76. package/dist/lib/testing/storyboard/task-map.js +23 -9
  77. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  78. package/dist/lib/testing/storyboard/types.d.ts +6 -2
  79. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  80. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  81. package/dist/lib/testing/storyboard/validations.js +21 -4
  82. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  83. package/dist/lib/testing/types.d.ts +1 -1
  84. package/dist/lib/testing/types.d.ts.map +1 -1
  85. package/dist/lib/types/core.generated.d.ts +242 -3
  86. package/dist/lib/types/core.generated.d.ts.map +1 -1
  87. package/dist/lib/types/core.generated.js +1 -1
  88. package/dist/lib/types/schemas.generated.d.ts +3697 -3468
  89. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  90. package/dist/lib/types/schemas.generated.js +226 -118
  91. package/dist/lib/types/schemas.generated.js.map +1 -1
  92. package/dist/lib/types/tools.generated.d.ts +281 -79
  93. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  94. package/dist/lib/utils/capabilities.d.ts +2 -2
  95. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  96. package/dist/lib/utils/capabilities.js +9 -3
  97. package/dist/lib/utils/capabilities.js.map +1 -1
  98. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  99. package/dist/lib/utils/response-schemas.js +9 -0
  100. package/dist/lib/utils/response-schemas.js.map +1 -1
  101. package/dist/lib/version.d.ts +3 -3
  102. package/dist/lib/version.js +3 -3
  103. package/docs/llms.txt +56 -32
  104. package/package.json +8 -2
  105. package/skills/adcp/SKILL.md +118 -33
  106. package/skills/build-creative-agent/SKILL.md +221 -0
  107. package/skills/build-generative-seller-agent/SKILL.md +288 -0
  108. package/skills/build-retail-media-agent/SKILL.md +237 -0
  109. package/skills/build-seller-agent/SKILL.md +313 -0
  110. package/skills/build-signals-agent/SKILL.md +203 -0
  111. package/storyboards/audience_sync.yaml +18 -29
  112. package/storyboards/behavioral_analysis.yaml +40 -72
  113. package/storyboards/brand_rights.yaml +172 -75
  114. package/storyboards/campaign_governance_conditions.yaml +187 -0
  115. package/storyboards/campaign_governance_delivery.yaml +231 -0
  116. package/storyboards/campaign_governance_denied.yaml +136 -0
  117. package/storyboards/capability_discovery.yaml +106 -0
  118. package/storyboards/content_standards.yaml +251 -0
  119. package/storyboards/creative_ad_server.yaml +108 -16
  120. package/storyboards/creative_generative.yaml +317 -0
  121. package/storyboards/creative_lifecycle.yaml +284 -0
  122. package/storyboards/creative_sales_agent.yaml +2 -6
  123. package/storyboards/creative_template.yaml +3 -6
  124. package/storyboards/deterministic_testing.yaml +271 -245
  125. package/storyboards/error_compliance.yaml +105 -108
  126. package/storyboards/media_buy_catalog_creative.yaml +8 -5
  127. package/storyboards/media_buy_generative_seller.yaml +581 -0
  128. package/storyboards/media_buy_governance_escalation.yaml +10 -6
  129. package/storyboards/media_buy_guaranteed_approval.yaml +21 -19
  130. package/storyboards/media_buy_non_guaranteed.yaml +9 -8
  131. package/storyboards/media_buy_proposal_mode.yaml +12 -11
  132. package/storyboards/media_buy_seller.yaml +161 -173
  133. package/storyboards/media_buy_state_machine.yaml +102 -101
  134. package/storyboards/property_governance.yaml +239 -0
  135. package/storyboards/schema.yaml +3 -2
  136. package/storyboards/schema_validation.yaml +58 -51
  137. package/storyboards/si_session.yaml +99 -317
  138. package/storyboards/signal_marketplace.yaml +9 -5
  139. package/storyboards/signal_owned.yaml +6 -5
  140. package/storyboards/social_platform.yaml +274 -0
  141. package/storyboards/test-kits/acme-outdoor.yaml +118 -0
  142. package/storyboards/test-kits/nova-motors.yaml +134 -0
  143. package/storyboards/governance_content_standards.yaml +0 -213
  144. package/storyboards/governance_property_lists.yaml +0 -372
@@ -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
@@ -0,0 +1,313 @@
1
+ ---
2
+ name: build-seller-agent
3
+ description: Use when building an AdCP seller agent — a publisher, SSP, or retail media network that sells advertising inventory to buyer agents.
4
+ ---
5
+
6
+ # Build a Seller Agent
7
+
8
+ ## Overview
9
+
10
+ A seller agent receives briefs from buyers, returns products with pricing, accepts media buys, manages creatives, and reports delivery. The business model — what you sell, how you price it, and whether humans approve deals — shapes every implementation decision. Determine that first.
11
+
12
+ ## When to Use
13
+
14
+ - User wants to build an agent that sells ad inventory
15
+ - User mentions publisher, SSP, retail media, or media network in the context of AdCP
16
+ - User references `get_products`, `create_media_buy`, or the media buy protocol
17
+
18
+ **Not this skill:**
19
+ - Buying ad inventory → that's a buyer/DSP agent (see `docs/getting-started.md`)
20
+ - Serving audience segments → `skills/build-signals-agent/`
21
+ - Rendering creatives from briefs → that's a creative agent
22
+
23
+ ## Before Writing Code
24
+
25
+ Determine these five things. Ask the user — don't guess.
26
+
27
+ ### 1. What Kind of Seller?
28
+
29
+ - **Premium publisher** — guaranteed inventory, fixed pricing, IO approval (ESPN, NYT)
30
+ - **SSP / Exchange** — non-guaranteed, auction-based, instant activation
31
+ - **Retail media network** — both guaranteed and non-guaranteed, proposals, catalog-driven creative, conversion tracking
32
+
33
+ ### 2. Guaranteed or Non-Guaranteed?
34
+
35
+ - **Guaranteed** — `delivery_type: "guaranteed"`, may require async approval (`submitted` → `pending_approval` → `confirmed`)
36
+ - **Non-guaranteed** — `delivery_type: "non_guaranteed"`, buyer sets `bid_price`, instant activation
37
+
38
+ Many sellers support both — different products can have different delivery types.
39
+
40
+ ### 3. Products and Pricing
41
+
42
+ Get specific inventory. Each product needs:
43
+ - Name and description
44
+ - Channel: `display`, `olv`, `ctv`, `social`, `retail_media`, `dooh`, etc.
45
+ - Creative format requirements
46
+ - At least one pricing option
47
+
48
+ Pricing models:
49
+ - `cpm` — `{ pricing_model: "cpm", fixed_price: 12.00, currency: "USD" }`
50
+ - `cpc` — `{ pricing_model: "cpc", fixed_price: 1.50, currency: "USD" }`
51
+ - Auction — `{ pricing_model: "cpm", floor_price: 5.00, currency: "USD" }` (buyer bids above floor)
52
+
53
+ Each pricing option can set `min_spend_per_package` to enforce minimum budgets.
54
+
55
+ ### 4. Approval Workflow
56
+
57
+ For guaranteed buys, choose one:
58
+ - **Instant confirmation** — `create_media_buy` returns completed with confirmed status. Simplest.
59
+ - **Async approval** — returns `submitted`, buyer polls `get_media_buys`. Use `registerAdcpTaskTool`.
60
+ - **Human-in-the-loop** — returns `input-required` with a setup URL for IO signing.
61
+
62
+ Non-guaranteed buys are always instant confirmation.
63
+
64
+ ### 5. Creative Management
65
+
66
+ - **Standard** — `list_creative_formats` + `sync_creatives`. Buyer uploads assets, seller validates.
67
+ - **Catalog-driven** — buyer syncs product catalog via `sync_catalogs`. Common for retail media.
68
+ - **None** — creative handled out-of-band. Omit creative tools.
69
+
70
+ ## Tools and Required Response Shapes
71
+
72
+ **`get_adcp_capabilities`** — register first, empty `{}` schema
73
+ ```
74
+ capabilitiesResponse({
75
+ adcp: { major_versions: [3] },
76
+ supported_protocols: ['media_buy'],
77
+ })
78
+ ```
79
+
80
+ **`sync_accounts`** — `SyncAccountsRequestSchema.shape`
81
+ ```
82
+ taskToolResponse({
83
+ accounts: [{
84
+ account_id: string, // required - your platform's ID
85
+ brand: { domain: string },// required - echo back from request
86
+ operator: string, // required - echo back from request
87
+ action: 'created' | 'updated', // required
88
+ status: 'active' | 'pending_approval', // required
89
+ }]
90
+ })
91
+ ```
92
+
93
+ **`sync_governance`** — `SyncGovernanceRequestSchema.shape`
94
+ ```
95
+ taskToolResponse({
96
+ accounts: [{
97
+ account: { brand: {...}, operator: string }, // required - echo back
98
+ status: 'synced', // required
99
+ governance_agents: [{ url: string, categories?: string[] }], // required
100
+ }]
101
+ })
102
+ ```
103
+
104
+ **`get_products`** — `GetProductsRequestSchema.shape`
105
+ ```
106
+ productsResponse({
107
+ products: Product[], // required - each needs product_id, delivery_type, pricing_options
108
+ sandbox: true, // for mock data
109
+ })
110
+ ```
111
+
112
+ **`create_media_buy`** — `CreateMediaBuyRequestSchema.shape`
113
+ ```
114
+ mediaBuyResponse({
115
+ media_buy_id: string, // required
116
+ packages: [{ // required
117
+ package_id: string,
118
+ product_id: string,
119
+ pricing_option_id: string,
120
+ budget: number,
121
+ }],
122
+ })
123
+ ```
124
+
125
+ **`get_media_buys`** — `GetMediaBuysRequestSchema.shape`
126
+ ```
127
+ taskToolResponse({
128
+ media_buys: [{
129
+ media_buy_id: string, // required
130
+ status: 'active' | 'pending_start' | ..., // required
131
+ currency: 'USD', // required
132
+ packages: [{
133
+ package_id: string, // required
134
+ }],
135
+ }]
136
+ })
137
+ ```
138
+
139
+ **`list_creative_formats`** — `ListCreativeFormatsRequestSchema.shape`
140
+ ```
141
+ taskToolResponse({
142
+ formats: [{
143
+ format_id: { agent_url: string, id: string }, // required
144
+ name: string, // required
145
+ }]
146
+ })
147
+ ```
148
+
149
+ **`sync_creatives`** — `SyncCreativesRequestSchema.shape`
150
+ ```
151
+ taskToolResponse({
152
+ creatives: [{
153
+ creative_id: string, // required - echo from request
154
+ action: 'created' | 'updated', // required
155
+ }]
156
+ })
157
+ ```
158
+
159
+ **`get_media_buy_delivery`** — `GetMediaBuyDeliveryRequestSchema.shape`
160
+ ```
161
+ deliveryResponse({
162
+ reporting_period: { start: string, end: string }, // required - ISO timestamps
163
+ media_buy_deliveries: [{
164
+ media_buy_id: string, // required
165
+ status: 'active', // required
166
+ totals: { impressions: number, spend: number }, // required
167
+ by_package: [], // required (can be empty)
168
+ }]
169
+ })
170
+ ```
171
+
172
+ ## Compliance Testing (Optional)
173
+
174
+ Add `registerTestController` so the comply framework can deterministically test your state machines. Without it, compliance testing relies on observational storyboards that can't force state transitions.
175
+
176
+ ```
177
+ import { registerTestController } from '@adcp/client';
178
+ import type { TestControllerStore } from '@adcp/client';
179
+
180
+ const store: TestControllerStore = {
181
+ async forceAccountStatus(accountId, status) {
182
+ const prev = accounts.get(accountId);
183
+ if (!prev) throw new TestControllerError('NOT_FOUND', `Account ${accountId} not found`);
184
+ accounts.set(accountId, status);
185
+ return { success: true, previous_state: prev, current_state: status };
186
+ },
187
+ async forceMediaBuyStatus(mediaBuyId, status) {
188
+ const prev = mediaBuys.get(mediaBuyId);
189
+ if (!prev) throw new TestControllerError('NOT_FOUND', `Media buy ${mediaBuyId} not found`);
190
+ const terminal = ['completed', 'rejected', 'canceled'];
191
+ if (terminal.includes(prev))
192
+ throw new TestControllerError('INVALID_TRANSITION', `Cannot transition from ${prev}`, prev);
193
+ mediaBuys.set(mediaBuyId, status);
194
+ return { success: true, previous_state: prev, current_state: status };
195
+ },
196
+ async forceCreativeStatus(creativeId, status, rejectionReason) {
197
+ const prev = creatives.get(creativeId);
198
+ if (!prev) throw new TestControllerError('NOT_FOUND', `Creative ${creativeId} not found`);
199
+ if (prev === 'archived')
200
+ throw new TestControllerError('INVALID_TRANSITION', `Cannot transition from archived`, prev);
201
+ creatives.set(creativeId, status);
202
+ return { success: true, previous_state: prev, current_state: status };
203
+ },
204
+ async simulateDelivery(mediaBuyId, params) {
205
+ // Accumulate delivery data and return simulated + cumulative totals
206
+ return { success: true, simulated: { ...params }, cumulative: { ...params } };
207
+ },
208
+ async simulateBudgetSpend(params) {
209
+ return { success: true, simulated: { spend_percentage: params.spend_percentage } };
210
+ },
211
+ };
212
+
213
+ registerTestController(server, store);
214
+ ```
215
+
216
+ When using this, declare `compliance_testing` in `supported_protocols`:
217
+ ```
218
+ capabilitiesResponse({
219
+ adcp: { major_versions: [3] },
220
+ supported_protocols: ['media_buy', 'compliance_testing'],
221
+ })
222
+ ```
223
+
224
+ Only implement the store methods for scenarios your agent supports. Unimplemented methods are excluded from `list_scenarios` automatically.
225
+
226
+ The storyboard tests state machine correctness:
227
+ - `NOT_FOUND` when forcing transitions on unknown entities
228
+ - `INVALID_TRANSITION` when transitioning from terminal states (completed, rejected, canceled for media buys; archived for creatives)
229
+ - Successful transitions between valid states
230
+
231
+ Throw `TestControllerError` from store methods for typed errors. The SDK validates status enum values before calling your store.
232
+
233
+ Validate with: `adcp storyboard run <agent> deterministic_testing --json`
234
+
235
+ ## SDK Quick Reference
236
+
237
+ | SDK piece | Usage |
238
+ |-----------|-------|
239
+ | `serve(createAgent)` | Start HTTP server on `:3001/mcp` |
240
+ | `createTaskCapableServer(name, version, { taskStore })` | Create MCP server with task support |
241
+ | `server.tool(name, Schema.shape, handler)` | Register tool — `.shape` unwraps Zod for MCP SDK |
242
+ | `capabilitiesResponse(data)` | Build `get_adcp_capabilities` response |
243
+ | `productsResponse(data)` | Build `get_products` response |
244
+ | `mediaBuyResponse(data)` | Build `create_media_buy` response |
245
+ | `deliveryResponse(data)` | Build `get_media_buy_delivery` response |
246
+ | `taskToolResponse(data, summary)` | Build generic tool response |
247
+ | `adcpError(code, { message })` | Structured error (e.g., `BUDGET_TOO_LOW`, `PRODUCT_NOT_FOUND`) |
248
+ | `registerTestController(server, store)` | Add `comply_test_controller` for deterministic testing |
249
+ | `TestControllerError(code, message)` | Typed error from store methods |
250
+
251
+ Import everything from `@adcp/client`. Types from `@adcp/client` with `import type`.
252
+
253
+ ## Implementation
254
+
255
+ 1. Single `.ts` file — all tools in one file
256
+ 2. Always register `get_adcp_capabilities` as the **first** tool with empty `{}` schema
257
+ 3. Use `Schema.shape` (not `Schema`) when registering tools
258
+ 4. Use response builders — never return raw JSON
259
+ 5. Set `sandbox: true` on all mock/demo responses
260
+ 6. Use `ServeContext` pattern: `function createAgent({ taskStore }: ServeContext)` and pass `taskStore` to `createTaskCapableServer`
261
+
262
+ The skill contains everything you need. Do not read additional docs before writing code.
263
+
264
+ ## Validation
265
+
266
+ **After writing the agent, validate it. Fix failures. Repeat.**
267
+
268
+ **Full validation** (if you can bind ports):
269
+ ```bash
270
+ npx tsx agent.ts &
271
+ npx @adcp/client storyboard run http://localhost:3001/mcp media_buy_seller --json
272
+ ```
273
+
274
+ **Sandbox validation** (if ports are blocked):
275
+ ```bash
276
+ npx tsc --noEmit agent.ts
277
+ ```
278
+
279
+ When storyboard output shows failures, fix each one:
280
+ - `response_schema` → response doesn't match Zod schema
281
+ - `field_present` → required field missing
282
+ - MCP error → check tool registration (schema, name)
283
+
284
+ **Keep iterating until all steps pass.**
285
+
286
+ ## Storyboards
287
+
288
+ | Storyboard | Use case |
289
+ |-----------|----------|
290
+ | `media_buy_seller` | Full lifecycle — every seller should pass this |
291
+ | `media_buy_non_guaranteed` | Auction flow with bid adjustment |
292
+ | `media_buy_guaranteed_approval` | IO approval workflow |
293
+ | `media_buy_proposal_mode` | AI-generated proposals |
294
+ | `media_buy_catalog_creative` | Catalog sync + conversions |
295
+
296
+ ## Common Mistakes
297
+
298
+ | Mistake | Fix |
299
+ |---------|-----|
300
+ | Pass `Schema` instead of `Schema.shape` | MCP SDK needs unwrapped Zod fields |
301
+ | Skip `get_adcp_capabilities` | Must be the first tool registered |
302
+ | Return raw JSON without response builders | LLM clients need the text content layer |
303
+ | Missing `brand`/`operator` in sync_accounts response | Echo them back from the request — they're required |
304
+ | sync_governance returns wrong shape | Must include `status: 'synced'` and `governance_agents` array |
305
+ | `sandbox: false` on mock data | Buyers may treat mock data as real |
306
+
307
+ ## Reference
308
+
309
+ - `docs/guides/BUILD-AN-AGENT.md` — SDK patterns and async tools
310
+ - `docs/llms.txt` — full protocol reference
311
+ - `docs/TYPE-SUMMARY.md` — curated type signatures
312
+ - `storyboards/media_buy_seller.yaml` — full buyer interaction sequence
313
+ - `examples/error-compliant-server.ts` — seller with error handling