Interoperability size target
MCP hosts and gateways impose different undocumented response ceilings. For pageable reads and other read-only tasks that offer field projection, AdCP producers SHOULD keep each serialized MCP response at or below 128 KiB (131,072 bytes). This is an interoperability target, not permission for a client to allocate without bounds: clients SHOULD still enforce the streamed 1 MiB whole-message cap described in MCP response extraction. For those read paths, use the task’s cursor, reduce the requested fields or breakdowns, or return a smaller schema-valid page with continuation metadata before the response exceeds 128 KiB. Do not invent a partial response for a task whose schema does not define one. Do not apply the 128 KiB target by turning a completed mutation into an error. If a producer can determine before execution that it cannot return the mutation’s required result within a host limit, it MUST reject the request before side effects. Once side effects occur, it MUST return the schema-valid committed result or a task-defined asynchronous handoff; it must not report failure merely to satisfy the size target.tools/list is cursor-pageable at the MCP layer even though it is not an AdCP task. Servers SHOULD paginate the live tool catalog so each page remains under the same 128 KiB target. Clients that need the complete catalog MUST follow nextCursor until it is absent. Capability-selected tool registration and concise x-tool-summary descriptions reduce each page further; do not publish the full AdCP catalog, response schemas, or long reference prose in a session’s tools/list result.
Wire response ≠ model context
The single most important point: the bytes on the wire are not what your model has to consume. AdCP responses are structured data. An MCP response arrives asstructuredContent — typed JSON that the client parses before any model sees it. An A2A response pairs a human-readable TextPart with an authoritative DataPart. In both cases, a well-built client stores the full response and projects or summarizes it before prompting the next model turn.
Ask for a plan when you want curation
Userequest_proposals when the seller should turn a campaign brief into a
small set of actionable plans:
refine_proposals requests to narrow the plan without retransmitting the
full source response.
Use list_products instead when the buyer wants published offers or maintains
a wholesale mirror. A mirror is a feed-synchronization topology, not a cheaper
proposal mode.
fields selector for lightweight discovery
When you only need a subset of published product data, pass fields to
list_products:
- Your agent is scanning multiple sellers and only needs IDs and prices for initial comparison
- You want to skip heavy fields like
product_card,product_card_detailed,placements, or signal metadata - You’re building a summary view before drilling into specific products
fields, the seller returns the full product object — including visual card definitions, placement specs, and any bundled signal metadata. For a first-pass discovery across multiple sellers, that’s more data than you need.
Pagination for cardinality control
list_products uses cursor-based pagination:
Response:
Pass
next_cursor into the next list_products request. A mirror stores
feed_version with its cache_scope and uses account-level product webhooks
for routine changes. Conditional reads with if_feed_version repair gaps or
confirm that a selected feed is unchanged; they do not replace a healthy
webhook stream.
Truncation flags on delivery
get_media_buy_delivery returns breakdown arrays (by geo, by creative, by day, etc.) that can grow large. Each breakdown array has a sibling boolean flag — by_geo_truncated, by_creative_truncated, etc. — that tells you whether the returned rows are the complete set or just the top-N:
When a flag is
true, the returned rows are ordered by the applied sort metric in the applied direction (descending by default — the most significant breakdowns; sort_direction: "asc" deliberately returns the least significant, e.g. worst performers) and the tail is omitted. The response echoes the applied sort per breakdown (by_X_sorted_by / by_X_sort_direction), so a fallback to spend is visible. This is by design: delivery breakdowns are for optimization decisions, not archival reporting. If you need the full dataset, use the seller’s native reporting API.
Putting it together
A token-efficient AdCP integration follows this pattern:- Use
request_proposalsfor seller curation. Ask for plans instead of downloading a feed and prompting a model to rediscover seller expertise. - Use typed
refine_proposals. Carry proposal IDs and changes, not the full prior response, into the next turn. - Store raw responses client-side. Don’t feed full product objects into the model context. Extract what matters, summarize the rest.
- Read truncation flags before paginating delivery. If
by_geo_truncated: false, you already have everything — no need for follow-up calls. - Treat wholesale as synchronization. Bootstrap with
list_products, apply product webhooks, and use versioned reads for repair.
Discovery and planning
Choose published offers, wholesale mirroring, or seller-planned proposals.
list_products reference
Field selection, cursor pagination, feed versions, and mirror repair.
Delivery reporting
Breakdown arrays, truncation flags, and sort semantics.
How agents communicate
MCP vs A2A transport and how responses are structured.
Media products
Product structure,
product_card vs product_card_detailed, and rendering.