Documentation Index
Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
Use this file to discover all available pages before exploring further.
Non-normative. This page describes a complementary harness pattern, not a compliance tier. The recipe builds on top of the storyboard runner (Validate your agent) — it does not replace storyboards or staging integration tests, and is not a certification gate. The mock-fixture conventions described here (
/_lookup/<resource>, /_debug/traffic) are reference implementations in @adcp/client; alternative SDK implementations may diverge. Treat as harness convention, not protocol contract.The four-step recipe
The mock-server CLI shown above ships in
@adcp/client (TypeScript). If your CI runs in another language, the simplest pattern is to run the TS CLI as a sidecar (e.g. a Docker service container in GitHub Actions, or a separate Node process) — the agent under test stays in its native language, only the mock + storyboard runner are TS. A Python or Go SDK shipping its own mock-server would re-implement the conventions described below; until that lands, the TS CLI is the reference.Why traffic counters
Storyboards check the AdCP wire contract: did the response match the schema, did the agent advertise the right tools, did context echo. They do not assert what happened behind the wire. The CLAUDE.md guidance for this repo puts it directly: storyboards are assertions, not ground truth. Adapters that look correct under shape-only validation but skip the upstream entirely have shipped to staging more than once. Common shapes:- Handler short-circuits before the upstream call when the input doesn’t match a non-spec branch (e.g. empty
members[]onsync_audiences→ returns shape-valid empty response, never POSTs). - OAuth client is wired but never invoked from any handler; tree-shaking is defeated with a
void fetchUpstreamToken;literal. - Synthetic placeholder data is injected to satisfy the upstream’s required-field schema, masking real-data shape mismatches.
What’s available
The reference TS implementation (@adcp/client) ships four mock-server specialisms covering distinct upstream surface shapes. These are not exhaustive — they exist to exercise representative auth/tenancy/payload patterns, and other specialisms reuse the closest match.
| Specialism | Mimics | Auth | Multi-tenant scope |
|---|---|---|---|
signal-marketplace | LiveRamp / Lotame / data marketplace | Static Bearer | Header (X-Operator-Id) |
creative-template | Celtra / Innovid creative-management platform | Static Bearer | Path (/v3/workspaces/{ws}/…) |
sales-social | TikTok / Meta-shaped social ads platform | OAuth 2.0 client_credentials with refresh | Path (/v1.3/advertiser/{advertiser_id}/…) |
sales-guaranteed | GAM / FreeWheel guaranteed-sales platform | Static Bearer | Header (X-Network-Code) |
account.advertiser, account.operator) are SDK conventions for binding AdCP requests to upstream tenants, not normative AdCP terms. See @adcp/client source for the canonical mapping.
Each mock exposes:
- The upstream’s domain endpoints — shaped to match the real platform’s public contract.
GET /_lookup/<resource>?<adcp_field>=<value>— runtime resolution from AdCP-side identifiers to upstream tenant IDs. Harness convention.GET /_debug/traffic— hit counters keyed by<METHOD> <route-template>, no auth, harness-only. Harness convention. Read after the storyboard run; assert each headline route is hit at least once.
CI integration
Reference shape for a GitHub Actions job, language-agnostic:≥ 1 per headline route — that proves the handler reached the upstream. Stronger assertions (per-route counts, distinct-payload verification) require encoding storyboard-payload expectations and aren’t worth the maintenance burden in a pre-staging gate. If your storyboard exercises 3 audience uploads, expect 3 hits to custom_audience/upload; if it doesn’t, the storyboard’s payload coverage is the lever to pull, not the gate’s threshold.
For agents claiming multiple specialisms, run one CI job per specialism in parallel; each gets its own mock-server port pair (agent + upstream). Jobs are independent.
Iteration loop
Realistically your first run will not pass both gates. Common shapes and how to debug:- Storyboard
passingbut traffic gate fails on N endpoints — classic façade. The handlers for those routes either short-circuited or never got exercised by the storyboard’s input. - Storyboard
partialwith cascade skips — an early step (get_products,get_signals) returned a shape-valid response missing fields that the runner extracts state from. Downstream steps skip withunresolved context variables. Fix the early step’s response shape and most cascade skips clear. - Storyboard
failingon a single step + traffic gate clean — usually a one-line shape bug (wrong field name, missing required field, status mismatch). The per-stepdetailsnames the field via JSON pointer. - Traffic gate empty (0 hits everywhere) + agent appears to start — agent boot threw a recoverable error after listening on port. Check the agent’s stderr.
npx @adcp/client@latest storyboard step <agent> <storyboard_id> <step_id> to isolate the failing step. Skips the cascade, runs a single tool call, sub-second feedback. Don’t run the full storyboard until the isolated step passes — saves minutes per iteration.
Limitations
Be honest with your team about what these gates do and don’t catch:Storyboard limitations
- Storyboards under-cover payload variety. A storyboard step may pass shape with an empty input where a real adapter never gets exercised on the variant that matters. Tracked at adcontextprotocol/adcp#3785.
Runner / tooling caveats
- Storyboard cascade skips silently when an early step’s response is shape-valid but missing fields the runner extracts state from. The error you see is on the downstream step, not the early one — investigate “skipped” steps before “failed”. Tracked at adcontextprotocol/adcp#3796 (runner-side).
- Mock seed data may not match storyboard fixture inputs. If you see 404s on
_lookup/<resource>, the storyboard’s payloads may reference IDs the mock doesn’t seed. Either widen the mock’s seed or seed scenario state at runtime via the compliance test controller.
Traffic gate limitations
- Necessary, not sufficient. A handler can call upstream with synthetic placeholder data and still satisfy the hit-count assertion. For agents in regulated channels (audience uploads, conversion tracking, signed requests), additional integration tests against the real upstream’s payload validation are still required before production.
- Idempotency replay is not exercised by traffic counters. A façade that ignores
idempotency_keyand doubles upstream writes will pass the hit-count gate. Use the storyboard’s idempotency-replay scenarios + a separate counter check (sameidempotency_key→ same hit count) if your platform has at-most-once semantics. - Outbound webhook delivery is not exercised by upstream traffic counters. Traffic counters live on the upstream the agent calls into; agent → buyer webhook signing/delivery is graded by the storyboard runner’s
--webhook-receiverflag, separately. Both gates apply for adapters that emit webhooks.
What’s next
- Validate your agent — the broader storyboard-runner-driven validation checklist (fuzz, multi-instance, request-signing, webhook conformance).
- Compliance test controller — seed scenario state at runtime when storyboards need fixtures the mock doesn’t provide.
- Build an agent — the language-agnostic guide to building an AdCP agent.
- Compliance catalog — the full taxonomy of universal / protocol / specialism storyboards.