chore: release package#759
Merged
Merged
Conversation
d7ac7bd to
e4c916b
Compare
e4c916b to
2a21863
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@adcp/client@5.10.0
Minor Changes
8f9260b: feat(server): request validation defaults to
'warn'outside productioncreateAdcpServer({ validation: { requests } })previously defaulted to'off'everywhere. It now defaults to'warn'whenNODE_ENV !== 'production', mirroring the asymmetric default already inplace for
responses('strict'in dev/test,'off'in production).Production behaviour is unchanged: the default stays
'off'whenNODE_ENV === 'production', so prod request paths pay no AJV cost.What operators will see: in dev/test/CI, each incoming request that
doesn't match the bundled AdCP request schema logs a single
Schema validation warning (request)line through the configuredlogger, with the tool name and the field pointer. Nothing is rejected —
the request still flows to the handler exactly as before. Node's test
runner does not set
NODE_ENV, so suites running undernode --testfall into the dev/test bucket and will start emitting these warnings.
How to opt out: pass
validation: { requests: 'off' }on the serverconfig, or set
NODE_ENV=productionfor the process.Why: keeps request and response defaults symmetric, and prepares seller
operators for upstream AdCP schema tightenings (e.g. adcp#2795, which
introduces a required
asset_typediscriminator — buyer agents stillon RC3 fixtures will lack it). Surfacing those drifts as warnings
during development beats discovering them in a downstream consumer's
VALIDATION_ERRORafter deploy.Related: feat(validation): schema-driven validation with client hooks and opt-in server middleware #694 (original intent for
requests: 'warn') and Tighten generated types + default response validation to catch schema drift at the right layer #727 A(response-side default precedent).
86a0fde: Register the fourth default cross-step assertion
status.monotonic(Assertion: status.monotonic (waits on lifecycle formalization #1612-#1616) adcp#2664). Resource statuses observed across storyboard steps MUST transition only along edges in the spec-published lifecycle graph for their resource type. Catches regressions likeactive → pending_creativeson a media_buy, orapproved → processingon a creative asset, that per-step validations cannot detect.Tracked lifecycles (one transition table per resource type, hardcoded against the enum schemas in
static/schemas/source/enums/*-status.jsonin the spec repo, with bidirectional edges listed explicitly):media_buy— forward flowpending_creatives → pending_start → active,active ↔ pausedreversible, terminalscompleted | rejected | canceled.creative(asset lifecycle) — forward flowprocessing → pending_review → approved | rejected,approved ↔ archivedreversible,rejected → processing | pending_reviewallowed on re-sync, no terminals.creative_approval— per-assignment on a package, forwardpending_review → approved | rejected,rejected → pending_reviewallowed on re-sync.account—active ↔ suspendedandactive ↔ payment_requiredreversible, terminalsrejected | closed.si_session— forwardactive → pending_handoff → complete | terminated, terminalscomplete | terminated.catalog_item— forwardpending → approved | rejected | warning,approved ↔ warningreversible,rejected → pendingallowed on re-sync.proposal— one-waydraft → committed.Observations are drawn from task-aware extractors on
stepResult.response:create_media_buy/update_media_buy/get_media_buys,sync_creatives/list_creatives, nested.packages[].creative_approvals[],sync_accounts/list_accounts,si_initiate_session/si_send_message/si_terminate_session,sync_catalogs/list_catalogs(per-item),get_products(when the response carries aproposal). Unknown tasks produce no observations.State is scoped
(resource_type, resource_id)so independent resources don't interfere. Self-edges (same status re-read) are silent pass. Skipped / errored /expect_error: truesteps don't record observations. Unknown enum values (drift) reset the anchor without failing —response_schemacatches enum violations.Failure output names the resource, the illegal transition, and the two step ids:
media_buy mb-1: active → pending_creatives (step "create" → step "regress") is not in the lifecycle graph.Consumers who need a stricter variant canregisterAssertion(spec, { override: true }).18 new unit tests cover forward flows, terminal enforcement, bidirectional edges, skip semantics, (resource_type, resource_id) scoping, nested creative_approval arrays, adcp_error-gated observations, enum-drift tolerance.
573a176: Improve OAuth ergonomics for
adcp storyboard run."requires OAuth authorization"(the wordingNeedsAuthorizationErroremits) now classify asauth_requiredwith theSave credentials: adcp --save-auth <alias> <url> --oauthremediation hint, instead of falling through tooverall_status: 'unreachable'with no actionable advice. The keyword list indetectAuthRejectionnow matches"authorization"and"oauth"in addition to401/unauthorized/authentication/jws/jwt/signature verification.discoverOAuthMetadatasuccessfully walks the well-known chain — an agent that 401s before its OAuth metadata is resolvable still gets a useful hint.adcp storyboard run <alias> --oauthnow opens the browser to complete PKCE when the saved alias has no valid tokens, then proceeds with the run. Matches the existingadcp <alias> get_adcp_capabilities --oauthbehavior so the two-step dance (--save-auth --oauththenstoryboard run) is no longer required. Raw URLs still need--save-authfirst; MCP only.Docs:
docs/CLI.mdanddocs/guides/VALIDATE-YOUR-AGENT.mddocument both flows and add a troubleshooting row for theAgent requires OAuthfailure.86ccc99: feat(server): response validation defaults to
'strict'outside productioncreateAdcpServer({ validation: { responses } })previously defaulted to'warn'whenNODE_ENV !== 'production'. It now defaults to'strict'in dev/test/CI so handler-returned schema drift fails with
VALIDATION_ERROR(with the offending field path indetails.issues)instead of logging a warning the caller can silently ignore.
Production behaviour is unchanged: the default stays
'off'whenNODE_ENV === 'production', so prod request paths pay no validationcost. Pass
validation: { responses: 'warn' }to restore the previousdev-mode behaviour;
validation: { responses: 'off' }opts outentirely.
Why: the
compliance:skill-matrixharness has repeatedly surfacedSERVICE_UNAVAILABLEfrom agents whose responses fail the wire schema.The dispatcher's response validator catches this drift with a clear
field pointer, one layer that every tool inherits automatically. Making
that the default catches it during handler development rather than in a
downstream consumer.
Migration: handler tests that use sparse fixtures (e.g.
{ products: [{ product_id: 'p1' }] }) will start returningVALIDATION_ERROR. Either fill in the missing required fields to matchthe AdCP schema, or set
validation: { responses: 'off' }on the testserver to keep the fixture intentionally minimal. Note that Node's
test runner does not set
NODE_ENV, so test suites running undernode --test(withNODE_ENV=undefined) fall into the dev/testbucket and will start validating responses — this is intentional.
Also: the
VALIDATION_ERRORenvelope'sdetails.issues[].schemaPathis now gated behind
exposeErrorDetails(same policy as the existingSERVICE_UNAVAILABLE.details.reasonfield). Production responses nolonger leak
#/oneOf/<n>/properties/...paths that fingerprint thehandler's internal
oneOfbranch selection — buyers still getpointer,message, andkeyword, which is sufficient to fix adrifted payload.
Closes Tighten generated types + default response validation to catch schema drift at the right layer #727 (A).
f64007c: fix(server): tighten handler return types so schema drift fails
tscTwo related tightenings close Tighten generated types + default response validation to catch schema drift at the right layer #727 (B).
1.
AdcpToolMapbrand rights results.acquire_rights,get_rights, andget_brand_identityhadresult: Record<string, unknown>— a stale scaffold from before the response types werecode-generated. Replaced with the proper generated types:
acquire_rights→AcquireRightsAcquired | AcquireRightsPendingApproval | AcquireRightsRejectedget_rights→GetRightsSuccessget_brand_identity→GetBrandIdentitySuccess2.
DomainHandlerreturn type. The handler return unionpreviously included
| Record<string, unknown>as a general escapehatch, so any handler could return any shape. Sparse returns like
{ rights_id, status: 'acquired' }passedtscand only failed atwire-level validation. Handler return type is now just
AdcpToolMap[K]['result'] | McpToolResponse, so drift fails atcompile time.
adcpError(...)still works — it returnsMcpToolResponse.Migration. If a handler returns a plain object literal without
spelling out the full success shape,
tscwill now flag the driftwith an error like:
Two ways to fix:
Fill in the missing required fields to match the AdCP schema (what
the wire-level validator would have demanded anyway). Use
DEFAULT_REPORTING_CAPABILITIESforProduct.reporting_capabilitiesif you don't have seller-specific reporting policy yet.
If you genuinely need a loose return (e.g. a test fixture), wrap
with a response builder —
productsResponse({ ... }),acquireRightsResponse({ ... }), etc. The builders accept typedinputs so the drift surfaces there instead of silently passing
through.
Reference agents.
test-agents/seller-agent.tsnow usesDEFAULT_REPORTING_CAPABILITIESon each product (the old code hada "Use plain objects instead of Product type" comment whose premise
was wrong —
reporting_capabilitiesis required, not optional).test-agents/seller-agent-signed-mcp.tshad a latent bug:createMediaBuywas readingpkg.package_idfrom the request, butPackageRequesthas no such field — buyers sendbuyer_refandthe seller mints
package_idper spec. The handler now mintscrypto.randomUUID()like a real seller would.Patch Changes
275fa70:
docs/llms.txtnow includes per-tool response contracts. Each tool section gets a**Response (success branch):**block listing the required + optional fields drawn from the bundled JSON schemas — same format the existing request block uses.Closes the drift path we kept seeing in matrix runs: agents dropped required response fields (missing
format_idoncreative_manifest, plural-variant hallucinations likecreative_deliveriesforcreatives, missing top-levelcurrency) because the skill examples documented the intent but the full per-field contract lived in the generated schemas and was never surfaced in the llms.txt index Claude actually reads when building. The contract is now one anchored section away:docs/llms.txt#build_creative,docs/llms.txt#get_creative_delivery, etc. — same convention as the llms.txt pattern other projects use.No SDK code change; llms.txt is regenerated via
npm run generate-agent-docs.6ae3169: chore(server): migrate McpServer.tool() → registerTool() repo-wide (tech-debt: migrate MCP tool registrations from deprecated
tool()toregisterTool()#705)Replaces every use of the MCP SDK's deprecated
McpServer.tool(...)overload with the supported
registerTool(name, config, handler)form.Behavior is unchanged;
tools/listoutput is identical aside from acleaner path to the same metadata.
What moved
src/lib/server/create-adcp-server.ts— the AdcpToolMap registrationloop and
get_adcp_capabilitiesnow useregisterTool, withannotationsdeclared at register time instead of via a post-hoc.update()call.src/lib/server/test-controller.ts,src/lib/testing/comply-controller.ts—
comply_test_controllerregistration.src/lib/testing/stubs/governance-agent-stub.ts— all five tools(
get_adcp_capabilities,sync_plans,check_governance,report_plan_outcome,get_plan_audit_logs).examples/error-compliant-server.ts— the canonical seller template.outputSchemadeliberately not wired on framework toolsThe MCP SDK's client-side
callToolvalidatesstructuredContentagainst the declared
outputSchemawhenever structuredContent ispresent — regardless of
isError(
@modelcontextprotocol/sdk/dist/esm/client/index.js:504). AdCP'sadcpError()envelope carriesstructuredContent: { adcp_error: {...} }alongside
isError: true, which would fail every client-side outputSchemacheck (the error shape doesn't match the success schema). Until the SDK
gates that client-side check on
!isError(the server-side validatoralready does), framework-registered tools are migrated without
outputSchema. Response drift is caught by the dispatcher's AJVvalidator (Tighten generated types + default response validation to catch schema drift at the right layer #727) instead, and
customToolsmay opt in explicitly viacustomTools[*].outputSchema— validated by a new regression test.Closes tech-debt: migrate MCP tool registrations from deprecated
tool()toregisterTool()#705.275fa70: Skill files now point Claude at
docs/llms.txt#<tool>for per-tool field contracts instead of duplicating them inline.Callout wording is imperative, not descriptive (per prompt-engineering review): "Before writing any handler's return statement, fetch
docs/llms.txtand grep for#### \<tool_name>``..." Replaces the earlier passive "contracts live at X" phrasing that relied on Claude optionally following the pointer. Safety-net sentence reframed as permission ("write the obvious thing and trust the contract") rather than threat.Grep instructions match how agents actually find sections: Markdown anchors resolve on GitHub but Claude reading the raw file searches for
#### \tool_name``. The callout names that pattern directly.build-creative-agent slim collapses verbose response-shape blocks into a 4-column handler-binding table:
Tool | Handler | Contract | Gotchas. The Contract column carries a direct anchor link per tool so Claude is likelier to click the one adjacent to the row it's reading than a general pointer three lines up. Asset-shape bullets stay inline (most-drifted fields historically). Net -94 lines on that skill.Other 7 skills get the pointer callout only — structural slims deferred until matrix v12 signal is in. Two variables (pointer + slim) on one skill lets us disambiguate outcomes.
Lands on top of strict validation default in dev (Tighten generated types + default response validation to catch schema drift at the right layer #727/feat(server): strict response validation default in dev/test (#727) #757) and the llms.txt response-contract generator (docs: surface response contracts per tool in docs/llms.txt #761). Together: llms.txt is canonical, skills are narrative + gotchas, strict validation catches residual drift at call site.