一键导入
build-generative-seller-agent
// Use when building an AdCP generative seller — an AI ad network, generative DSP, or platform that sells inventory AND generates creatives from briefs.
// Use when building an AdCP generative seller — an AI ad network, generative DSP, or platform that sells inventory AND generates creatives from briefs.
| 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. |
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.
A generative seller that sells programmatic inventory MUST also accept standard IAB formats (display images, VAST tags). The generative capability is additive.
Not this skill:
skills/build-seller-agent/skills/build-creative-agent/skills/build-signals-agent/Same decisions as the seller skill, plus: what generative formats? What brief inputs (name, objective, tone, messaging)? How to handle invalid brand domains?
Start from examples/seller_agent.py (the 9/9 passing seller reference), then modify list_creative_formats and sync_creatives to handle generative formats.
from adcp.server import ADCPHandler, serve
from adcp.server.responses import (
capabilities_response, products_response, media_buy_response,
delivery_response, creative_formats_response, sync_creatives_response,
build_creative_response, preview_creative_response,
)
from adcp.server.test_controller import TestControllerStore
import os
ADCP_PORT = int(os.environ.get("ADCP_PORT", 3001))
AGENT_URL = f"http://localhost:{ADCP_PORT}/mcp"
class MyGenerativeSeller(ADCPHandler):
# All seller tools (see seller skill) with modified creative handling
...
serve(MyGenerativeSeller(), name="my-gen-seller", port=3001, test_controller=MyStore())
Implement all tools from the seller skill. Copy the pattern from examples/seller_agent.py:
get_adcp_capabilities → capabilities_response(["media_buy"])sync_accounts → sync_accounts_response(results)sync_governance → sync_governance_response(results)get_products → products_response(PRODUCTS)create_media_buy → media_buy_response(mb_id, packages)get_media_buys → media_buys_response(buys)get_media_buy_delivery → delivery_response(deliveries, reporting_period=...)See skills/build-seller-agent/SKILL.md for the exact response shapes of each.
list_creative_formats — return BOTH generative and standard formats:
from adcp.server.responses import creative_formats_response
async def list_creative_formats(self, params, context=None):
return creative_formats_response([
# Generative format — accepts brief input
{
"format_id": {"agent_url": AGENT_URL, "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": AGENT_URL, "id": "display_300x250"},
"name": "Display 300x250",
"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 — handle both brief-based and standard creatives:
from adcp.server.responses import sync_creatives_response
async def sync_creatives(self, params, context=None):
results = []
for c in params.get("creatives", []):
creative_id = c.get("creative_id", f"c-{uuid.uuid4().hex[:8]}")
format_id = c.get("format_id", {}).get("id", "")
if "generative" in format_id:
# Brief-based: async generation, return pending_review
creatives[creative_id] = {**c, "status": "pending_review"}
results.append({"creative_id": creative_id, "action": "created", "status": "pending_review"})
else:
# Standard upload: immediate approve
creatives[creative_id] = {**c, "status": "approved"}
results.append({"creative_id": creative_id, "action": "created", "status": "approved"})
return sync_creatives_response(results)
Same as the seller skill. Copy the TestControllerStore from examples/seller_agent.py:
serve(MyGenerativeSeller(), name="my-gen-seller", port=3001, test_controller=MyStore())
All seller response builders apply. The generative delta is in list_creative_formats (both generative + standard formats) and sync_creatives (check format_id to decide processing path).
| Function | Usage |
|---|---|
creative_formats_response(formats) | list_creative_formats response |
sync_creatives_response(creatives) | sync_creatives response |
ADCPHandler advertises build_creative and preview_creative by default and returns not_supported unless you override them. A generative seller MUST implement both, or the storyboard fails at the generation step.
build_creative — render a creative manifest from a brief. idempotency_key is a REQUIRED request field (pattern ^[A-Za-z0-9_.:-]{16,255}$, see src/adcp/types/generated_poc/media_buy/build_creative_request.py:157-165). Use adcp.server.idempotency.IdempotencyStore to dedupe retries:
from adcp.server.responses import build_creative_response
from adcp.server.idempotency import IdempotencyStore, MemoryBackend
idempotency = IdempotencyStore(backend=MemoryBackend(), ttl_seconds=86400)
@idempotency.wrap
async def build_creative(self, params, context=None):
# idempotency_key is required by schema; @idempotency.wrap dedups replays per (caller, key).
manifest = {
"promoted_offering": params.get("promoted_offering"),
"format_id": params["format_id"],
"assets": [{"asset_id": "image", "url": "https://cdn.example/generated.jpg"}],
}
return build_creative_response(manifest)
Declare idempotency in your capabilities response so replays are advertised: capabilities_response(["media_buy"], idempotency=idempotency.capability()).
preview_creative — return pre-render previews for a built manifest. Responses wrap a list of {preview_id, input, renders}:
from adcp.server.responses import preview_creative_response
async def preview_creative(self, params, context=None):
return preview_creative_response([{
"preview_id": f"prev-{uuid.uuid4().hex[:8]}",
"input": params,
"renders": [{"width": 300, "height": 250, "url": "https://cdn.example/preview.png"}],
}])
python agent.py &
npx -y -p @adcp/client adcp storyboard run http://localhost:3001/mcp media_buy_generative_seller --json
| Mistake | Fix |
|---|---|
| Only generative formats, no standard IAB | Must accept pre-built assets too |
| Same handler for brief and standard | Check format_id to decide processing path |
| Skip seller tools | All seller tools are required — start from examples/seller_agent.py |
Wrong delivery_response signature | Takes delivery_response(deliveries_list, reporting_period=...), not individual metrics |
skills/build-seller-agent/SKILL.md — base seller skill (start there, modify creative handling)skills/build-seller-agent/SKILL.md — complete seller tool shapesUse when building an AdCP seller agent — a publisher, SSP, or retail media network that sells advertising inventory to buyer agents.
Use when building an AdCP signals agent, creating an audience data server, or standing up a data provider agent that serves targeting segments to buyers.
Use when building an AdCP creative agent — an ad server, creative management platform, or any system that accepts, stores, transforms, and serves ad creatives.
Use when building an AdCP retail media network agent — a platform that sells on-site placements, supports product catalogs, tracks conversions, and reports performance.