Problem
@adcp/sdk@8.1.0-beta.12 now gives us the buyer-side pieces for media-buy available_actions[]:
- generated types/schemas for
MediaBuyAvailableAction, SLAWindow, ActionNotAllowedDetails
getAvailableActions / findAvailableAction
- boolean gates (
canIncreaseBudget, canExtendFlight, etc.)
getActionForMutation
preflightUpdateMediaBuy
ActionNotAllowedError
ACTION_NOT_ALLOWED in the standard error-code runtime table
That is enough for buyer-side preflight, but seller/training-agent implementations still have to hand-roll the server-side enforcement path: preserve/emit available_actions[], map an incoming update_media_buy mutation to the canonical action, reject non-direct modes with ACTION_NOT_ALLOWED, and build the exact error details payload.
During adcontextprotocol/adcp#5018, the training agent still needed local logic equivalent to:
- derive attempted action(s) from
update_media_buy request
- look up the buy's resolved
available_actions[]
- allow direct update only for
self_serve / maybe conditional_self_serve
- reject
requires_proposal / requires_approval direct updates as mode_mismatch
- reject absent actions as
not_supported_on_buy
- return payload error
ACTION_NOT_ALLOWED with details:
attempted_action
reason
currently_available_actions
The SDK has most primitives, but not the seller-side composition that makes this hard to drift from the spec.
Ask
Please add a server-side helper that makes the common seller path “just work”. Rough shape:
const result = enforceAvailableActionForUpdate(currentBuy, updateMediaBuyRequest, {
directModes: ['self_serve', 'conditional_self_serve'],
});
if (!result.ok) {
throw result.error; // or return result.payloadError
}
The helper should use the SDK's canonical getActionForMutation mapping internally and produce a spec-shaped ACTION_NOT_ALLOWED result on denial.
Desired output options:
type EnforceAvailableActionResult =
| { ok: true; actions: ResolvedAction[]; matched: MediaBuyAvailableAction[]; modes: MediaBuyActionMode[] }
| {
ok: false;
code: 'ACTION_NOT_ALLOWED';
recovery: 'correctable';
details: ActionNotAllowedDetails;
error: AdcpError; // server-side structured error, if importing from @adcp/sdk/server
};
It would also help to expose a small builder for the payload shape when a seller has already determined the attempted action/reason:
buildActionNotAllowedError(details: ActionNotAllowedDetails, opts?: { message?: string }): AdcpError
buildActionNotAllowedPayload(details: ActionNotAllowedDetails, opts?: { message?: string }): { errors: [...] }
Why this belongs in the SDK
The attempted-action mapping and ACTION_NOT_ALLOWED details shape are protocol logic, not training-agent-specific behavior. If every seller adapter implements this independently, they will drift on:
update_media_buy field → MediaBuyValidAction mapping
- rollup behavior for legacy coarse actions
mode_mismatch vs unsupported reasons
- whether
currently_available_actions[] is echoed
- recovery classification and message text
The SDK already centralizes buyer preflight; seller enforcement should use the same source of truth.
Non-goals
The SDK does not need to own scenario seeding or the training agent's internal storage shape. Those can remain app-specific. The useful shared piece is the enforcement/error builder over SDK-shaped MediaBuyActionContext + UpdateMediaBuyRequestLike.
Repro context
Protocol repo branch for adcontextprotocol/adcp#5018 added the media_buy_seller/available_actions storyboard. With beta.12, the branch can use SDK types and buyer helpers, but still carries custom seller-side enforcement to pass the storyboard.
Problem
@adcp/sdk@8.1.0-beta.12now gives us the buyer-side pieces for media-buyavailable_actions[]:MediaBuyAvailableAction,SLAWindow,ActionNotAllowedDetailsgetAvailableActions/findAvailableActioncanIncreaseBudget,canExtendFlight, etc.)getActionForMutationpreflightUpdateMediaBuyActionNotAllowedErrorACTION_NOT_ALLOWEDin the standard error-code runtime tableThat is enough for buyer-side preflight, but seller/training-agent implementations still have to hand-roll the server-side enforcement path: preserve/emit
available_actions[], map an incomingupdate_media_buymutation to the canonical action, reject non-direct modes withACTION_NOT_ALLOWED, and build the exact error details payload.During adcontextprotocol/adcp#5018, the training agent still needed local logic equivalent to:
update_media_buyrequestavailable_actions[]self_serve/ maybeconditional_self_serverequires_proposal/requires_approvaldirect updates asmode_mismatchnot_supported_on_buyACTION_NOT_ALLOWEDwith details:attempted_actionreasoncurrently_available_actionsThe SDK has most primitives, but not the seller-side composition that makes this hard to drift from the spec.
Ask
Please add a server-side helper that makes the common seller path “just work”. Rough shape:
The helper should use the SDK's canonical
getActionForMutationmapping internally and produce a spec-shapedACTION_NOT_ALLOWEDresult on denial.Desired output options:
It would also help to expose a small builder for the payload shape when a seller has already determined the attempted action/reason:
Why this belongs in the SDK
The attempted-action mapping and
ACTION_NOT_ALLOWEDdetails shape are protocol logic, not training-agent-specific behavior. If every seller adapter implements this independently, they will drift on:update_media_buyfield →MediaBuyValidActionmappingmode_mismatchvs unsupported reasonscurrently_available_actions[]is echoedThe SDK already centralizes buyer preflight; seller enforcement should use the same source of truth.
Non-goals
The SDK does not need to own scenario seeding or the training agent's internal storage shape. Those can remain app-specific. The useful shared piece is the enforcement/error builder over SDK-shaped
MediaBuyActionContext+UpdateMediaBuyRequestLike.Repro context
Protocol repo branch for adcontextprotocol/adcp#5018 added the
media_buy_seller/available_actionsstoryboard. With beta.12, the branch can use SDK types and buyer helpers, but still carries custom seller-side enforcement to pass the storyboard.