From 6f007958e8dce860e026334405bb8121f1f58fbb Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 25 Apr 2026 07:58:11 -0400 Subject: [PATCH] feat(sync): pull canonical agent skills from the protocol tarball MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/sync-schemas.ts now extracts protocol-managed skills (call-adcp-agent plus the six per-protocol skills: adcp-{brand,creative,governance,media-buy, si,signals}) from /protocol/.tgz alongside schemas and compliance. Manifest-driven and per-name: only directories enumerated in manifest.contents.skills are overwritten. SDK-local skills (build-seller-agent, build-creative-agent, the legacy adcp CLI skill, etc.) stay untouched. Filters out nested schemas/ subdirs from each per-protocol skill — the spec repo bundles them for self-contained agent consumption, but @adcp/client already has the canonical schemas in schemas/cache//, so re-copying would duplicate ~1.4MB per protocol with no functional gain. Adds an ADCP_BASE_URL env override (defaults to https://adcontextprotocol.org) so CI / local dev can point sync at a fake CDN for testing. The buyer-side call-adcp-agent skill is now sourced from the spec repo (adcontextprotocol/adcp#3097) rather than maintained as a local copy — version-pinned to ADCP_VERSION, Sigstore-verified via the same cosign path as schemas, no manual sync. Tested end-to-end against a local CDN serving a release-built tarball: all seven protocol-managed skills extracted; SDK-local skills (adcp/, build-*) untouched; nested schemas/ subdirs filtered (1 file per skill, ~80K total vs. 1.4MB+ per protocol unfiltered). Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/sync-skills-from-tarball.md | 11 + .gitignore | 3 + scripts/sync-schemas.ts | 89 ++++- skills/adcp-brand/SKILL.md | 202 +++++++++++ skills/adcp-creative/SKILL.md | 301 +++++++++++++++++ skills/adcp-governance/SKILL.md | 444 ++++++++++++++++++++++++ skills/adcp-media-buy/SKILL.md | 446 +++++++++++++++++++++++++ skills/adcp-si/SKILL.md | 206 ++++++++++++ skills/adcp-signals/SKILL.md | 204 +++++++++++ skills/call-adcp-agent/SKILL.md | 27 +- 10 files changed, 1920 insertions(+), 13 deletions(-) create mode 100644 .changeset/sync-skills-from-tarball.md create mode 100644 skills/adcp-brand/SKILL.md create mode 100644 skills/adcp-creative/SKILL.md create mode 100644 skills/adcp-governance/SKILL.md create mode 100644 skills/adcp-media-buy/SKILL.md create mode 100644 skills/adcp-si/SKILL.md create mode 100644 skills/adcp-signals/SKILL.md diff --git a/.changeset/sync-skills-from-tarball.md b/.changeset/sync-skills-from-tarball.md new file mode 100644 index 000000000..9ed4b5fba --- /dev/null +++ b/.changeset/sync-skills-from-tarball.md @@ -0,0 +1,11 @@ +--- +"@adcp/client": minor +--- + +feat(sync): pull canonical agent skills from the protocol tarball + +`scripts/sync-schemas.ts` now extracts protocol-managed skills (`call-adcp-agent`, `adcp-media-buy`, `adcp-creative`, `adcp-signals`, `adcp-governance`, `adcp-si`, `adcp-brand`) from the published `/protocol/.tgz` bundle alongside schemas and compliance, into `@adcp/client/skills//`. The sync is **manifest-driven and per-name** — only directories enumerated in `manifest.contents.skills` are overwritten, so SDK-local skills (`build-seller-agent`, `build-creative-agent`, etc.) stay untouched. + +The buyer-side `call-adcp-agent` skill is now sourced from the spec repo (adcontextprotocol/adcp#3097) rather than maintained as a local copy — version-pinned to `ADCP_VERSION`, Sigstore-verified via the same cosign path as schemas, no manual sync. + +Adds an `ADCP_BASE_URL` env override (defaults to `https://adcontextprotocol.org`) so CI / local-dev can point sync at a fake CDN for testing. diff --git a/.gitignore b/.gitignore index fb1e04ada..23a040c57 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,6 @@ IMPLEMENTATION_PLAN.md # Claude Code runtime artifacts (.claude/skills/ is tracked on purpose) .claude/scheduled_tasks.lock + +# Snapshot artifacts created by sync-schemas replaceTree +skills/*.previous/ diff --git a/scripts/sync-schemas.ts b/scripts/sync-schemas.ts index e1932031b..f045353e4 100644 --- a/scripts/sync-schemas.ts +++ b/scripts/sync-schemas.ts @@ -20,10 +20,11 @@ import { spawnSync } from 'child_process'; import path from 'path'; import * as tar from 'tar'; -const ADCP_BASE_URL = 'https://adcontextprotocol.org'; +const ADCP_BASE_URL = process.env.ADCP_BASE_URL || 'https://adcontextprotocol.org'; const REPO_ROOT = path.join(__dirname, '..'); const SCHEMA_CACHE_DIR = path.join(REPO_ROOT, 'schemas/cache'); const COMPLIANCE_CACHE_DIR = path.join(REPO_ROOT, 'compliance/cache'); +const SKILLS_DIR = path.join(REPO_ROOT, 'skills'); const REGISTRY_SPEC_PATH = path.join(REPO_ROOT, 'schemas/registry/registry.yaml'); // Sigstore keyless identity used by the upstream release workflow (adcontextprotocol/adcp#2273). @@ -210,6 +211,85 @@ async function verifyCosignSignature(tgzPath: string, version: string): Promise< console.log(`✅ cosign signature verified (identity: adcontextprotocol/adcp release workflow).`); } +/** + * Copy a skill directory tree into the SDK, replacing the destination but + * skipping nested `schemas/` subdirs (duplicates of `schemas/cache//`). + */ +function copySkillTree(srcDir: string, destDir: string): void { + if (existsSync(destDir)) { + // Snapshot the outgoing tree the same way replaceTree does so the + // schema-diff helper can pick up changes between syncs. + const previous = `${destDir}.previous`; + if (existsSync(previous)) rmSync(previous, { recursive: true, force: true }); + renameSync(destDir, previous); + } + mkdirSync(destDir, { recursive: true }); + copyTreeFiltered(srcDir, destDir); +} + +function copyTreeFiltered(srcDir: string, destDir: string): void { + for (const entry of readdirSync(srcDir, { withFileTypes: true })) { + const src = path.join(srcDir, entry.name); + const dst = path.join(destDir, entry.name); + if (entry.isDirectory()) { + // The spec repo's per-protocol skills bundle a copy of the AdCP schemas + // for self-contained agent consumption; the SDK has them in + // `schemas/cache/` already, so this would just duplicate ~1.4MB per + // protocol. Skip. + if (entry.name === 'schemas') continue; + mkdirSync(dst, { recursive: true }); + copyTreeFiltered(src, dst); + } else { + copyFileSync(src, dst); + } + } +} + +/** + * Sync protocol-managed skills from the extracted bundle into the SDK's + * top-level `skills/` tree. Driven by `manifest.contents.skills` (a list of + * skill directory names) so we only overwrite the entries the spec repo + * publishes — leaves SDK-local skills (`build-seller-agent/`, etc.) alone. + */ +function syncSkillsFromBundle(extractRoot: string): void { + const skillsInBundle = path.join(extractRoot, 'skills'); + const manifestPath = path.join(extractRoot, 'manifest.json'); + if (!existsSync(skillsInBundle) || !existsSync(manifestPath)) { + return; + } + let manifest: unknown; + try { + manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); + } catch (err) { + console.warn(`⚠️ Skill sync skipped: manifest unparseable (${err instanceof Error ? err.message : err}).`); + return; + } + const skillNames = (manifest as { contents?: { skills?: unknown } }).contents?.skills; + if (!Array.isArray(skillNames)) { + // Older tarballs predate manifest.contents.skills enumeration. Skip silently. + return; + } + let synced = 0; + for (const name of skillNames) { + if (typeof name !== 'string' || name.includes('/') || name.includes('..')) { + continue; + } + const src = path.join(skillsInBundle, name); + const dst = path.join(SKILLS_DIR, name); + if (!existsSync(src) || !statSync(src).isDirectory()) continue; + // Skip nested `schemas/` subdirs — those duplicate `schemas/cache//` + // already extracted from the same tarball. Per-protocol skills in the spec + // repo bundle them for self-contained agent consumption; the SDK has them + // in `schemas/cache/` already, so re-copying inflates the package by ~1.4MB + // per protocol with no functional gain. + copySkillTree(src, dst); + synced++; + } + if (synced > 0) { + console.log(`📁 Skills: ${SKILLS_DIR} (${synced} protocol-managed)`); + } +} + /** * Fetch /protocol/{version}.tgz, verify sha256, and extract schemas + compliance * into their cache directories. Returns true on success. @@ -258,6 +338,13 @@ async function syncFromTarball(version: string): Promise { replaceTree(path.join(extractRoot, 'schemas'), path.join(SCHEMA_CACHE_DIR, version)); replaceTree(path.join(extractRoot, 'compliance'), path.join(COMPLIANCE_CACHE_DIR, version)); + // Skills sync is manifest-driven and per-name. SDK-local skills like + // build-seller-agent/ stay untouched; protocol-canonical ones (the + // call-adcp-agent buyer skill plus per-protocol skills) are kept aligned + // with the pinned spec version. Older tarballs (no manifest.contents.skills + // array) are silently skipped — the SDK-local copies stay as-is. + syncSkillsFromBundle(extractRoot); + // Refs inside the tarball point to /schemas/latest/; rewrite for pinned versions. const schemaDest = path.join(SCHEMA_CACHE_DIR, version); const indexJson = JSON.parse(readFileSync(path.join(schemaDest, 'index.json'), 'utf8')); diff --git a/skills/adcp-brand/SKILL.md b/skills/adcp-brand/SKILL.md new file mode 100644 index 000000000..8620108f6 --- /dev/null +++ b/skills/adcp-brand/SKILL.md @@ -0,0 +1,202 @@ +--- +name: adcp-brand +description: Execute AdCP Brand Protocol operations with brand agents - get brand identity data, search for licensable rights, acquire rights for campaigns, and manage existing grants. Use when users want to look up brand identities, find talent or IP for licensing, or manage rights grants. +--- + +# AdCP Brand Protocol + +This skill enables you to execute the AdCP Brand Protocol with brand agents. The Brand Protocol provides access to brand identity, creative guidelines, and licensable rights (talent, IP, content). + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Brand Protocol provides 4 standardized tasks: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_brand_identity` | Get brand identity and guidelines | ~1-3s | +| `get_rights` | Search licensable rights | ~1-5s | +| `acquire_rights` | Acquire rights for a campaign | ~1-10s | +| `update_rights` | Modify an existing grant | ~1-5s | + +## Typical Workflow + +### Brand Identity Lookup +1. **Get identity**: `get_brand_identity` with brand domain and optional field filter +2. **Use data**: Apply colors, logos, tone, guidelines to creative generation + +### Rights Licensing +1. **Search rights**: `get_rights` with natural language query and use types +2. **Review options**: Evaluate matches by pricing, availability, compatibility +3. **Acquire**: `acquire_rights` with selected pricing option and campaign details +4. **Manage**: `update_rights` to extend, adjust caps, or pause/resume + +--- + +## Task Reference + +### get_brand_identity + +Get brand identity data from a brand agent. + +**Request:** +```json +{ + "brand_id": "athlete-jane-doe", + "fields": ["description", "logos", "colors", "tone"], + "use_case": "creative_production", + "authorized": true +} +``` + +**Key fields:** +- `brand_id` (string, required): Brand identifier within the agent's roster +- `fields` (array, optional): Sections to include — `description`, `industry`, `keller_type`, `logos`, `colors`, `fonts`, `visual_guidelines`, `tone`, `tagline`, `voice_synthesis`, `assets`, `rights`. Omit for all. +- `use_case` (string, optional): Intended use — `endorsement`, `voice_synthesis`, `likeness`, `creative_production`, `media_planning` +- `authorized` (boolean, optional): Sandbox only — simulate authorized access to see protected fields. Real agents use OAuth. Default false. + +**Response contains:** +- `brand`: Brand identity object with requested fields +- Public fields (always available): `description`, `industry`, `logos` (public subset) +- Protected fields (require authorization): `colors`, `fonts`, `tone`, `voice_synthesis`, `visual_guidelines`, full `assets` + +--- + +### get_rights + +Search for licensable rights (talent, IP, content) from a brand agent. + +**Request:** +```json +{ + "query": "Dutch athlete for restaurant brand in Amsterdam, budget 400 EUR/month", + "uses": ["likeness", "endorsement"], + "buyer_brand": { + "domain": "restaurant.nl" + }, + "countries": ["NL"], + "include_excluded": false +} +``` + +**Key fields:** +- `query` (string, required): Natural language description of desired rights +- `uses` (array, required): Rights uses — `likeness`, `voice`, `name`, `endorsement` +- `buyer_brand` (object, optional): Buyer brand for compatibility filtering — `{ domain, brand_id }` +- `countries` (array, optional): Countries where rights are needed (ISO 3166-1 alpha-2) +- `brand_id` (string, optional): Search within a specific brand only +- `include_excluded` (boolean, optional): Include filtered-out results with reasons. Default false. + +**Response contains:** +- `rights`: Array of matching rights offerings with: + - `rights_id`: Use in `acquire_rights` + - `brand_id`, `name`, `description`: Who/what the rights cover + - `uses`: Available use types + - `pricing_options`: Array with `pricing_option_id`, `price`, `currency`, `period` + - `availability`: Geographic and temporal restrictions + - `exclusions`: Any brand/category conflicts + +--- + +### acquire_rights + +Acquire rights from a brand agent for a campaign. + +**Request:** +```json +{ + "rights_id": "rights_jane_doe_endorsement", + "pricing_option_id": "monthly_standard", + "buyer": { + "domain": "restaurant.nl" + }, + "campaign": { + "description": "Social media campaign featuring athlete endorsement for Amsterdam restaurant launch", + "uses": ["likeness", "endorsement"], + "countries": ["NL"], + "estimated_impressions": 500000, + "start_date": "2025-03-01", + "end_date": "2025-06-30" + } +} +``` + +**Key fields:** +- `rights_id` (string, required): From `get_rights` response +- `pricing_option_id` (string, required): Selected pricing option +- `buyer` (object, required): Buyer brand identity — `{ domain, brand_id }` +- `campaign` (object, required): Campaign details for rights clearance + - `description` (string, required): How the rights will be used + - `uses` (array, required): Rights uses for this campaign + - `countries` (array, optional): Campaign countries + - `estimated_impressions` (integer, optional): Estimated total impressions + - `start_date`, `end_date` (string, optional): Campaign dates (YYYY-MM-DD) + +**Response contains:** +- `status`: `acquired`, `pending_approval`, or `rejected` +- `rights_grant_id`: Grant identifier (if acquired) +- `generation_credentials`: Credentials for AI generation (voice synthesis, likeness, etc.) +- `rejection_reason`: Why the request was rejected (category conflict, exclusivity, etc.) + +--- + +### update_rights + +Update an existing rights grant — extend dates, adjust impression caps, or pause/resume. + +**Request:** +```json +{ + "rights_id": "grant_abc123", + "end_date": "2025-09-30", + "impression_cap": 1000000, + "paused": false +} +``` + +**Key fields:** +- `rights_id` (string, required): Rights grant identifier from `acquire_rights` +- `end_date` (string, optional): New end date (must be >= current end date) +- `impression_cap` (number, optional): New impression cap (must be >= current) +- `paused` (boolean, optional): Pause or resume the grant + +--- + +## Key Concepts + +### Public vs Protected Fields + +Brand agents distinguish between public and protected data: +- **Public**: Available without authorization — basic description, industry, public logos +- **Protected**: Requires OAuth or authorized flag — colors, fonts, tone, voice synthesis credentials, full asset library + +### Rights Use Types + +- `likeness`: Use of a person's visual likeness (photos, AI-generated images) +- `voice`: Voice synthesis or audio recording rights +- `name`: Use of a person's name in advertising +- `endorsement`: Endorsement/testimonial rights + +### Rights Clearance + +`acquire_rights` checks: +1. Brand/category compatibility (no competitor conflicts) +2. Geographic availability +3. Temporal availability +4. Existing exclusivity agreements + +Results: `acquired` (immediate), `pending_approval` (human review), or `rejected` (with reason). + +--- + +## Error Handling + +Common error codes: + +- `BRAND_NOT_FOUND`: Invalid brand_id +- `RIGHTS_NOT_FOUND`: Invalid rights_id +- `PRICING_OPTION_NOT_FOUND`: Invalid pricing_option_id +- `CATEGORY_CONFLICT`: Buyer brand conflicts with existing agreements +- `GEOGRAPHIC_RESTRICTION`: Rights not available in requested countries +- `AUTHORIZATION_REQUIRED`: Protected fields require OAuth diff --git a/skills/adcp-creative/SKILL.md b/skills/adcp-creative/SKILL.md new file mode 100644 index 000000000..59d707748 --- /dev/null +++ b/skills/adcp-creative/SKILL.md @@ -0,0 +1,301 @@ +--- +name: adcp-creative +description: Execute AdCP Creative Protocol operations with creative agents - build creatives from briefs or existing assets, preview renderings, and discover format specifications. Use when users want to generate or transform ad creatives, preview how ads will look, or understand creative format requirements. +--- + +# AdCP Creative Protocol + +This skill enables you to execute the AdCP Creative Protocol with creative agents. Use the standard MCP tools (`list_creative_formats`, `build_creative`, `preview_creative`) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Creative Protocol provides 4 standardized tasks for building and previewing advertising creatives: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `list_creative_formats` | View format specifications | ~1s | +| `build_creative` | Generate or transform creatives | ~30s-5m | +| `preview_creative` | Get visual previews | ~5s | +| `get_creative_delivery` | Variant-level delivery data | ~5-30s | + +## Typical Workflow + +1. **Discover formats**: `list_creative_formats` to see available format specs +2. **Build creative**: `build_creative` to generate or transform a manifest +3. **Preview**: `preview_creative` to see how it renders +4. **Sync**: Use `sync_creatives` (media-buy task) to traffic the creative + +--- + +## Task Reference + +### list_creative_formats + +Discover creative formats and their specifications. + +**Request:** +```json +{ + "type": "video", + "asset_types": ["image", "text"] +} +``` + +**Key fields:** +- `format_ids` (array, optional): Request specific format IDs +- `type` (string, optional): Filter by type: `video`, `display`, `audio`, `dooh` +- `asset_types` (array, optional): Filter by accepted asset types +- `max_width`, `max_height` (integer, optional): Dimension constraints +- `is_responsive` (boolean, optional): Filter for responsive formats +- `name_search` (string, optional): Search formats by name + +**Response contains:** +- `formats`: Array of format definitions with `format_id`, `name`, `type`, `assets_required`, `renders` +- `creative_agents`: Optional array of other creative agents providing additional formats + +--- + +### build_creative + +Generate a creative from scratch or transform an existing creative to a different format. + +**Pure Generation (from brief):** +```json +{ + "message": "Create a banner promoting our winter sale with a warm, inviting feel", + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "brand": { + "domain": "mybrand.com" + } +} +``` + +**Transformation (resize/reformat):** +```json +{ + "message": "Adapt this leaderboard to a 300x250 banner", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_728x90" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.mybrand.com/leaderboard.png", + "width": 728, + "height": 90 + }, + "headline": { + "asset_type": "text", + "content": "Spring Sale - 30% Off" + } + } + }, + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +**Key fields:** +- `message` (string, optional): Natural language instructions for generation/transformation +- `creative_manifest` (object, optional): Source manifest - minimal for generation, complete for transformation +- `target_format_id` (object, required): Format to generate - `{ agent_url, id }` + +**Response contains:** +- `creative_manifest`: Complete manifest ready for `preview_creative` or `sync_creatives` + +--- + +### preview_creative + +Generate visual previews of creative manifests. + +**Single preview:** +```json +{ + "request_type": "single", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + } + } + } +} +``` + +**With device variants:** +```json +{ + "request_type": "single", + "creative_manifest": { /* includes format_id, assets */ }, + "inputs": [ + { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, + { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } } + ] +} +``` + +**Batch preview (5-10x faster):** +```json +{ + "request_type": "batch", + "requests": [ + { "creative_manifest": { /* creative 1 */ } }, + { "creative_manifest": { /* creative 2 */ } } + ] +} +``` + +**Key fields:** +- `request_type` (string, required): `"single"` or `"batch"` +- `format_id` (object, optional): Format identifier. Defaults to `creative_manifest.format_id` if omitted. +- `creative_manifest` (object, required): Complete creative manifest +- `inputs` (array, optional): Generate variants with different macros/contexts +- `output_format` (string, optional): `"url"` (default) or `"html"` + +**Response contains:** +- `previews`: Array of preview objects with `preview_url` or `preview_html` +- `expires_at`: When preview URLs expire + +--- + +### get_creative_delivery + +Retrieve variant-level creative delivery data from a creative agent. Returns what was generated, served, and how each variant performed. + +**Request:** +```json +{ + "media_buy_ids": ["mb_abc123"], + "start_date": "2025-01-01", + "end_date": "2025-01-31", + "max_variants": 10 +} +``` + +**Key fields:** +- `media_buy_ids` (array, optional): Filter to specific media buys +- `creative_ids` (array, optional): Filter to specific creatives +- `start_date`, `end_date` (string, optional): Delivery period (YYYY-MM-DD) +- `max_variants` (integer, optional): Max variants per creative (useful for generative creatives) +- `account` (object, optional): Account for routing and scoping + +At least one scoping filter (`media_buy_ids` or `creative_ids`) is required. + +**Response contains:** +- `creatives`: Array with variant-level delivery data + - `variant_id`: Unique variant identifier (use in `preview_creative` with `request_type: "variant"`) + - `generation_context`: What triggered this variant (page topic, device, etc.) + - `delivery_metrics`: Impressions, clicks, completions + - `ext`: Platform engagement metrics (likes, shares, comments) + +--- + +## Key Concepts + +### Format IDs + +All format references use structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies the creative agent authoritative for this format. + +### Creative Manifests + +Manifests pair format specifications with actual assets: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + }, + "headline": { + "asset_type": "text", + "content": "Shop Now" + }, + "clickthrough_url": { + "asset_type": "url", + "url": "https://brand.com/sale" + } + } +} +``` + +### Asset Types + +Common asset types: +- `image`: Static images (JPEG, PNG, WebP) +- `video`: Video files (MP4, WebM) or VAST tags +- `audio`: Audio files (MP3, M4A) or DAAST tags +- `text`: Headlines, descriptions, CTAs +- `html`: HTML5 creatives or third-party tags +- `javascript`: JavaScript tags +- `url`: Tracking pixels, clickthrough URLs + +### Brand identity + +For generative creatives, provide brand context by domain: +```json +{ + "brand": { + "domain": "acmecorp.com" + } +} +``` + +The agent resolves the domain to retrieve the brand's identity (name, colors, guidelines, etc.) from its `brand.json` file. + +### Generative vs Transformation + +- **Pure Generation**: Provide `target_format_id`, `brand`, and a natural language `message`. Creative agent generates all output assets from scratch. +- **Transformation**: Complete manifest with existing assets. Creative agent adapts to target format, following `message` guidance. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid manifest or format_id +- **404 Not Found**: Format not supported by this agent +- **422 Validation Error**: Manifest doesn't match format requirements + +Error responses include: +```json +{ + "error": { + "code": "INVALID_FORMAT_ID", + "message": "format_id must be a structured object with 'agent_url' and 'id' fields" + } +} +``` diff --git a/skills/adcp-governance/SKILL.md b/skills/adcp-governance/SKILL.md new file mode 100644 index 000000000..ef20efc13 --- /dev/null +++ b/skills/adcp-governance/SKILL.md @@ -0,0 +1,444 @@ +--- +name: adcp-governance +description: Execute AdCP Governance Protocol operations with governance agents - manage property lists, collection lists, and content standards for brand safety and campaign compliance. Use when users want to create include/exclude lists, set up brand safety rules, or validate content delivery. +--- + +# AdCP Governance Protocol + +This skill enables you to execute the AdCP Governance Protocol with governance agents. Covers three areas: property lists (site-level targeting), collection lists (program-level targeting), and content standards (brand safety rules). + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Governance Protocol provides 17 standardized tasks across three areas: + +### Property Lists +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_property_list` | Create include/exclude list | ~1s | +| `update_property_list` | Modify list filters/properties | ~1s | +| `get_property_list` | Retrieve list with optional resolution | ~1-5s | +| `list_property_lists` | List all accessible lists | ~1s | +| `delete_property_list` | Delete a list | ~1s | + +### Collection Lists +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_collection_list` | Create program-level list | ~1s | +| `update_collection_list` | Modify list | ~1s | +| `get_collection_list` | Retrieve with optional resolution | ~1-5s | +| `list_collection_lists` | List all accessible lists | ~1s | +| `delete_collection_list` | Delete a list | ~1s | + +### Content Standards +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_content_standards` | Create brand safety rules | ~1s | +| `get_content_standards` | Retrieve standards by ID | ~1s | +| `update_content_standards` | Modify rules | ~1s | +| `list_content_standards` | List all accessible standards | ~1s | +| `calibrate_content` | Test content against standards | ~5-30s | +| `get_media_buy_artifacts` | Get creatives for compliance review | ~5s | +| `validate_content_delivery` | Audit delivery compliance | ~10-60s | + +## Typical Workflow + +### Property Lists (site-level) +1. **Create list**: `create_property_list` with base properties and filters +2. **Resolve**: `get_property_list` with `resolve: true` to see matched properties +3. **Refine**: `update_property_list` to adjust filters +4. **Apply**: Reference `list_id` in `create_media_buy` targeting + +### Collection Lists (program-level, CTV) +1. **Create list**: `create_collection_list` with distribution IDs or genre filters +2. **Resolve**: `get_collection_list` with `resolve: true` to see matched programs +3. **Apply**: Reference in campaign targeting for CTV brand safety + +### Content Standards +1. **Create standards**: `create_content_standards` with rules +2. **Calibrate**: `calibrate_content` with test samples to validate configuration +3. **Monitor**: `get_media_buy_artifacts` + `validate_content_delivery` for ongoing compliance + +--- + +## Task Reference + +### create_property_list + +Create a property list for brand safety and inventory targeting. + +**Request:** +```json +{ + "name": "Premium News Properties", + "description": "Tier 1 news publishers for brand campaigns", + "base_properties": [ + { + "selection_type": "publisher_tags", + "publisher_domain": "publisher.com", + "tags": ["premium", "news"] + } + ], + "filters": { + "countries_all": ["US", "GB"], + "channels_any": ["display", "video"] + }, + "brand": { + "domain": "acmecorp.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `description` (string, optional): Purpose of the list +- `base_properties` (array, optional): Property sources — `publisher_tags`, `publisher_ids`, or `identifiers` +- `filters` (object, optional): Resolution filters — `countries_all`, `channels_any`, `property_types`, `feature_requirements`, `exclude_identifiers` +- `brand` (object, optional): Brand reference for automatic rule inference + +--- + +### update_property_list + +Modify an existing property list. + +**Request:** +```json +{ + "list_id": "pl_abc123", + "filters": { + "countries_all": ["US", "GB", "DE"], + "channels_any": ["display", "video", "ctv"] + } +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier +- `name`, `description` (string, optional): Update metadata +- `base_properties` (array, optional): Replace property sources +- `filters` (object, optional): Replace filter configuration + +--- + +### get_property_list + +Retrieve a property list with optional resolution. + +**Request:** +```json +{ + "list_id": "pl_abc123", + "resolve": true, + "max_results": 50 +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier +- `resolve` (boolean, optional): Resolve filters and return property identifiers (default: false) +- `max_results` (number, optional): Max properties when resolved + +--- + +### list_property_lists + +List all property lists accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "premium" +} +``` + +**Key fields:** +- `name_contains` (string, optional): Filter by name substring +- `max_results` (number, optional): Max results + +--- + +### delete_property_list + +Delete a property list. + +**Request:** +```json +{ + "list_id": "pl_abc123" +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier to delete + +--- + +### create_collection_list + +Create a collection list for program-level brand safety (CTV, podcast, streaming). + +**Request:** +```json +{ + "name": "Family-Safe CTV Programs", + "description": "Programs suitable for family brand campaigns", + "base_collections": [ + { + "selection_type": "publisher_genres", + "publisher_domain": "ctv-publisher.com", + "genres": ["family", "comedy"], + "genre_taxonomy": "iab_content_taxonomy_3.0" + } + ], + "filters": { + "content_ratings_exclude": [ + { "system": "us_tv", "rating": "TV-MA" } + ], + "kinds": ["series"] + }, + "brand": { + "domain": "familybrand.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `base_collections` (array, optional): Collection sources — `distribution_ids`, `publisher_collections`, or `publisher_genres` +- `filters` (object, optional): `content_ratings_exclude`, `content_ratings_include`, `genres_exclude`, `genres_include`, `kinds`, `production_quality` +- `brand` (object, optional): Brand reference + +**Distribution identifier types:** `imdb_id`, `gracenote_id`, `eidr_id` + +--- + +### update_collection_list + +Modify an existing collection list. + +**Request:** +```json +{ + "list_id": "cl_abc123", + "filters": { + "content_ratings_exclude": [ + { "system": "us_tv", "rating": "TV-MA" }, + { "system": "us_tv", "rating": "TV-14" } + ] + } +} +``` + +**Key fields:** +- `list_id` (string, required): Collection list identifier +- `base_collections`, `filters` (optional): Replace configuration + +--- + +### get_collection_list + +Retrieve a collection list with optional resolution. + +**Request:** +```json +{ + "list_id": "cl_abc123", + "resolve": true +} +``` + +**Key fields:** +- `list_id` (string, required): Collection list identifier +- `resolve` (boolean, optional): Resolve and return collection entries +- `max_results` (number, optional): Max collections when resolved + +--- + +### list_collection_lists + +List all collection lists accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "family" +} +``` + +--- + +### delete_collection_list + +Delete a collection list. + +**Request:** +```json +{ + "list_id": "cl_abc123" +} +``` + +--- + +### create_content_standards + +Create content standards (brand safety rules) for campaign compliance. + +**Request:** +```json +{ + "name": "Automotive Brand Safety", + "description": "Content rules for automotive brand campaigns", + "rules": [ + { "rule_type": "category", "action": "block", "value": "violence", "severity": "critical" }, + { "rule_type": "category", "action": "block", "value": "adult", "severity": "critical" }, + { "rule_type": "keyword", "action": "flag", "value": "accident", "severity": "medium" } + ], + "brand": { + "domain": "automaker.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `rules` (array, optional): Content rules — `rule_type`, `action` (allow/block/flag), `value`, `severity` +- `brand` (object, optional): Brand reference for automatic rule inference + +--- + +### get_content_standards + +Retrieve content standards by ID. + +**Request:** +```json +{ + "standards_id": "cs_abc123" +} +``` + +--- + +### update_content_standards + +Modify existing content standards. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "rules": [ + { "rule_type": "category", "action": "block", "value": "violence", "severity": "critical" } + ] +} +``` + +--- + +### list_content_standards + +List all content standards accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "automotive" +} +``` + +--- + +### calibrate_content + +Test content samples against content standards to validate configuration. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "samples": [ + { "url": "https://example.com/article1", "expected_result": "allow" }, + { "url": "https://example.com/article2", "expected_result": "block" }, + { "text": "Car crash injures three people", "expected_result": "block" } + ] +} +``` + +**Key fields:** +- `standards_id` (string, required): Content standards to calibrate against +- `samples` (array, required): Content samples with `url` and/or `text`, and optional `expected_result` + +--- + +### get_media_buy_artifacts + +Get creative artifacts from a media buy for compliance review. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "sales_agent_url": "https://sales.publisher.com" +} +``` + +**Key fields:** +- `media_buy_id` (string, required): Media buy identifier +- `sales_agent_url` (string, required): Sales agent that owns the media buy + +--- + +### validate_content_delivery + +Validate delivered content against content standards. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "media_buy_id": "mb_abc123", + "sales_agent_url": "https://sales.publisher.com", + "date_range": { + "start": "2025-01-01", + "end": "2025-01-31" + } +} +``` + +**Key fields:** +- `standards_id` (string, required): Content standards to validate against +- `media_buy_id` (string, required): Media buy identifier +- `sales_agent_url` (string, required): Sales agent URL +- `date_range` (object, optional): Filter by delivery date range + +--- + +## Key Concepts + +### Property Lists vs Collection Lists + +- **Property Lists**: Site-level targeting. Operates on publisher domains and properties (websites, apps, CTV apps). Use for "where" the ad appears. +- **Collection Lists**: Program-level targeting. Operates on shows, series, and content programs using distribution identifiers (IMDb, Gracenote, EIDR). Use for "what content" the ad appears alongside (primarily CTV). + +### Content Standards vs Property/Collection Lists + +- **Content Standards**: Rules-based evaluation of content quality, topics, and safety. Evaluates content dynamically. +- **Property/Collection Lists**: Pre-computed sets of approved or excluded inventory. Static targeting applied at campaign setup. + +### Filter Resolution + +Property and collection lists combine static selections with dynamic filters. Use `resolve: true` on get operations to see the final resolved set of properties or collections. + +--- + +## Error Handling + +Common error codes: + +- `LIST_NOT_FOUND`: Invalid list_id +- `STANDARDS_NOT_FOUND`: Invalid standards_id +- `UNAUTHORIZED`: Not authorized to access this resource +- `VALIDATION_ERROR`: Invalid filter or rule configuration diff --git a/skills/adcp-media-buy/SKILL.md b/skills/adcp-media-buy/SKILL.md new file mode 100644 index 000000000..2e3c93c6e --- /dev/null +++ b/skills/adcp-media-buy/SKILL.md @@ -0,0 +1,446 @@ +--- +name: adcp-media-buy +description: Execute AdCP Media Buy Protocol operations with sales agents - discover advertising products, create and manage campaigns, sync creatives, and track delivery. Use when users want to buy advertising, create media buys, interact with ad sales agents, or test advertising APIs. +--- + +# AdCP Media Buy Protocol + +This skill enables you to execute the AdCP Media Buy Protocol with sales agents. Use the standard MCP tools (`get_products`, `create_media_buy`, `sync_creatives`, etc.) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Media Buy Protocol provides 11 standardized tasks for managing advertising campaigns: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_products` | Discover inventory using natural language | ~60s | +| `list_authorized_properties` | See publisher properties | ~1s | +| `list_creative_formats` | View creative specifications | ~1s | +| `create_media_buy` | Create campaigns | Minutes-Days | +| `update_media_buy` | Modify campaigns | Minutes-Days | +| `get_media_buys` | Retrieve campaign state and status | ~1-5s | +| `sync_creatives` | Upload creative assets | Minutes-Days | +| `sync_catalogs` | Sync product feeds and catalogs | Minutes-Days | +| `list_creatives` | Query creative library | ~1s | +| `get_media_buy_delivery` | Get performance data | ~60s | +| `provide_performance_feedback` | Share outcomes with publishers | ~1-5s | + +## Typical Workflow + +1. **Discover products**: `get_products` with a natural language brief +2. **Review formats**: `list_creative_formats` to understand creative requirements +3. **Create campaign**: `create_media_buy` with selected products and budget +4. **Upload creatives**: `sync_creatives` to add creative assets +5. **Monitor delivery**: `get_media_buy_delivery` to track performance + +--- + +## Task Reference + +### get_products + +Discover advertising products using natural language briefs. + +**Request:** +```json +{ + "buying_mode": "brief", + "brief": "Looking for premium video inventory for a tech brand targeting developers", + "brand": { + "domain": "example.com" + }, + "filters": { + "channels": ["video", "ctv"], + "budget_range": { "min": 5000, "max": 50000 } + } +} +``` + +**Key fields:** +- `buying_mode` (string): Required discriminator - `"brief"` or `"wholesale"` +- `brief` (string): Natural language description of campaign requirements +- `brand` (object): Brand identity - `{ "domain": "acmecorp.com" }` +- `filters` (object, optional): Filter by channels, budget, delivery_type + +**Response contains:** +- `products`: Array of matching products with `product_id`, `name`, `description`, `pricing_options` +- Each product includes `format_ids` (supported creative formats) and `targeting` (available targeting) + +--- + +### list_authorized_properties + +Get the list of publisher properties this agent can sell. + +**Request:** +```json +{} +``` + +No parameters required. + +**Response contains:** +- `publisher_domains`: Array of domain strings the agent is authorized to sell + +--- + +### list_creative_formats + +View supported creative specifications. + +**Request:** +```json +{ + "asset_types": ["video", "image"] +} +``` + +**Key fields:** +- `asset_types` (array, optional): Filter by asset types (image, video, audio, text, html, vast, etc.) +- `name_search` (string, optional): Case-insensitive partial match on name or description + +**Response contains:** +- `formats`: Array of format specifications with dimensions, requirements, and asset schemas + +--- + +### create_media_buy + +Create an advertising campaign from selected products. + +**Request:** +```json +{ + "brand": { + "domain": "acme.com" + }, + "packages": [ + { + "product_id": "premium_video_30s", + "pricing_option_id": "cpm-standard", + "budget": 10000 + } + ], + "start_time": { + "type": "asap" + }, + "end_time": "2024-03-31T23:59:59Z" +} +``` + +**Key fields:** +- `brand` (object, required): Brand identity - `{ "domain": "acmecorp.com" }` +- `packages` (array, required): Products to purchase, each with: + - `product_id`: From `get_products` response + - `pricing_option_id`: From product's `pricing_options` + - `budget`: Amount in dollars + - `bid_price`: Required for auction pricing + - `targeting_overlay`: Additional targeting constraints + - `creative_ids` or `creatives`: Creative assignments +- `start_time` (object, required): `{ "type": "asap" }` or `{ "type": "scheduled", "datetime": "..." }` +- `end_time` (string, required): ISO 8601 datetime + +**Response contains:** +- `media_buy_id`: The created campaign identifier +- `status`: Current lifecycle state — `pending_creatives` (no creatives assigned yet), `pending_start` (waiting for flight date), or `active` (serving immediately) +- `packages`: Created packages with their IDs + +--- + +### update_media_buy + +Modify an existing campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "updates": { + "budget_change": 5000, + "end_time": "2024-04-30T23:59:59Z", + "status": "paused" + } +} +``` + +**Key fields:** +- `media_buy_id` (string, required): The campaign to update +- `updates` (object): Changes to apply - budget_change, end_time, status, targeting, etc. + +--- + +### sync_catalogs + +Sync product catalogs, store locations, job postings, and other structured feeds to a seller account. Supports inline items or external feed URLs. When called without catalogs, returns existing catalogs (discovery mode). + +**Request:** +```json +{ + "account": { + "account_id": "acct_123" + }, + "catalogs": [ + { + "catalog_id": "winter-collection", + "name": "Winter 2025 Collection", + "type": "product", + "items": [ + { "id": "sku-001", "name": "Wool Coat", "price": 299.99, "currency": "USD" } + ] + } + ] +} +``` + +**Key fields:** +- `account` (object, required): Account that owns the catalogs — `{ account_id }` +- `catalogs` (array, optional): Catalog objects to sync. Omit for discovery mode. + - `type` (string, required): `offering`, `product`, `inventory`, `store`, `promotion`, `hotel`, `flight`, `job`, `vehicle`, `real_estate`, `education`, `destination`, `app` + - `items` (array): Inline catalog data (mutually exclusive with `url`) + - `url` (string): External feed URL (mutually exclusive with `items`) + - `feed_format` (string): `google_merchant_center`, `facebook_catalog`, `shopify`, `linkedin_jobs`, `custom` +- `delete_missing` (boolean, optional): Remove catalogs not in this sync (use with caution) +- `dry_run` (boolean, optional): Preview changes without applying + +--- + +### sync_creatives + +Upload and manage creative assets. + +**Request:** +```json +{ + "creatives": [ + { + "creative_id": "hero_video_30s", + "name": "Brand Hero Video", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "video_standard_30s" + }, + "assets": { + "video": { + "url": "https://cdn.example.com/hero.mp4", + "width": 1920, + "height": 1080, + "duration_ms": 30000 + } + } + } + ], + "assignments": { + "hero_video_30s": ["pkg_001", "pkg_002"] + } +} +``` + +**Key fields:** +- `creatives` (array, required): Creative assets to sync + - `creative_id`: Your unique identifier + - `format_id`: Object with `agent_url` and `id` from format specifications + - `assets`: Asset content (video, image, html, etc.) +- `assignments` (object, optional): Map creative_id to package IDs +- `dry_run` (boolean): Preview changes without applying +- `delete_missing` (boolean): Archive creatives not in this sync + +--- + +### list_creatives + +Query the creative library with filtering. + +**Request:** +```json +{ + "filters": { + "status": ["active"] + }, + "limit": 20 +} +``` + +--- + +### get_media_buys + +Retrieve media buy state: status, valid_actions, creative approvals, pending formats, and optional delivery snapshots or revision history. + +**Request:** +```json +{ + "media_buy_ids": ["mb_abc123"], + "include_snapshot": true, + "include_history": 5 +} +``` + +**Key fields:** +- `media_buy_ids` (array, optional): Specific media buy IDs to retrieve +- `account` (object, optional): Filter to a specific account +- `status_filter` (string or array, optional): Filter by status — `pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `rejected`, `canceled`. Defaults to `["active"]` when no IDs provided. +- `include_snapshot` (boolean, optional): Include near-real-time delivery snapshots per package +- `include_history` (integer, optional): Include the last N revision history entries per media buy + +**Response contains:** +- `media_buys`: Array with `media_buy_id`, `status`, `valid_actions`, `packages`, creative approval state +- Optional `snapshot` per package (impressions, spend, pacing) +- Optional `history` entries (revision, timestamp, actor, action, summary) + +--- + +### provide_performance_feedback + +Share performance outcomes with publishers to enable data-driven optimization. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "measurement_period": { + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-31T23:59:59Z" + }, + "performance_index": 1.2, + "metric_type": "conversion_rate", + "feedback_source": "buyer_attribution" +} +``` + +**Key fields:** +- `media_buy_id` (string, required): Publisher's media buy identifier +- `measurement_period` (object, required): Time period with `start` and `end` (ISO 8601) +- `performance_index` (number, required): Normalized score — 0.0 = no value, 1.0 = expected, >1.0 = above expected +- `package_id` (string, optional): Specific package for package-level feedback +- `creative_id` (string, optional): Specific creative for creative-level feedback +- `metric_type` (string, optional): `overall_performance`, `conversion_rate`, `brand_lift`, `click_through_rate`, `completion_rate`, `viewability`, `brand_safety`, `cost_efficiency` +- `feedback_source` (string, optional): `buyer_attribution`, `third_party_measurement`, `platform_analytics`, `verification_partner` + +--- + +### get_media_buy_delivery + +Retrieve performance metrics for a campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "granularity": "daily", + "date_range": { + "start": "2024-01-01", + "end": "2024-01-31" + } +} +``` + +**Response contains:** +- `delivery`: Aggregated metrics (impressions, spend, clicks, etc.) +- `by_package`: Breakdown by package +- `timeseries`: Data points over time if granularity specified + +--- + +## Key Concepts + +### Brand identity + +Brand context is provided by domain reference: + +```json +{ + "brand": { + "domain": "acmecorp.com" + } +} +``` + +The agent resolves the domain to retrieve the brand's identity (name, colors, guidelines, etc.) from its `brand.json` file. + +### Format IDs + +Creative format identifiers are structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies which creative agent defines the format. Use `https://creative.adcontextprotocol.org` for standard IAB formats. + +### Pricing Options + +Products include `pricing_options` array. Each option has: +- `pricing_option_id`: Use this in `create_media_buy` +- `pricing_model`: "cpm", "cpm-auction", "flat-fee", etc. +- `price`: Base price (for fixed pricing) +- `floor`: Minimum bid (for auction) + +For auction pricing, include `bid_price` in your package. + +### Asynchronous Operations + +Operations like `create_media_buy` and `sync_creatives` may require human approval. The response includes: +- `status: "pending"` - Operation awaiting approval +- `task_id` - For tracking async progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid parameters - check required fields +- **401 Unauthorized**: Invalid or missing authentication token +- **404 Not Found**: Invalid product_id, media_buy_id, or creative_id +- **422 Validation Error**: Schema validation failure - check field types + +Error responses include: +```json +{ + "errors": [ + { + "code": "VALIDATION_ERROR", + "message": "budget must be greater than 0", + "field": "packages[0].budget" + } + ] +} +``` + +--- + +## Testing Mode + +Use **sandbox mode** for testing without real transactions. Sandbox is account-level — once a request references a sandbox account, the entire request is treated as sandbox with no real platform calls or spend. + +Check whether the agent supports sandbox via `get_adcp_capabilities`: +```json +{ + "account": { + "sandbox": true + } +} +``` + +To enter sandbox mode, set `sandbox: true` on the account reference: +```json +{ + "account": { + "brand": { "domain": "acme-corp.com" }, + "operator": "acme-corp.com", + "sandbox": true + } +} +``` + +Some sync tasks (`sync_creatives`, `sync_catalogs`) also support a `dry_run` parameter that previews changes without applying them. This is orthogonal to sandbox — you can use `dry_run` in both sandbox and production accounts. + +See [Sandbox mode](https://docs.adcontextprotocol.org/docs/media-buy/advanced-topics/sandbox) for full details. diff --git a/skills/adcp-si/SKILL.md b/skills/adcp-si/SKILL.md new file mode 100644 index 000000000..1d1b297e1 --- /dev/null +++ b/skills/adcp-si/SKILL.md @@ -0,0 +1,206 @@ +--- +name: adcp-si +description: Execute AdCP Sponsored Intelligence (SI) Protocol operations with brand agents - start conversational sessions, send messages, preview offerings, and manage session lifecycle. Use when users want to have conversations with brand agents, explore product offerings, or manage sponsored interactions. +--- + +# AdCP Sponsored Intelligence (SI) Protocol + +This skill enables you to execute the AdCP SI Protocol with brand agents. SI enables conversational commerce sessions where users engage directly with brand agents for shopping, inquiries, and transactions. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The SI Protocol provides 4 standardized tasks for managing conversational sessions: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `si_initiate_session` | Start a brand conversation | ~2-5s | +| `si_send_message` | Send a message in an active session | ~1-5s | +| `si_get_offering` | Preview offerings before starting | ~1-3s | +| `si_terminate_session` | End a session | ~1s | + +## Typical Workflow + +1. **Preview** (optional): `si_get_offering` to see what the brand offers before consent +2. **Start session**: `si_initiate_session` with the user's `intent` and consent +3. **Converse**: `si_send_message` to relay user messages and action responses +4. **End**: `si_terminate_session` when done + +--- + +## Task Reference + +### si_initiate_session + +Start a conversational session with a brand agent. + +**Request:** +```json +{ + "intent": "I'm interested in your winter jacket collection", + "identity": { + "consent_granted": true, + "consent_timestamp": "2025-01-15T10:30:00Z", + "consent_scope": ["email", "name"], + "user": { + "email": "user@example.com", + "name": "Jane Smith", + "locale": "en-US" + } + }, + "placement": "chatgpt_search" +} +``` + +**Key fields:** +- `intent` (string, required): Natural language description of user intent — the conversation handoff from host to brand agent +- `identity` (object, required): User identity with consent status + - `consent_granted` (boolean, required): Whether user consented to share identity + - `consent_timestamp` (string, optional): ISO 8601 timestamp of consent + - `consent_scope` (array, optional): Fields user agreed to share + - `user` (object, optional): PII (only if consent_granted is true) — `email`, `name`, `locale` + - `anonymous_session_id` (string, optional): Session ID if no consent +- `media_buy_id` (string, optional): AdCP media buy ID if triggered by advertising +- `placement` (string, optional): Where the session was triggered +- `offering_id` (string, optional): Brand-specific offering reference +- `offering_token` (string, optional): Token from `si_get_offering` for session continuity +- `supported_capabilities` (object, optional): Host platform capabilities (modalities, components, commerce) +- `context` (object, optional): Opaque correlation data (e.g., `{"trace_id": "abc-123"}`) echoed unchanged in the response — never parsed by the brand agent + +**Response contains:** +- `session_id`: Use in subsequent `si_send_message` and `si_terminate_session` calls +- `greeting`: Brand agent's initial message +- `suggested_actions`: Optional UI elements (buttons, quick replies) + +--- + +### si_send_message + +Send a message within an active SI session. + +**Text message:** +```json +{ + "session_id": "sess_abc123", + "message": "Do you have this in size medium?" +} +``` + +**Action response (button click, form submit):** +```json +{ + "session_id": "sess_abc123", + "action_response": { + "action": "add_to_cart", + "element_id": "btn_add_cart_sku789", + "payload": { + "size": "M", + "color": "navy" + } + } +} +``` + +**Key fields:** +- `session_id` (string, required): Session ID from `si_initiate_session` +- `message` (string, conditional): User's text message. Required unless `action_response` is provided. +- `action_response` (object, conditional): Response to a UI action — `action`, `element_id`, `payload`. Required unless `message` is provided. + +**Response contains:** +- `message`: Brand agent's response text +- `suggested_actions`: Optional UI elements for next interaction +- `components`: Optional rich UI components (product cards, carousels, forms) + +--- + +### si_get_offering + +Get offering details and availability before initiating a session. Allows showing rich previews before asking for user consent. + +**Request:** +```json +{ + "offering_id": "winter-collection-2025", + "intent": "Looking for warm jackets under $200", + "include_products": true, + "product_limit": 5 +} +``` + +**Key fields:** +- `offering_id` (string, required): Offering identifier from the catalog +- `intent` (string, optional): Natural language description of user intent for personalized results (no PII) +- `include_products` (boolean, optional): Include matching products +- `product_limit` (number, optional): Max products to return (default 5, max 50) +- `context` (object, optional): Opaque correlation data echoed unchanged in the response — never parsed by the brand agent + +**Response contains:** +- `offering`: Offering details (name, description, availability) +- `products`: Matching products if `include_products` is true +- `offering_token`: Pass to `si_initiate_session` for session continuity + +--- + +### si_terminate_session + +End an SI session. + +**Request:** +```json +{ + "session_id": "sess_abc123", + "reason": "user_exit" +} +``` + +**Key fields:** +- `session_id` (string, required): Session ID to terminate +- `reason` (string, required): Why the session is ending — `handoff_transaction`, `handoff_complete`, `user_exit`, `session_timeout`, `host_terminated` +- `termination_context` (object, optional): Conversation summary, transaction intent, and cause for the termination +- `context` (object, optional): Opaque correlation data echoed unchanged in the response — never parsed by the brand agent + +**Reason values:** +- `handoff_transaction`: User is being redirected to complete a transaction +- `handoff_complete`: Transaction completed within the session +- `user_exit`: User chose to leave +- `session_timeout`: Session timed out +- `host_terminated`: Host platform ended the session + +--- + +## Key Concepts + +### Consent Model + +SI sessions require explicit user consent before sharing PII: +- `consent_granted: false` + `anonymous_session_id`: Anonymous session +- `consent_granted: true` + `user` object: Personalized session with identity + +### Session Lifecycle + +``` +si_get_offering (optional) → si_initiate_session → si_send_message (repeat) → si_terminate_session +``` + +Sessions are stateful. The brand agent maintains context across messages within a session. + +### Placements + +Where the SI session was triggered: +- `chatgpt_search`: Within ChatGPT search results +- `publisher_article`: On a publisher's article page +- `social_feed`: In a social media feed +- `ctv_overlay`: On a CTV streaming overlay + +--- + +## Error Handling + +Common error codes: + +- `SESSION_NOT_FOUND`: Invalid or expired session_id +- `SESSION_EXPIRED`: Session timed out +- `CONSENT_REQUIRED`: Attempting to share PII without consent +- `OFFERING_NOT_FOUND`: Invalid offering_id +- `RATE_LIMITED`: Too many messages in quick succession diff --git a/skills/adcp-signals/SKILL.md b/skills/adcp-signals/SKILL.md new file mode 100644 index 000000000..0b8deca13 --- /dev/null +++ b/skills/adcp-signals/SKILL.md @@ -0,0 +1,204 @@ +--- +name: adcp-signals +description: Execute AdCP Signals Protocol operations with signal agents - discover audience signals using natural language and activate them on DSPs or sales agents. Use when users want to find targeting data, activate audience segments, or work with signal providers. +--- + +# AdCP Signals Protocol + +This skill enables you to execute the AdCP Signals Protocol with signal agents. Use the standard MCP tools (`get_signals`, `activate_signal`) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Signals Protocol provides 2 standardized tasks for discovering and activating targeting data: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_signals` | Discover signals using natural language | ~60s | +| `activate_signal` | Activate a signal on a platform/agent | Minutes-Hours | + +## Typical Workflow + +1. **Discover signals**: `get_signals` with a natural language description of targeting needs +2. **Review options**: Evaluate signals by coverage, pricing, and deployment status +3. **Activate if needed**: `activate_signal` for signals not yet live on your platform +4. **Use in campaigns**: Reference the activation key in your media buy targeting + +--- + +## Task Reference + +### get_signals + +Discover signals based on natural language description, with deployment status across platforms. + +**Request:** +```json +{ + "signal_spec": "High-income households interested in luxury goods", + "destinations": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" + } + ], + "countries": ["US"], + "filters": { + "max_cpm": 5.0, + "catalog_types": ["marketplace"] + }, + "max_results": 5 +} +``` + +**Key fields:** +- `signal_spec` (string, conditional): Natural language description of desired signals. Required unless `signal_ids` is provided. +- `destinations` (array, optional): Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. Each item: `type`, `platform`/`agent_url`, optional `account`. +- `countries` (array, optional): ISO 3166-1 alpha-2 country codes where signals will be used +- `filters` (object, optional): Filter by `catalog_types`, `data_providers`, `max_cpm`, `min_coverage_percentage` +- `max_results` (number, optional): Limit number of results + +**Deployment types:** +```json +// DSP platform +{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123" } + +// Sales agent +{ "type": "agent", "agent_url": "https://salesagent.example.com" } +``` + +**Response contains:** +- `signals`: Array of matching signals with: + - `signal_agent_segment_id`: Use this in `activate_signal` + - `name`, `description`: Human-readable signal info + - `data_provider`: Source of the signal data + - `coverage_percentage`: Reach relative to agent's population + - `deployments`: Status per platform with `is_live`, `activation_key`, `estimated_activation_duration_minutes` + - `pricing`: CPM and currency + +--- + +### activate_signal + +Activate a signal for use on a specific platform or agent. + +**Request:** +```json +{ + "signal_agent_segment_id": "luxury_auto_intenders", + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123-ttd" + } + ] +} +``` + +**Key fields:** +- `signal_agent_segment_id` (string, required): From `get_signals` response +- `deployments` (array, required): Target deployment(s) with `type`, `platform`/`agent_url`, and optional `account` + +**Response contains:** +- `deployments`: Array with activation results per target + - `activation_key`: The key to use for targeting (segment ID or key-value pair) + - `deployed_at`: ISO timestamp when activation completed + - `estimated_activation_duration_minutes`: Time remaining if async +- `errors`: Any warnings or errors encountered + +--- + +## Key Concepts + +### Deployment Targets + +Signals can be activated on two types of targets: + +**DSP Platforms:** +```json +{ + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" +} +``` + +**Sales Agents:** +```json +{ + "type": "agent", + "agent_url": "https://wonderstruck.salesagents.com" +} +``` + +### Activation Keys + +When signals are live, the response includes an activation key for targeting: + +**Segment ID format (typical for DSPs):** +```json +{ + "type": "segment_id", + "segment_id": "ttd_segment_12345" +} +``` + +**Key-Value format (typical for sales agents):** +```json +{ + "type": "key_value", + "key": "audience_segment", + "value": "luxury_auto_intenders" +} +``` + +### Signal Types + +- **marketplace**: Licensed from data providers (CPM pricing) +- **custom**: Built for specific principal accounts +- **owned**: Private signals from your own data (no cost) + +### Coverage Percentage + +Indicates signal reach relative to the agent's population: +- 99%: Very broad signal (matches most identifiers) +- 50%: Medium signal +- 1%: Very niche signal + +### Asynchronous Operations + +Signal activation may take time. Check the response: +- `is_live: true` + `activation_key`: Ready to use immediately +- `is_live: false` + `estimated_activation_duration_minutes`: Activation in progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error codes: + +- `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Invalid signal_agent_segment_id +- `ACTIVATION_FAILED`: Could not activate signal +- `ALREADY_ACTIVATED`: Signal already active on target +- `DEPLOYMENT_UNAUTHORIZED`: Not authorized for platform/account +- `AGENT_NOT_FOUND`: Private agent not visible to this principal +- `AGENT_ACCESS_DENIED`: Not authorized for this signal agent + +Error responses include: +```json +{ + "errors": [ + { + "code": "DEPLOYMENT_UNAUTHORIZED", + "message": "Account not authorized for this data provider", + "field": "deployment.account", + "suggestion": "Contact your account manager to enable access" + } + ] +} +``` diff --git a/skills/call-adcp-agent/SKILL.md b/skills/call-adcp-agent/SKILL.md index c230034ed..f77c13f9a 100644 --- a/skills/call-adcp-agent/SKILL.md +++ b/skills/call-adcp-agent/SKILL.md @@ -1,29 +1,31 @@ --- name: call-adcp-agent -description: Use when calling an AdCP agent as a buyer — media buys, signal lookups, creative sync, delivery reports. Covers the wire contract, minimal payload shapes, async flow (`status:'submitted'` + `task_id`), and error recovery from `adcp_error.issues[]`. +description: Wire-level invariants for any AdCP buyer call — idempotency_key replay semantics, account `oneOf` variants, async `status:'submitted'`+`task_id` polling, error recovery from `adcp_error.issues[]`. Load before any per-protocol task skill (adcp-media-buy, adcp-creative, adcp-signals, adcp-governance, adcp-si, adcp-brand) when calling an AdCP agent as a buyer. +adcp_version: "3.x" +type: cross-cutting --- # Call an AdCP agent ## Overview -AdCP (Ad Context Protocol) agents expose a fixed tool surface (`get_products`, `create_media_buy`, `get_signals`, …) over MCP or A2A. Tool names come from `get_adcp_capabilities`; exact request/response shapes live in the spec's bundled JSON Schemas at `schemas/cache/3.0.0/bundled//-{request,response}.json` (or via `get_schema` when the agent exposes it). This skill teaches the invariants that don't live cleanly in any schema: cross-tool patterns, async flow, error recovery. +AdCP (Ad Context Protocol) agents expose a fixed tool surface (`get_products`, `create_media_buy`, `get_signals`, …) over MCP or A2A. Tool names come from `get_adcp_capabilities`; exact request/response shapes live in the spec's bundled JSON Schemas at `dist/schemas//bundled//-{request,response}.json` (or via `get_schema` when the agent exposes it). This skill teaches the invariants that don't live cleanly in any schema: cross-tool patterns, async flow, error recovery. ## When to Use - User wants to call a publisher / SSP / retail media network over AdCP - Tool names like `get_products`, `create_media_buy`, `sync_creatives`, `get_signals` appear in the available-tools list - Agent card advertises `protocolVersion: '0.3.0'` with `skills` listing AdCP tool names -- **Not this skill:** building an AdCP seller agent (see `skills/build-seller-agent/`) +- **Not this skill:** building an AdCP seller agent (see `@adcp/client/skills/build-seller-agent/` and analogous SDK skills) ## Discovery chain Walk these in order on first contact: -1. **Agent card** (A2A) or **`tools/list`** (MCP): returns tool NAMES. Post-`@adcp/client` [#915](https://github.com/adcontextprotocol/adcp-client/pull/915), `tools/list` no longer publishes per-tool parameter schemas — everything shows `{type: 'object', properties: {}}`. Don't try to infer shape from here. +1. **Agent card** (A2A) or **`tools/list`** (MCP): returns tool NAMES. AdCP MCP servers no longer publish per-tool parameter schemas in `tools/list` — everything shows `{type: 'object', properties: {}}`. Don't try to infer shape from here. 2. **`get_adcp_capabilities`**: returns supported protocols (`media_buy`, `signals`, `creative`, …), AdCP major versions, feature flags. Tells you WHICH tools this agent supports, not how to call them. -3. **`get_schema(tool_name)`** *(when the agent exposes it — upstream [adcp#3057](https://github.com/adcontextprotocol/adcp/issues/3057))*: returns the JSON Schema for a tool's request/response. Preferred over reading bundled schemas when available. -4. **Bundled schemas** (offline, authoritative): `schemas/cache/3.0.0/bundled//-request.json` and `-response.json`. `npm run sync-schemas` in `@adcp/client` pulls them from the spec repo; substitute your target AdCP major version in the path. +3. **`get_schema(tool_name)`** *(when the agent exposes it — pending standardization in [#3057](https://github.com/adcontextprotocol/adcp/issues/3057), not yet universal)*: returns the JSON Schema for a tool's request/response. Preferred over reading bundled schemas when available. +4. **Bundled schemas** (offline, authoritative): `dist/schemas//bundled//-request.json` and `-response.json`. The adcp main repo publishes these; SDKs sync them (`npm run sync-schemas` in `@adcp/client`, equivalents in other SDKs). Substitute your target AdCP major version in the path. ## Non-obvious rules every buyer must follow @@ -241,12 +243,13 @@ If your symptom isn't here, fall through to the next section. Priority order: 1. Re-read the failure's `issues[]`. The pointer list plus this skill covers 80% of cases. -2. Call `get_schema(tool_name)` if the agent exposes it (adcp#3057). -3. Read the bundled JSON Schema at `schemas/cache/3.0.0/bundled//-request.json`. -4. Consult `docs/guides/VALIDATE-YOUR-AGENT.md` in `@adcp/client` for per-specialism patterns. +2. Call `get_schema(tool_name)` if the agent exposes it (see [#3057](https://github.com/adcontextprotocol/adcp/issues/3057) for the pending standard). +3. Read the bundled JSON Schema at `dist/schemas//bundled//-request.json`. +4. Consult the per-protocol skill in this repo (`skills/adcp-media-buy/`, `skills/adcp-creative/`, …) for specialism-specific patterns. ## Related -- `skills/build-seller-agent/SKILL.md` — building agents on the other side of the call -- `docs/guides/BUILD-AN-AGENT.md` — framework reference -- `schemas/cache/3.0.0/` — canonical JSON Schemas (every tool, pinned to the AdCP version `@adcp/client` bundles) +- [Calling an agent (docs)](https://adcontextprotocol.org/docs/protocol/calling-an-agent) — human-readable narrative form of this skill +- `skills/adcp-media-buy/`, `skills/adcp-creative/`, `skills/adcp-signals/`, `skills/adcp-governance/`, `skills/adcp-si/`, `skills/adcp-brand/` — per-protocol task skills (layered on top of this one) +- `@adcp/client/skills/build-seller-agent/SKILL.md` — building agents on the other side of the call +- `dist/schemas//bundled/` — canonical JSON Schemas (every tool, pinned to the AdCP version)