Problem
preview_creative is the only tool in AdCP whose request schema is a z.union() of three variants (single, batch, variant). This means:
-
Cannot use server.tool(name, schema.shape, handler) — unions don't have .shape. Every other tool works with this pattern.
-
Server frameworks can't auto-register it — createAdcpServer and similar builders wire tools to schemas automatically. preview_creative requires manual registration.
-
Workaround is fragile — the library splits the union into three exported variant schemas (PreviewCreativeSingleRequestSchema, etc.) with a runtime assertion that the union order hasn't changed.
Proposal
Flatten the union into a single z.object():
request_type: 'single' | 'batch' | 'variant' — required discriminant field
- Variant-specific fields become optional, documented as required-when for each mode
- Validation of conditional requirements happens at the application level, not the schema level
This matches how other AdCP tools with modal behavior work — they use discriminant fields within a flat object (e.g., buying_mode on get_products, action on update_media_buy).
Impact
- Every tool in AdCP works with
server.tool(name, schema.shape, handler)
- Server frameworks can auto-register
preview_creative
- Simpler schema, no union splitting needed
- Slight loss of schema-level validation (conditional requirements become documentation)
Priority
Should land before 3.0 — changing the schema shape after release is breaking.
Discovered during createAdcpServer implementation (#537).
Problem
preview_creativeis the only tool in AdCP whose request schema is az.union()of three variants (single, batch, variant). This means:Cannot use
server.tool(name, schema.shape, handler)— unions don't have.shape. Every other tool works with this pattern.Server frameworks can't auto-register it —
createAdcpServerand similar builders wire tools to schemas automatically.preview_creativerequires manual registration.Workaround is fragile — the library splits the union into three exported variant schemas (
PreviewCreativeSingleRequestSchema, etc.) with a runtime assertion that the union order hasn't changed.Proposal
Flatten the union into a single
z.object():request_type: 'single' | 'batch' | 'variant'— required discriminant fieldThis matches how other AdCP tools with modal behavior work — they use discriminant fields within a flat object (e.g.,
buying_modeonget_products,actiononupdate_media_buy).Impact
server.tool(name, schema.shape, handler)preview_creativePriority
Should land before 3.0 — changing the schema shape after release is breaking.
Discovered during
createAdcpServerimplementation (#537).