@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.
- package/README.md +23 -9
- package/bin/adcp.js +83 -18
- package/dist/lib/index.d.ts +3 -5
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +16 -12
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/server/index.d.ts +5 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +10 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/postgres-task-store.d.ts +105 -0
- package/dist/lib/server/postgres-task-store.d.ts.map +1 -0
- package/dist/lib/server/postgres-task-store.js +267 -0
- package/dist/lib/server/postgres-task-store.js.map +1 -0
- package/dist/lib/server/responses.d.ts +1 -0
- package/dist/lib/server/responses.d.ts.map +1 -1
- package/dist/lib/server/responses.js +1 -0
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/test-controller.d.ts +88 -0
- package/dist/lib/server/test-controller.d.ts.map +1 -0
- package/dist/lib/server/test-controller.js +227 -0
- package/dist/lib/server/test-controller.js.map +1 -0
- package/dist/lib/testing/agent-tester.d.ts +1 -1
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js +13 -1
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +24 -5
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +318 -277
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/index.d.ts +2 -1
- package/dist/lib/testing/compliance/index.d.ts.map +1 -1
- package/dist/lib/testing/compliance/index.js +6 -1
- package/dist/lib/testing/compliance/index.js.map +1 -1
- package/dist/lib/testing/compliance/platform-storyboards.d.ts +44 -0
- package/dist/lib/testing/compliance/platform-storyboards.d.ts.map +1 -0
- package/dist/lib/testing/compliance/platform-storyboards.js +232 -0
- package/dist/lib/testing/compliance/platform-storyboards.js.map +1 -0
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts +2 -9
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.js +15 -46
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
- package/dist/lib/testing/compliance/types.d.ts +22 -1
- package/dist/lib/testing/compliance/types.d.ts.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 +6 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +5 -1
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.d.ts +19 -1
- package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +138 -1
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.js +7 -7
- package/dist/lib/testing/scenarios/deterministic.js.map +1 -1
- package/dist/lib/testing/scenarios/index.d.ts +1 -1
- package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/index.js +4 -2
- package/dist/lib/testing/scenarios/index.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +4 -4
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/storyboard/loader.d.ts +1 -0
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/loader.js +14 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.js +88 -11
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +83 -5
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts +2 -0
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +23 -9
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/types.d.ts +6 -2
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +21 -4
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/types.d.ts +1 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +242 -3
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/schemas.generated.d.ts +3697 -3468
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +226 -118
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +281 -79
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +2 -2
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +9 -3
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +9 -0
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/docs/llms.txt +56 -32
- package/package.json +8 -2
- package/skills/adcp/SKILL.md +118 -33
- package/skills/build-creative-agent/SKILL.md +221 -0
- package/skills/build-generative-seller-agent/SKILL.md +288 -0
- package/skills/build-retail-media-agent/SKILL.md +237 -0
- package/skills/build-seller-agent/SKILL.md +313 -0
- package/skills/build-signals-agent/SKILL.md +203 -0
- package/storyboards/audience_sync.yaml +18 -29
- package/storyboards/behavioral_analysis.yaml +40 -72
- package/storyboards/brand_rights.yaml +172 -75
- package/storyboards/campaign_governance_conditions.yaml +187 -0
- package/storyboards/campaign_governance_delivery.yaml +231 -0
- package/storyboards/campaign_governance_denied.yaml +136 -0
- package/storyboards/capability_discovery.yaml +106 -0
- package/storyboards/content_standards.yaml +251 -0
- package/storyboards/creative_ad_server.yaml +108 -16
- package/storyboards/creative_generative.yaml +317 -0
- package/storyboards/creative_lifecycle.yaml +284 -0
- package/storyboards/creative_sales_agent.yaml +2 -6
- package/storyboards/creative_template.yaml +3 -6
- package/storyboards/deterministic_testing.yaml +271 -245
- package/storyboards/error_compliance.yaml +105 -108
- package/storyboards/media_buy_catalog_creative.yaml +8 -5
- package/storyboards/media_buy_generative_seller.yaml +581 -0
- package/storyboards/media_buy_governance_escalation.yaml +10 -6
- package/storyboards/media_buy_guaranteed_approval.yaml +21 -19
- package/storyboards/media_buy_non_guaranteed.yaml +9 -8
- package/storyboards/media_buy_proposal_mode.yaml +12 -11
- package/storyboards/media_buy_seller.yaml +161 -173
- package/storyboards/media_buy_state_machine.yaml +102 -101
- package/storyboards/property_governance.yaml +239 -0
- package/storyboards/schema.yaml +3 -2
- package/storyboards/schema_validation.yaml +58 -51
- package/storyboards/si_session.yaml +99 -317
- package/storyboards/signal_marketplace.yaml +9 -5
- package/storyboards/signal_owned.yaml +6 -5
- package/storyboards/social_platform.yaml +274 -0
- package/storyboards/test-kits/acme-outdoor.yaml +118 -0
- package/storyboards/test-kits/nova-motors.yaml +134 -0
- package/storyboards/governance_content_standards.yaml +0 -213
- 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
|