build-generative-seller-agent · git:20260409.69a6dde · 2026-04-09 · sha256 0eec4dd2f2c4cdb0
build-generative-seller-agent git:20260409.69a6ddeA
Immutable. This exact content is served forever at /api/v1/blob/0eec4dd2f2c4cdb0.
---
name: build-generative-seller-agent
description: Use when building an AdCP generative seller — an AI ad network, generative DSP, or platform that sells inventory AND generates creatives from briefs.
---
# Build a Generative Seller Agent
## Overview
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.
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.
## When to Use
- User wants to build a generative DSP or AI ad network
- User's platform both sells inventory and creates/generates creatives
- User mentions "creative from brief", "AI-generated ads", or "generative"
**Not this skill:**
- Standard seller (no creative generation) → `skills/build-seller-agent/`
- Standalone creative agent (renders but doesn't sell) → creative agent
- Signals/audience data → `skills/build-signals-agent/`
## Before Writing Code
Determine these things. Ask the user — don't guess.
### 1. What kind of platform?
- **AI ad network** — sells inventory across publishers, generates creatives from briefs
- **Generative DSP** — programmatic buying + AI creative generation
- **Retail media with creative** — retail inventory + dynamic ad generation from catalogs
### 2. Products and pricing
Same as standard seller. Each product needs: name, channel, delivery_type, pricing_options.
### 3. Generative formats
What creative formats does your platform generate?
- **Display** — generated static images (300x250, 728x90, etc.)
- **Video** — generated video ads (15s, 30s pre-roll)
- **HTML** — generated interactive/rich media
Each generative format needs a brief asset slot. Standard formats need traditional asset slots (image, video, VAST).
### 4. What inputs does the brief accept?
At minimum: `name`, `objective`, `tone`, `messaging` (headline, cta, key_messages).
Optional: `audience`, `territory`, `compliance` (required_disclosures, prohibited_claims).
### 5. Brand resolution
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.
## Tools and Required Response Shapes
Everything from the standard seller skill applies. The delta is in `list_creative_formats` and `sync_creatives`.
**`get_adcp_capabilities`** — register first, empty `{}` schema
```
capabilitiesResponse({
adcp: { major_versions: [3] },
supported_protocols: ['media_buy'],
})
```
**`sync_accounts`** — `SyncAccountsRequestSchema.shape`
```
taskToolResponse({
accounts: [{
account_id: string,
brand: { domain: string },
operator: string,
action: 'created' | 'updated',
status: 'active' | 'pending_approval',
}]
})
```
**`get_products`** — `GetProductsRequestSchema.shape`
```
productsResponse({
products: Product[], // each needs product_id, delivery_type, pricing_options
sandbox: true,
})
```
**`create_media_buy`** — `CreateMediaBuyRequestSchema.shape`
```
mediaBuyResponse({
media_buy_id: string,
packages: [{ package_id, product_id, pricing_option_id, budget }],
})
```
**`list_creative_formats`** — `ListCreativeFormatsRequestSchema.shape`
Return BOTH generative and standard formats:
```
taskToolResponse({
formats: [
// Generative format — accepts brief input
{
format_id: { agent_url: string, id: 'display_300x250_generative' },
name: 'Generated Display 300x250',
description: 'AI-generated display ad from creative brief',
renders: [{ width: 300, height: 250 }],
assets: [{
item_type: 'individual',
asset_id: 'brief',
asset_type: 'brief',
required: true,
description: 'Creative brief with messaging and brand guidelines',
}],
},
// Standard format — accepts pre-built assets
{
format_id: { agent_url: string, id: 'display_300x250' },
name: 'Display 300x250',
description: 'Standard IAB display banner',
renders: [{ width: 300, height: 250 }],
assets: [{
item_type: 'individual',
asset_id: 'image',
asset_type: 'image',
required: true,
accepted_media_types: ['image/jpeg', 'image/png'],
}],
},
],
})
```
**`sync_creatives`** — `SyncCreativesRequestSchema.shape`
Handle both brief-based and standard creatives:
```
taskToolResponse({
creatives: [{
creative_id: string, // echo from request
action: 'created' | 'updated', // required
status: 'accepted' | 'pending_review', // pending_review if generation is async
}],
})
```
For invalid brand domains, return rejection:
```
taskToolResponse({
creatives: [{
creative_id: string,
action: 'created',
status: 'rejected',
errors: ['Brand domain not found: nonexistent-brand.example'],
}],
})
```
**`get_media_buys`** — `GetMediaBuysRequestSchema.shape`
```
taskToolResponse({
media_buys: [{
media_buy_id: string,
status: 'active' | 'pending_start' | ...,
currency: 'USD',
packages: [{ package_id: string }],
}]
})
```
**`get_media_buy_delivery`** — `GetMediaBuyDeliveryRequestSchema.shape`
```
deliveryResponse({
reporting_period: { start: string, end: string },
media_buy_deliveries: [{
media_buy_id: string,
status: 'active',
totals: { impressions: number, spend: number },
by_package: [],
}]
})
```
## SDK Quick Reference
| SDK piece | Usage |
|-----------|-------|
| `serve(createAgent)` | Start HTTP server on `:3001/mcp` |
| `createTaskCapableServer(name, version, { taskStore })` | Create MCP server with task support |
| `server.tool(name, Schema.shape, handler)` | Register tool — `.shape` unwraps Zod |
| `capabilitiesResponse(data)` | Build `get_adcp_capabilities` response |
| `productsResponse(data)` | Build `get_products` response |
| `mediaBuyResponse(data)` | Build `create_media_buy` response |
| `deliveryResponse(data)` | Build `get_media_buy_delivery` response |
| `taskToolResponse(data, summary)` | Build generic tool response |
| `adcpError(code, { message })` | Structured error |
Import everything from `@adcp/client`. Types from `@adcp/client` with `import type`.
## Implementation
1. Single `.ts` file — all tools in one file
2. Always register `get_adcp_capabilities` as the **first** tool with empty `{}` schema
3. Use `Schema.shape` (not `Schema`) when registering tools
4. Use response builders — never return raw JSON
5. Set `sandbox: true` on all mock/demo responses
6. Use `ServeContext` pattern: `function createAgent({ taskStore }: ServeContext)`
The skill contains everything you need. Do not read additional docs before writing code.
### Key implementation detail: sync_creatives handler
The sync_creatives handler must check the format_id to decide how to process:
- If the format is generative (e.g., id contains "generative"): read the `brief` asset from the creative's assets
- If the format is standard: read the image/video/html asset
- Validate the brand domain from the account — reject if invalid
- Return `pending_review` for generative (async generation) or `accepted` for standard
## Validation
**After writing the agent, validate it. Fix failures. Repeat.**
**Full validation** (if you can bind ports):
```bash
npx tsx agent.ts &
npx @adcp/client storyboard run http://localhost:3001/mcp media_buy_generative_seller --json
```
**Sandbox validation** (if ports are blocked):
```bash
npx tsc --noEmit agent.ts
```
When storyboard output shows failures, fix each one:
- `response_schema` → response doesn't match Zod schema
- `field_present` → required field missing
- MCP error → check tool registration (schema, name)
**Keep iterating until all steps pass.**
## Common Mistakes
| Mistake | Fix |
|---------|-----|
| Only generative formats, no standard IAB | Programmatic sellers must accept pre-built assets too |
| Ignore brand domain on brief sync | Validate brand, reject if unresolvable |
| Same handler for brief and standard creatives | Check format_id to decide processing path |
| Skip `get_adcp_capabilities` | Must be the first tool registered |
| Pass `Schema` instead of `Schema.shape` | MCP SDK needs unwrapped Zod fields |
| `sandbox: false` on mock data | Buyers may treat mock data as real |
## Reference
- `storyboards/media_buy_generative_seller.yaml` — full generative seller storyboard
- `storyboards/media_buy_seller.yaml` — base seller storyboard (for standard seller parts)
- `skills/build-seller-agent/SKILL.md` — standard seller skill (generative extends this)
- `docs/guides/BUILD-AN-AGENT.md` — SDK patterns
- `docs/llms.txt` — full protocol reference