adcontextprotocol/adcp/call-adcp-agent
Claude Code and agent skill by adcontextprotocol/adcp
- community
Checking that the SKILL.md file is still in the repository.
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.
Install
npx vskill@latest install adcontextprotocol/adcp/call-adcp-agentSKILL.md
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 come from get_schema(tool_name) when the agent exposes it, otherwise from the bundled JSON Schemas your SDK ships (the layout differs by SDK — see "Discovery chain" below). 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_signalsappear in the available-tools list - A2A 1.0 Agent Card advertises
https://adcontextprotocol.org/extensions/adcp/v3undercapabilities.extensions[], withskillslisting AdCP task names - 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:
- Agent card (A2A) or `tools/list` (MCP): returns tool NAMES. For A2A 1.0, confirm the versioned AdCP profile under
capabilities.extensions[]and activate it withA2A-Extensionson every call. Don't infer runtime AdCP capabilities from extension params. - `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. - `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.
- Bundled schemas (offline, authoritative): every SDK ships the AdCP JSON Schemas locally. Path differs by SDK — spec repo source uses
dist/schemas/<version>/bundled/,@adcp/clientputs them atschemas/cache/<version>/bundled/afternpm run sync-schemas, Python and Go SDKs use their own conventions. Don't hardcode a path — let the SDK's loader find them, or ask the developer. Each schema is<protocol>/<tool>-{request,response}.jsononce you locate the bundle. The canonical source for every SDK ishttps://adcontextprotocol.org/protocol/<version>.tgz.
Non-obvious rules every buyer must follow
idempotency_key is required on every mutating tool
UUID format. The key is your retry-safety guarantee — and the most common way naive callers create duplicate media buys is by misunderstanding it:
- Same key on retry → replay. The server returns the SAME response — same
task_id, samemedia_buy_id, same shape, byte-for-byte. Use this for transport-level retries (timeout, 5xx, dropped connection). - Fresh key on retry → NEW operation. Generating a new UUID because the previous attempt failed is how you double-book. Reuse the key until you've seen a terminal response (success, error, or non-retryable error).
- Same key, different canonical body → `IDEMPOTENCY_CONFLICT`. Servers MUST reject. Do not silently apply the second body; do not silently replay the first. If your planner re-ran and produced different bytes, the intent changed — mint a new key.
- Same key while first request still running → `IDEMPOTENCY_IN_FLIGHT`. Server returns this with top-level
error.retry_after(seconds) when it doesn't want to block. Wait the hint and retry with the same key — minting a fresh key here turns a safe retry into a double-execution race. - For async flows, the replayed response carries the same `task_id`, so polling continues against the same task instead of forking a duplicate.
Required on: create_media_buy, update_media_buy, sync_creatives, sync_audiences, sync_accounts, sync_catalogs, sync_event_sources, sync_plans, sync_governance, activate_signal, acquire_rights, log_event, report_usage, provide_performance_feedback, report_plan_outcome, create_property_list, update_property_list, delete_property_list, create_collection_list, update_collection_list, delete_collection_list, create_content_standards, update_content_standards, calibrate_content, si_initiate_session, si_send_message.
Missing the key → adcp_error.code: 'VALIDATION_ERROR' with /idempotency_key in issues.
account is a oneOf — pick ONE variant, send ONLY its fields
Probably the single most common stumble for naive LLMs. account is a discriminated union. Per AdCP 3.0, two variants on create_media_buy / update_media_buy:
Do NOT merge required fields across variants — additionalProperties: false on each variant means {account_id, brand} fails BOTH. Pick one variant and send only its fields. Always check the specific tool's schema because other tools (e.g. sync_creatives) may accept a superset.
brand takes {domain} — not {brand_id}
Async responses: status: 'submitted' means "queued, poll later"
A mutating tool can return one of three shapes:
When you see status: 'submitted', the work is NOT complete. Under the AdCP v3 A2A profile, the enclosing A2A Task is completed and the AdCP task_id appears only in the artifact DataPart. Poll by sending fresh profile invocations of get_task_status with that task_id; do not poll the completed A2A Task and do not look for artifact.metadata.adcp_task_id. On MCP, use the AdCP polling task the agent advertises.
packages[*] on media buys
budget is a number (not {amount, currency} — currency is implied by the pricing option). Required per package: product_id, budget, pricing_option_id. buyer_ref is optional but strongly recommended as a buyer-side correlation id across retries and reporting.
Webhook signing — omit authentication for new integrations
When you include push_notification_config in a request, do not set authentication unless you are integrating with a legacy receiver. Omitting authentication selects the default: the seller signs each inbound webhook POST with its RFC 9421 adcp_use: "webhook-signing" key, published at the jwks_uri in its own brand.json agents[] entry. You verify against the seller's JWKS. No shared secret crosses the wire.
Presence of authentication is a switch, not a fallback — it opts the seller into Bearer or HMAC-SHA256 and disables 9421 for that registration. A buyer MUST NOT attempt "try 9421 first, fall back to HMAC" verification — mode is fixed at registration time.
The authentication block (Bearer / HMAC-SHA256) is deprecated and sellers MAY decline to support it. It is removed in AdCP 4.0. The token field (a correlation token echoed back in the webhook payload) is separate from authentication and is not deprecated.
See Webhook callbacks for the full verifier checklist and downgrade-resistance rules.
Error envelope — read issues[] to recover
Every validation failure produces:
Required fields — every conformant validator surfaces these:
issues[].pointer— RFC 6901 JSON Pointer to the field.issues[].keyword— AJV keyword (required,type,oneOf,anyOf,additionalProperties,format,enum).issues[].variants— when the keyword isoneOforanyOf, each entry lists one variant'srequired+ declaredproperties. Pick ONE variant, send only itsrequiredfields. This is the fastest recovery path when you didn't know the field was a union.
Spec-optional wire fields — sellers MAY emit per `error.json`:
issues[].schema_id—$idof the rejecting (sub-)schema (e.g./schemas/3.1.0/core/activation-key.json). Diagnostic; the actionable lever isdiscriminator+variants+pointer. Sellers MUST omit when the rejection is against a private extension or pre-release element. See error-handling.mdx.issues[].discriminator—[{property_name, value}, …]pairs identifying the const-discriminatedoneOf/anyOfvariant the validator selected from values present in the payload. Reads as "you targeted this branch; the missing/wrong fields are at the same level." Compound discriminators likeaudience-selector's(type, value_type)produce two-entry arrays. Example:discriminator: [{property_name: 'type', value: 'key_value'}]pluspointer: '/deployments/0/activation_key/key'andkeyword: 'required'means "you picked thekey_valueactivation_key variant and it requires top-levelkeyandvalue."
Both fields are optional in the spec — their presence shortens recovery; their absence just means falling back to pointer + keyword + variants. They are wire-level: a Python or Go caller reading the raw JSON sees them as schema_id and discriminator. SDKs that normalize keys (e.g. @adcp/sdk camelCases to schemaId) surface the SDK-shaped name.
Recovery order: patch the pointers using keyword + variants, resend. If discriminator is present, prefer it — it names the branch directly so you don't have to walk variants. If schema_id is present, use it for diagnostic logging only. Three attempts should cover every field.
SDK-side enrichment. Some SDKs synthesize additional fields client-side after parsing — e.g.@adcp/sdkaddshint(one-sentence curated recipes for known shape gotchas) andallowedValues(closed enum lists forkeyword: 'enum'). These are not wire fields and are not emitted by sellers; if you're not using that SDK, you won't see them regardless of the seller. When present, prefer them over walkingvariants. See your SDK's docs for the full list.
Minimal working examples
get_products
Returns { products: [{ product_id, name, description, delivery_type, pricing_options, ... }] }.
create_media_buy
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/call-adcp-agent/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/adcontextprotocol/adcp/call-adcp-agent)