@adcp/sdk 5.25.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -7
- package/dist/lib/compliance-fixtures/index.d.ts +1 -1
- package/dist/lib/compliance-fixtures/index.js +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +15 -0
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +7 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +9 -2
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +7 -8
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/schemas/index.d.ts +1 -1
- package/dist/lib/schemas/index.js +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +129 -11
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +112 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.d.ts +27 -0
- package/dist/lib/server/ctx-metadata/backends/memory.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/memory.js +72 -0
- package/dist/lib/server/ctx-metadata/backends/memory.js.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/pg.d.ts +62 -0
- package/dist/lib/server/ctx-metadata/backends/pg.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/backends/pg.js +145 -0
- package/dist/lib/server/ctx-metadata/backends/pg.js.map +1 -0
- package/dist/lib/server/ctx-metadata/index.d.ts +15 -0
- package/dist/lib/server/ctx-metadata/index.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/index.js +28 -0
- package/dist/lib/server/ctx-metadata/index.js.map +1 -0
- package/dist/lib/server/ctx-metadata/store.d.ts +177 -0
- package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/store.js +327 -0
- package/dist/lib/server/ctx-metadata/store.js.map +1 -0
- package/dist/lib/server/ctx-metadata/wire-shape.d.ts +55 -0
- package/dist/lib/server/ctx-metadata/wire-shape.d.ts.map +1 -0
- package/dist/lib/server/ctx-metadata/wire-shape.js +121 -0
- package/dist/lib/server/ctx-metadata/wire-shape.js.map +1 -0
- package/dist/lib/server/decisioning/account.d.ts +309 -0
- package/dist/lib/server/decisioning/account.d.ts.map +1 -0
- package/dist/lib/server/decisioning/account.js +102 -0
- package/dist/lib/server/decisioning/account.js.map +1 -0
- package/dist/lib/server/decisioning/admin-router.d.ts +75 -0
- package/dist/lib/server/decisioning/admin-router.d.ts.map +1 -0
- package/dist/lib/server/decisioning/admin-router.js +120 -0
- package/dist/lib/server/decisioning/admin-router.js.map +1 -0
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +204 -0
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/assembly-helpers.js +173 -0
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/async-outcome.d.ts +154 -0
- package/dist/lib/server/decisioning/async-outcome.d.ts.map +1 -0
- package/dist/lib/server/decisioning/async-outcome.js +239 -0
- package/dist/lib/server/decisioning/async-outcome.js.map +1 -0
- package/dist/lib/server/decisioning/capabilities.d.ts +251 -0
- package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -0
- package/dist/lib/server/decisioning/capabilities.js +16 -0
- package/dist/lib/server/decisioning/capabilities.js.map +1 -0
- package/dist/lib/server/decisioning/context.d.ts +212 -0
- package/dist/lib/server/decisioning/context.d.ts.map +1 -0
- package/dist/lib/server/decisioning/context.js +26 -0
- package/dist/lib/server/decisioning/context.js.map +1 -0
- package/dist/lib/server/decisioning/errors-typed.d.ts +104 -0
- package/dist/lib/server/decisioning/errors-typed.d.ts.map +1 -0
- package/dist/lib/server/decisioning/errors-typed.js +304 -0
- package/dist/lib/server/decisioning/errors-typed.js.map +1 -0
- package/dist/lib/server/decisioning/helpers.d.ts +131 -0
- package/dist/lib/server/decisioning/helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/helpers.js +134 -0
- package/dist/lib/server/decisioning/helpers.js.map +1 -0
- package/dist/lib/server/decisioning/index.d.ts +46 -0
- package/dist/lib/server/decisioning/index.d.ts.map +1 -0
- package/dist/lib/server/decisioning/index.js +120 -0
- package/dist/lib/server/decisioning/index.js.map +1 -0
- package/dist/lib/server/decisioning/list-helpers.d.ts +53 -0
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/list-helpers.js +96 -0
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +56 -0
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/manifest-helpers.js +78 -0
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -0
- package/dist/lib/server/decisioning/pagination.d.ts +21 -0
- package/dist/lib/server/decisioning/pagination.d.ts.map +1 -0
- package/dist/lib/server/decisioning/pagination.js +12 -0
- package/dist/lib/server/decisioning/pagination.js.map +1 -0
- package/dist/lib/server/decisioning/platform.d.ts +188 -0
- package/dist/lib/server/decisioning/platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/platform.js +19 -0
- package/dist/lib/server/decisioning/platform.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +510 -0
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +2196 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +114 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +247 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts +32 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +127 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.ts +105 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/task-registry.js +96 -0
- package/dist/lib/server/decisioning/runtime/task-registry.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/to-context.d.ts +54 -0
- package/dist/lib/server/decisioning/runtime/to-context.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/to-context.js +166 -0
- package/dist/lib/server/decisioning/runtime/to-context.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts +20 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.js +93 -0
- package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts +72 -0
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/audiences.js +15 -0
- package/dist/lib/server/decisioning/specialisms/audiences.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +92 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.js +28 -0
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts +67 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.js +31 -0
- package/dist/lib/server/decisioning/specialisms/campaign-governance.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +78 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.js +35 -0
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +81 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js +28 -0
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +144 -0
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/creative.js +19 -0
- package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/lists.d.ts +61 -0
- package/dist/lib/server/decisioning/specialisms/lists.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/lists.js +30 -0
- package/dist/lib/server/decisioning/specialisms/lists.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +163 -0
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sales.js +64 -0
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -0
- package/dist/lib/server/decisioning/specialisms/signals.d.ts +64 -0
- package/dist/lib/server/decisioning/specialisms/signals.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/signals.js +28 -0
- package/dist/lib/server/decisioning/specialisms/signals.js.map +1 -0
- package/dist/lib/server/decisioning/start-time.d.ts +76 -0
- package/dist/lib/server/decisioning/start-time.d.ts.map +1 -0
- package/dist/lib/server/decisioning/start-time.js +81 -0
- package/dist/lib/server/decisioning/start-time.js.map +1 -0
- package/dist/lib/server/decisioning/status-changes.d.ts +165 -0
- package/dist/lib/server/decisioning/status-changes.d.ts.map +1 -0
- package/dist/lib/server/decisioning/status-changes.js +131 -0
- package/dist/lib/server/decisioning/status-changes.js.map +1 -0
- package/dist/lib/server/decisioning/status-mappers.d.ts +46 -0
- package/dist/lib/server/decisioning/status-mappers.d.ts.map +1 -0
- package/dist/lib/server/decisioning/status-mappers.js +46 -0
- package/dist/lib/server/decisioning/status-mappers.js.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts +289 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.js +503 -0
- package/dist/lib/server/decisioning/tenant-registry.js.map +1 -0
- package/dist/lib/server/express-adapter.d.ts +1 -1
- package/dist/lib/server/express-adapter.js +1 -1
- package/dist/lib/server/governance.d.ts +1 -1
- package/dist/lib/server/governance.js +1 -1
- package/dist/lib/server/idempotency/store.d.ts +1 -1
- package/dist/lib/server/idempotency/store.js +1 -1
- package/dist/lib/server/index.d.ts +9 -2
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +79 -4
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +38 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -0
- package/dist/lib/server/legacy/v5/index.js +60 -0
- package/dist/lib/server/legacy/v5/index.js.map +1 -0
- package/dist/lib/server/normalize-errors.d.ts +88 -0
- package/dist/lib/server/normalize-errors.d.ts.map +1 -0
- package/dist/lib/server/normalize-errors.js +146 -0
- package/dist/lib/server/normalize-errors.js.map +1 -0
- package/dist/lib/server/pick-safe-details.d.ts +90 -0
- package/dist/lib/server/pick-safe-details.d.ts.map +1 -0
- package/dist/lib/server/pick-safe-details.js +148 -0
- package/dist/lib/server/pick-safe-details.js.map +1 -0
- package/dist/lib/server/postgres-state-store.d.ts +1 -1
- package/dist/lib/server/postgres-state-store.js +1 -1
- package/dist/lib/server/responses.d.ts +38 -0
- package/dist/lib/server/responses.d.ts.map +1 -1
- package/dist/lib/server/responses.js +38 -0
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/state-store.d.ts +1 -1
- package/dist/lib/server/state-store.js +1 -1
- package/dist/lib/server/test-controller.d.ts +10 -3
- package/dist/lib/server/test-controller.d.ts.map +1 -1
- package/dist/lib/server/test-controller.js +10 -3
- package/dist/lib/server/test-controller.js.map +1 -1
- package/dist/lib/testing/comply-controller.d.ts +47 -1
- package/dist/lib/testing/comply-controller.d.ts.map +1 -1
- package/dist/lib/testing/comply-controller.js +11 -4
- package/dist/lib/testing/comply-controller.js.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/personas/index.d.ts +143 -0
- package/dist/lib/testing/personas/index.d.ts.map +1 -0
- package/dist/lib/testing/personas/index.js +190 -0
- package/dist/lib/testing/personas/index.js.map +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +1 -1
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +3 -2
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts +13 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +179 -7
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/types/asset-instances.d.ts +1 -0
- package/dist/lib/types/asset-instances.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +203 -98
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/schemas.generated.d.ts +599 -159
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +175 -94
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +315 -46
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +1 -1
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +6 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/validation/schema-validator.d.ts +13 -0
- package/dist/lib/validation/schema-validator.d.ts.map +1 -1
- package/dist/lib/validation/schema-validator.js +240 -3
- package/dist/lib/validation/schema-validator.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +30 -5
- package/docs/llms.txt +28 -17
- package/examples/README.md +3 -1
- package/examples/decisioning-platform-broadcast-tv.ts +300 -0
- package/examples/decisioning-platform-identity-graph.ts +214 -0
- package/examples/decisioning-platform-mock-seller.ts +332 -0
- package/examples/decisioning-platform-multi-tenant.ts +128 -0
- package/examples/decisioning-platform-programmatic.ts +254 -0
- package/examples/signals-agent.ts +1 -1
- package/package.json +13 -2
- package/skills/build-brand-rights-agent/SKILL.md +10 -3
- package/skills/build-creative-agent/SKILL.md +94 -64
- package/skills/build-decisioning-creative-template/SKILL.md +554 -0
- package/skills/build-decisioning-platform/SKILL.md +304 -0
- package/skills/build-decisioning-platform/advanced/BRAND-RIGHTS.md +25 -0
- package/skills/build-decisioning-platform/advanced/COMPLIANCE.md +23 -0
- package/skills/build-decisioning-platform/advanced/GOVERNANCE.md +24 -0
- package/skills/build-decisioning-platform/advanced/HITL.md +34 -0
- package/skills/build-decisioning-platform/advanced/IDEMPOTENCY.md +52 -0
- package/skills/build-decisioning-platform/advanced/MULTI-TENANT.md +47 -0
- package/skills/build-decisioning-platform/advanced/OAUTH.md +22 -0
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +991 -0
- package/skills/build-decisioning-platform/advanced/SANDBOX.md +24 -0
- package/skills/build-decisioning-platform/advanced/STATE-MACHINE.md +52 -0
- package/skills/build-decisioning-signal-marketplace/SKILL.md +269 -0
- package/skills/build-generative-seller-agent/SKILL.md +89 -53
- package/skills/build-governance-agent/SKILL.md +76 -45
- package/skills/build-retail-media-agent/SKILL.md +87 -62
- package/skills/build-seller-agent/SKILL.md +384 -255
- package/skills/build-seller-agent/deployment.md +5 -3
- package/skills/build-seller-agent/specialisms/audience-sync.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-broadcast-tv.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-guaranteed.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-proposal-mode.md +0 -2
- package/skills/build-seller-agent/specialisms/sales-social.md +0 -2
- package/skills/build-seller-agent/specialisms/signed-requests.md +0 -2
- package/skills/build-si-agent/SKILL.md +40 -32
- package/skills/build-signals-agent/SKILL.md +139 -92
- package/skills/call-adcp-agent.previous/SKILL.md +5 -0
|
@@ -15,8 +15,9 @@ Companion to [`SKILL.md`](./SKILL.md). Read this only when you need deployment s
|
|
|
15
15
|
Pass functions for `publicUrl` and `protectedResource`, branch on `ctx.host` in the factory, and turn on `trustForwardedHost` when a proxy terminates TLS:
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
18
|
+
import { UnknownHostError, hostname } from '@adcp/sdk';
|
|
19
19
|
import { verifyBearer } from '@adcp/sdk/server';
|
|
20
|
+
import { serve, createAdcpServer } from '@adcp/sdk/server/legacy/v5';
|
|
20
21
|
|
|
21
22
|
// Host → adapter config. Whatever shape suits your deployment (DB, env, static).
|
|
22
23
|
// Cache the CONFIG (not the AdcpServer). serve() still instantiates the
|
|
@@ -116,7 +117,8 @@ When your agent is _both_ an OAuth 2.1 AS (issues tokens) and a protected resour
|
|
|
116
117
|
|
|
117
118
|
```typescript
|
|
118
119
|
import express from 'express';
|
|
119
|
-
import {
|
|
120
|
+
import { createExpressAdapter, verifyBearer, anyOf, verifyApiKey } from '@adcp/sdk/server';
|
|
121
|
+
import { createAdcpServer } from '@adcp/sdk/server/legacy/v5';
|
|
120
122
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
121
123
|
import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';
|
|
122
124
|
|
|
@@ -388,7 +390,7 @@ You own: the `mcpAuthRouter` wiring (provider-specific), the per-request `transp
|
|
|
388
390
|
|
|
389
391
|
```typescript
|
|
390
392
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
391
|
-
import { createAdcpServer } from '@adcp/sdk/server';
|
|
393
|
+
import { createAdcpServer } from '@adcp/sdk/server/legacy/v5';
|
|
392
394
|
|
|
393
395
|
const server = createAdcpServer({
|
|
394
396
|
name: 'Local Seller',
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `audience-sync`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `audience_sync`. Track is `audiences` — separate from the core seller lifecycle, but lives in this skill because identifier sync and account discovery sit next to media-buying.
|
|
7
6
|
|
|
8
7
|
Required tools: `sync_audiences` and `list_accounts`. `sync_audiences` is overloaded — it handles three cases through its request payload:
|
|
@@ -60,4 +59,3 @@ createAdcpServer({
|
|
|
60
59
|
**Identifier rules:** each `add` entry is a single-identifier object (`{hashed_email}` OR `{hashed_phone}`, not both). Values are SHA-256 of lowercased, trimmed input. Salting/normalization is out-of-band between buyer and platform — document your expected input format.
|
|
61
60
|
|
|
62
61
|
**Platform types:** destinations span `['dsp', 'retail_media', 'social', 'audio', 'pmax']`. Each has its own `activation_key` shape — see `skills/build-signals-agent/SKILL.md` for activation patterns, which are shared across signals and audience sync.
|
|
63
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `sales-broadcast-tv`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `media_buy_broadcast_seller`. Broadcast has four protocol surfaces not used in digital.
|
|
7
6
|
|
|
8
7
|
**Pricing** — unit-based (cost per spot). Until a `pricing_model: 'unit'` lands, express as CPM with a very high `fixed_price` that represents the cost per thousand spots equivalent, or use a custom pricing option ID and clarify in `description`.
|
|
@@ -68,4 +67,3 @@ Don't declare `measurement_windows: ['live', 'c3', 'c7']` — the Zod schema rej
|
|
|
68
67
|
**Measurement windows on delivery** — each delivery row tags `measurement_window: 'live' | 'c3' | 'c7'`, `is_final: boolean`, and `supersedes_window` (for window upgrades). Live ratings mature in 24h, C3 in ~4d, C7 in ~8d. Final reconciliation lands ~15d after last air date.
|
|
69
68
|
|
|
70
69
|
**Emit window_update webhooks** via `ctx.emitWebhook` (see [§ Webhooks](#webhooks-async-completion-signed-outbound) above). Use `operation_id: \`window_update.${media_buy_id}.${stage}\`` so C3 → C7 supersession retries share a stable idempotency_key.
|
|
71
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `sales-guaranteed`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `sales_guaranteed`. `create_media_buy` has **three return shapes**. Route on request signals FIRST — the specialism's name is about IO signing, but the baseline `media_buy_seller` storyboard exercises all three in sequence.
|
|
7
6
|
|
|
8
7
|
| Request signal | Return | Why |
|
|
@@ -64,4 +63,3 @@ When the task completes, emit the final `create_media_buy` result (carrying `med
|
|
|
64
63
|
Declare `requires_io_approval` in your `capabilities.features` for this path. For deterministic compliance testing, implement `forceTaskStatus` (not `forceMediaBuyStatus`) in your `TestControllerStore` to drive the task from `submitted → completed` without waiting for a human.
|
|
65
64
|
|
|
66
65
|
**Governance denial (`GOVERNANCE_DENIED`).** Baseline `media_buy_seller/governance_denied*` scenarios exercise governance refusal. For sellers that compose with a governance agent, call `checkGovernance(...)` from `@adcp/sdk/server` at the top of `create_media_buy`. If the governance agent returns denial, surface with `governanceDeniedError(result)` so the error code is `GOVERNANCE_DENIED` and context echoes. Sellers that don't compose with governance will see these scenarios fail with `INVALID_REQUEST` — expected until upstream gates the scenarios behind a composition-claim specialism (tracked at adcontextprotocol/adcp#2521).
|
|
67
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `sales-non-guaranteed`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `media_buy_non_guaranteed`. The specialism hinges on `bid_price` and `update_media_buy`, neither of which the baseline example shows.
|
|
7
6
|
|
|
8
7
|
Packages on `create_media_buy` carry `bid_price`. Validate it against the product's `floor_price`:
|
|
@@ -36,4 +35,3 @@ updateMediaBuy: async (params, ctx) => {
|
|
|
36
35
|
```
|
|
37
36
|
|
|
38
37
|
`valid_actions` on an active non-guaranteed buy should include `pause`, `update_bid`, `get_delivery`. The framework auto-populates this when `createMediaBuy`/`updateMediaBuy` return with `status: 'active'`.
|
|
39
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `sales-proposal-mode`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `media_buy_proposal_mode`. The acceptance path inverts the baseline — buyer sends `proposal_id` + `total_budget`, no `packages`.
|
|
7
6
|
|
|
8
7
|
`get_products` returns a `proposals[]` array alongside products:
|
|
@@ -48,4 +47,3 @@ createMediaBuy: async (params, ctx) => {
|
|
|
48
47
|
// ... fall through to baseline packages path
|
|
49
48
|
},
|
|
50
49
|
```
|
|
51
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `sales-social`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `social_platform` (category `sales_social`, track `audiences`).
|
|
7
6
|
|
|
8
7
|
**`sales-social` is additive, not a replacement.** The storyboard's own metadata declares `interaction_model: media_buy_seller` with `capabilities: [sells_media, accepts_briefs, supports_non_guaranteed]` and lists Snap, Meta, TikTok, and Pinterest as example agents — all of which have product catalogs (ad formats, placements, audience offerings as products) AND accept media buys (campaigns with flights, budgets, ad sets). The storyboard only exercises the audience / catalog / native-creative / events / financials leg because the baseline buyer-flow is covered by `sales-non-guaranteed` (or `sales-guaranteed`). Claim BOTH specialisms and implement the full surface.
|
|
@@ -27,4 +26,3 @@ Storyboard: `social_platform` (category `sales_social`, track `audiences`).
|
|
|
27
26
|
**Handler grouping in `createAdcpServer`:** `sync_audiences`, `sync_catalogs`, and `log_event` live under `eventTracking`, NOT `mediaBuy`. `get_account_financials` and `sync_accounts` live under `accounts`. Baseline `get_products`/`create_media_buy`/etc. stay under `mediaBuy`.
|
|
28
27
|
|
|
29
28
|
**Don't** rip out `get_products` or `create_media_buy` when adding `sales-social` — you need them. The failure mode from doing so: buyers who discover your agent via `get_adcp_capabilities` expecting a media-buy seller hit immediate compliance failures when every baseline storyboard fails with "tool not registered," and your entire `sales-non-guaranteed` bundle regresses to 0/N passing.
|
|
30
|
-
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [`../SKILL.md`](../SKILL.md). The SKILL.md baseline applies; this file covers only the deltas for `signed-requests`.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
Storyboard: `signed_requests`. Transport-layer security specialism — certifies that your agent correctly verifies incoming RFC 9421 HTTP Signatures on mutating AdCP operations.
|
|
7
6
|
|
|
8
7
|
**If you run this behind OAuth or combine it with idempotency,** also read [§ Composing OAuth, signing, and idempotency](#composing-oauth-signing-and-idempotency) for middleware mount order, 401 disambiguation (Bearer vs Signature challenge), and how the verified signing `keyid` threads into the idempotency principal.
|
|
@@ -86,4 +85,3 @@ npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp signed_requests --
|
|
|
86
85
|
```
|
|
87
86
|
|
|
88
87
|
Every negative vector must return the exact `expected_outcome.error_code` in `WWW-Authenticate: Signature error="<code>"`. A non-claiming agent is not graded against this specialism.
|
|
89
|
-
|
|
@@ -25,7 +25,7 @@ A sponsored intelligence (SI) agent serves conversational sponsored content with
|
|
|
25
25
|
|
|
26
26
|
| Specialism | Status | Delta |
|
|
27
27
|
| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
-
| _(none yet)_ | — | SI has no specialisms in AdCP 3.0 — pass the `
|
|
28
|
+
| _(none yet)_ | — | SI has no specialisms in AdCP 3.0 — pass the `sponsored_intelligence` *protocol* baseline (declared via `supported_protocols: ['sponsored_intelligence']`). Specialism storyboards for conversational-ad-specific patterns are pending future AdCP releases. |
|
|
29
29
|
|
|
30
30
|
## Before Writing Code
|
|
31
31
|
|
|
@@ -51,7 +51,7 @@ How should the agent respond during a session?
|
|
|
51
51
|
>
|
|
52
52
|
> **Cross-cutting pitfalls matrix runs keep catching:**
|
|
53
53
|
>
|
|
54
|
-
> - **
|
|
54
|
+
> - **Do NOT declare a `sponsored-intelligence` specialism.** SI is a *protocol* in AdCP 3.0 — declared via `supported_protocols: ['sponsored_intelligence']` on the `get_adcp_capabilities` response. There is no SI specialism in the `AdCPSpecialism` enum yet, so adopters wire SI through the v5 handler-bag path (`createAdcpServer` from `@adcp/sdk/server/legacy/v5`). The v6 `DecisioningPlatform` interface does not yet expose a `sponsoredIntelligence` field. (Tracking: SI specialism + auto-hydration of `req.session` is planned for a later v6.x — adopters today persist sessions explicitly via `ctx.store`.)
|
|
55
55
|
|
|
56
56
|
**`get_adcp_capabilities`** — register first, empty `{}` schema
|
|
57
57
|
|
|
@@ -113,7 +113,7 @@ taskToolResponse({
|
|
|
113
113
|
|
|
114
114
|
### Context and Ext Passthrough
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
The framework auto-echoes the request's `context` into every response — **do not set `context` yourself** on responses for tools whose request-side `context` is the protocol echo object (`core/context.json`).
|
|
117
117
|
|
|
118
118
|
**SI override.** `si_get_offering` and `si_initiate_session` override `context` on the request as a domain-specific **string** (natural-language intent hint, per spec: _'mens size 14 near Cincinnati'_). The response schema still keeps `context` as the protocol echo object. The framework detects this mismatch and skips the auto-echo for non-object values — your response simply won't carry a `context` field unless you populate it. If you want correlation tracking for SI responses, construct the context object in your handler (e.g., from a buyer-supplied `ext.correlation_id` or your own generator) and return it on the response.
|
|
119
119
|
|
|
@@ -121,16 +121,16 @@ taskToolResponse({
|
|
|
121
121
|
|
|
122
122
|
## SDK Quick Reference
|
|
123
123
|
|
|
124
|
-
| SDK piece
|
|
125
|
-
|
|
|
126
|
-
| `createAdcpServer({
|
|
127
|
-
| `serve(() => createAdcpServer(
|
|
128
|
-
| `ctx.store`
|
|
129
|
-
| `adcpError(code, { message })`
|
|
124
|
+
| SDK piece | Usage |
|
|
125
|
+
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
126
|
+
| `createAdcpServer(config)` *(use this for SI)* | v5 handler-bag entry. The only path that ships SI dispatch (the `sponsoredIntelligence: { getOffering, initiateSession, sendMessage, terminateSession }` sub-bag). Reach via `@adcp/sdk/server/legacy/v5`. v6 `createAdcpServerFromPlatform` does not yet expose an SI specialism — when it does, this skill will document the migration. |
|
|
127
|
+
| `serve(() => createAdcpServer(config))` | Start HTTP server on `:3001/mcp` |
|
|
128
|
+
| `ctx.store` | State persistence — `get/put/patch/delete/list` domain objects. SI sessions live here today (no auto-hydration yet). |
|
|
129
|
+
| `adcpError(code, { message })` | Structured error |
|
|
130
130
|
|
|
131
131
|
Handlers return raw data objects. The framework auto-wraps responses and auto-generates `get_adcp_capabilities` from registered handlers.
|
|
132
132
|
|
|
133
|
-
Import: `import { createAdcpServer, serve, adcpError } from '@adcp/sdk';`
|
|
133
|
+
Import: `import { createAdcpServer, serve, adcpError } from '@adcp/sdk/server/legacy/v5';`
|
|
134
134
|
|
|
135
135
|
## Setup
|
|
136
136
|
|
|
@@ -159,16 +159,21 @@ Minimal `tsconfig.json`:
|
|
|
159
159
|
|
|
160
160
|
## Implementation
|
|
161
161
|
|
|
162
|
-
1. Single `.ts` file —
|
|
162
|
+
1. Single `.ts` file — wire `createAdcpServer` from `@adcp/sdk/server/legacy/v5` with a `sponsoredIntelligence` handler bag
|
|
163
163
|
2. Do not register `get_adcp_capabilities` — the framework generates it from registered handlers
|
|
164
164
|
3. Return raw data objects from handlers — the framework wraps responses automatically
|
|
165
|
-
4. Use `ctx.store` to persist active sessions — track state: active → terminated
|
|
166
|
-
5. Handlers receive `(params, ctx)` — `ctx.store` for state, `ctx.account` for resolved account
|
|
165
|
+
4. Use `ctx.store` to persist active sessions — track state: active → terminated. **Sessions are NOT auto-hydrated yet** (planned for v6.x). Read `req.session_id` and look the session up in `ctx.store` on every `si_send_message`.
|
|
166
|
+
5. Handlers receive `(params, ctx)` — `ctx.store` for state, `ctx.account` (when `resolveAccount` is wired) for resolved account
|
|
167
167
|
|
|
168
168
|
```typescript
|
|
169
169
|
import { randomUUID } from 'node:crypto';
|
|
170
|
-
import {
|
|
171
|
-
|
|
170
|
+
import {
|
|
171
|
+
createAdcpServer,
|
|
172
|
+
serve,
|
|
173
|
+
adcpError,
|
|
174
|
+
createIdempotencyStore,
|
|
175
|
+
memoryBackend,
|
|
176
|
+
} from '@adcp/sdk/server/legacy/v5';
|
|
172
177
|
|
|
173
178
|
const idempotency = createIdempotencyStore({
|
|
174
179
|
backend: memoryBackend(),
|
|
@@ -180,21 +185,22 @@ serve(() =>
|
|
|
180
185
|
name: 'SI Agent',
|
|
181
186
|
version: '1.0.0',
|
|
182
187
|
idempotency,
|
|
183
|
-
// Principal scope for idempotency. MUST never return undefined.
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// `(ctx) => ctx.account?.id`. The framework additionally auto-scopes
|
|
187
|
-
// `si_send_message` by `session_id`, so the same key under two
|
|
188
|
-
// sessions doesn't cross-replay.
|
|
188
|
+
// Principal scope for idempotency. MUST never return undefined. The
|
|
189
|
+
// framework additionally auto-scopes `si_send_message` by `session_id`,
|
|
190
|
+
// so the same key under two sessions doesn't cross-replay.
|
|
189
191
|
resolveSessionKey: () => 'default-principal',
|
|
190
|
-
|
|
192
|
+
capabilities: {
|
|
193
|
+
// SI is a *protocol*, not a specialism. Declare it here; the framework
|
|
194
|
+
// adds it to `get_adcp_capabilities.supported_protocols`.
|
|
195
|
+
supported_protocols: ['sponsored_intelligence'],
|
|
196
|
+
},
|
|
191
197
|
sponsoredIntelligence: {
|
|
192
|
-
getOffering: async (
|
|
198
|
+
getOffering: async (req, ctx) => ({
|
|
193
199
|
available: true,
|
|
194
200
|
offering_token: `tok_${randomUUID()}`,
|
|
195
201
|
ttl_seconds: 300,
|
|
196
202
|
}),
|
|
197
|
-
initiateSession: async (
|
|
203
|
+
initiateSession: async (req, ctx) => {
|
|
198
204
|
// session_id MUST be high-entropy (≥122 bits) per spec — it's the
|
|
199
205
|
// scope key for conversational isolation. Never use Date.now() or
|
|
200
206
|
// predictable counters; a guessable session_id lets one buyer
|
|
@@ -206,14 +212,17 @@ serve(() =>
|
|
|
206
212
|
session_status: 'active',
|
|
207
213
|
};
|
|
208
214
|
},
|
|
209
|
-
sendMessage: async (
|
|
210
|
-
|
|
215
|
+
sendMessage: async (req, ctx) => {
|
|
216
|
+
// No auto-hydration of sessions yet — read explicitly. (v6.x will
|
|
217
|
+
// attach `req.session` for free; until then this lookup is your
|
|
218
|
+
// session-loss guard.)
|
|
219
|
+
const session = await ctx.store.get('session', req.session_id);
|
|
211
220
|
// Return the error — the framework echoes returned adcpError
|
|
212
221
|
// responses verbatim. Thrown errors are caught and converted to
|
|
213
222
|
// SERVICE_UNAVAILABLE, which hides your custom code from the buyer.
|
|
214
223
|
if (!session) return adcpError('RESOURCE_NOT_FOUND', { message: 'Session not found' });
|
|
215
224
|
return {
|
|
216
|
-
session_id:
|
|
225
|
+
session_id: req.session_id,
|
|
217
226
|
session_status: 'active' as const,
|
|
218
227
|
response: {
|
|
219
228
|
content: 'Sponsored content response',
|
|
@@ -221,10 +230,10 @@ serve(() =>
|
|
|
221
230
|
},
|
|
222
231
|
};
|
|
223
232
|
},
|
|
224
|
-
terminateSession: async (
|
|
225
|
-
await ctx.store.delete('session',
|
|
233
|
+
terminateSession: async (req, ctx) => {
|
|
234
|
+
await ctx.store.delete('session', req.session_id);
|
|
226
235
|
return {
|
|
227
|
-
session_id:
|
|
236
|
+
session_id: req.session_id,
|
|
228
237
|
terminated: true,
|
|
229
238
|
};
|
|
230
239
|
},
|
|
@@ -254,8 +263,7 @@ Idempotency is wired in the example above. What the framework handles for you:
|
|
|
254
263
|
**An AdCP agent that accepts unauthenticated requests is non-compliant** (see `security_baseline` in the universal storyboard bundle). Ask the operator: "API key, OAuth, or both?" — then wire one of these into `serve()`.
|
|
255
264
|
|
|
256
265
|
```typescript
|
|
257
|
-
import { serve } from '@adcp/sdk';
|
|
258
|
-
import { verifyApiKey, verifyBearer, anyOf } from '@adcp/sdk/server';
|
|
266
|
+
import { serve, verifyApiKey, verifyBearer, anyOf } from '@adcp/sdk/server/legacy/v5';
|
|
259
267
|
|
|
260
268
|
// API key — simplest, good for B2B integrations
|
|
261
269
|
serve(createAgent, {
|
|
@@ -32,7 +32,7 @@ A signals agent serves audience segments to buyers for campaign targeting. Two t
|
|
|
32
32
|
|
|
33
33
|
Full treatment lives in `skills/build-seller-agent/SKILL.md` §Protocol-Wide Requirements and §Composing. Minimum viable pointers for a signals agent:
|
|
34
34
|
|
|
35
|
-
- **`idempotency_key`** on every mutating request (`activate_signal`, and any future mutating signals tools).
|
|
35
|
+
- **`idempotency_key`** on every mutating request (`activate_signal`, and any future mutating signals tools). Pass `createIdempotencyStore` to `createAdcpServerFromPlatform(platform, { idempotency })`.
|
|
36
36
|
- **Authentication** via `serve({ authenticate })` with `verifyApiKey`/`verifyBearer` from `@adcp/sdk/server`. Unauthenticated agents fail the universal `security_baseline` storyboard.
|
|
37
37
|
- **Signature-header transparency**: accept requests with `Signature-Input`/`Signature` headers even if you don't claim `signed-requests`.
|
|
38
38
|
|
|
@@ -77,9 +77,9 @@ If implementing `activate_signal`:
|
|
|
77
77
|
>
|
|
78
78
|
> **Cross-cutting pitfalls matrix runs keep catching:**
|
|
79
79
|
>
|
|
80
|
-
> - **Declare `capabilities
|
|
80
|
+
> - **Declare `capabilities.specialisms: ['signal-marketplace'] as const` (or `'signal-owned'`) on the `DecisioningPlatform` you pass to `createAdcpServerFromPlatform`.** Value is `string[]` of enum ids (not `[{id, version}]`). Agents that don't declare their specialism fail the grader with "No applicable tracks found" even if every tool works — tracks are gated on the specialism claim.
|
|
81
81
|
|
|
82
|
-
**`get_adcp_capabilities`** — auto-generated by `
|
|
82
|
+
**`get_adcp_capabilities`** — auto-generated by `createAdcpServerFromPlatform` from your typed `DecisioningPlatform`. Do not register manually.
|
|
83
83
|
|
|
84
84
|
**`get_signals`** — handled by `signals.getSignals`
|
|
85
85
|
|
|
@@ -158,7 +158,7 @@ activateSignalResponse({
|
|
|
158
158
|
|
|
159
159
|
### Context and Ext Passthrough
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
The framework auto-echoes the request's `context` into every response — **do not set `context` yourself in your handler return values.** It's injected post-handler only when the field isn't already present.
|
|
162
162
|
|
|
163
163
|
**Crucial:** `context` is schema-typed as an object. If your handler hand-sets a string or narrative description, validation fails with `/context: must be object` and the framework does not overwrite. Leave the field out entirely; the framework handles it.
|
|
164
164
|
|
|
@@ -168,19 +168,20 @@ Some schemas also define an `ext` field for vendor-namespaced extensions. If you
|
|
|
168
168
|
|
|
169
169
|
| SDK piece | Usage |
|
|
170
170
|
| ----------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
171
|
+
| `createAdcpServerFromPlatform(platform, opts)` | Create server from a typed `DecisioningPlatform` — compile-time specialism enforcement, auto-capabilities |
|
|
172
|
+
| `createAdcpServer(config)` *(legacy)* | v5 handler-bag entry. Mid-migration / escape-hatch only; reach via `@adcp/sdk/server/legacy/v5` |
|
|
173
|
+
| `serve(() => createAdcpServerFromPlatform(platform, opts))` | Start HTTP server on `:3001/mcp` |
|
|
173
174
|
| `signals: { getSignals, activateSignal }` | Domain group — register handlers by name |
|
|
174
175
|
| `ctx.store.put(collection, id, data)` | Persist state (activations, segment cache) across requests |
|
|
175
176
|
| `ctx.store.get(collection, id)` | Retrieve persisted state |
|
|
176
177
|
| `getSignalsResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
177
178
|
| `activateSignalResponse(data)` | Auto-applied response builder (don't call manually) |
|
|
178
179
|
| `adcpError(code, { message })` | Structured error (`SIGNAL_NOT_FOUND`, `INVALID_DESTINATION`) |
|
|
179
|
-
| `createIdempotencyStore({ backend, ttlSeconds })` | Required on every mutating tool — pass via `
|
|
180
|
-
| `memoryBackend()` / `pgBackend(pool)` | Idempotency backends (from `@adcp/sdk/server`)
|
|
180
|
+
| `createIdempotencyStore({ backend, ttlSeconds })` | Required on every mutating tool — pass via `createAdcpServerFromPlatform(platform, { idempotency })` |
|
|
181
|
+
| `memoryBackend()` / `pgBackend(pool)` | Idempotency backends (from `@adcp/sdk/server`) |
|
|
181
182
|
| `type Signal = GetSignalsResponse['signals'][number]` | Type for a single signal object |
|
|
182
183
|
|
|
183
|
-
Import: `import {
|
|
184
|
+
Import: `import { createAdcpServerFromPlatform, serve, adcpError } from '@adcp/sdk/server';`
|
|
184
185
|
Server-only: `import { createIdempotencyStore, memoryBackend } from '@adcp/sdk/server';`
|
|
185
186
|
Types: `import type { GetSignalsResponse } from '@adcp/sdk';`
|
|
186
187
|
|
|
@@ -212,20 +213,41 @@ Minimal `tsconfig.json`:
|
|
|
212
213
|
## Implementation
|
|
213
214
|
|
|
214
215
|
1. Single `.ts` file — all tools in one file
|
|
215
|
-
2. Use `
|
|
216
|
+
2. Use `createAdcpServerFromPlatform` with `signals: SignalsPlatform` on a typed `DecisioningPlatform` class — `get_adcp_capabilities` is auto-generated
|
|
216
217
|
3. Handlers return raw data objects — response builders (`getSignalsResponse`, `activateSignalResponse`) are auto-applied
|
|
217
218
|
4. Use `ctx.store` for persisting signal activations across requests (InMemoryStateStore by default)
|
|
218
219
|
5. Set `sandbox: true` for mock/demo data
|
|
219
220
|
6. Context passthrough is handled by the framework — no need to manually echo `args.context`
|
|
220
221
|
|
|
221
222
|
```typescript
|
|
222
|
-
import {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
import {
|
|
224
|
+
createAdcpServerFromPlatform,
|
|
225
|
+
serve,
|
|
226
|
+
AdcpError,
|
|
227
|
+
createIdempotencyStore,
|
|
228
|
+
memoryBackend,
|
|
229
|
+
type DecisioningPlatform,
|
|
230
|
+
type SignalsPlatform,
|
|
231
|
+
type AccountStore,
|
|
232
|
+
} from '@adcp/sdk/server';
|
|
233
|
+
import type { GetSignalsResponse } from '@adcp/sdk';
|
|
234
|
+
|
|
235
|
+
// Type the catalog explicitly. Each entry MUST include `signal_agent_segment_id`
|
|
236
|
+
// — it's the key buyers pass to `activate_signal` and the field the framework
|
|
237
|
+
// uses to auto-store the signal for hydration on the activate call. If you
|
|
238
|
+
// omit it, `tsc` flags every entry; the response validator rejects the
|
|
239
|
+
// `get_signals` reply at runtime in dev/test (strict mode default).
|
|
240
|
+
type Signal = GetSignalsResponse['signals'][number];
|
|
241
|
+
const signals: Signal[] = [
|
|
242
|
+
/* your signal objects — `signal_agent_segment_id` is required on each */
|
|
227
243
|
];
|
|
228
244
|
|
|
245
|
+
// Publisher-internal storage for activations. Persist to your real DB in
|
|
246
|
+
// production; this Map is for the worked example. Note: a module-level Map
|
|
247
|
+
// is fine for single-tenant agents but leaks across tenants in a
|
|
248
|
+
// multi-tenant deployment — partition by `account.id` if you front many.
|
|
249
|
+
const activations = new Map<string, { destinations: unknown[]; activated_at: string }>();
|
|
250
|
+
|
|
229
251
|
// Idempotency — required for v3 compliance. `activate_signal` is mutating;
|
|
230
252
|
// `get_signals` is read-only and exempt from key validation.
|
|
231
253
|
const idempotency = createIdempotencyStore({
|
|
@@ -233,86 +255,110 @@ const idempotency = createIdempotencyStore({
|
|
|
233
255
|
ttlSeconds: 86400, // 24 hours (spec bounds: 1h–7d)
|
|
234
256
|
});
|
|
235
257
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
258
|
+
class MySignals implements DecisioningPlatform {
|
|
259
|
+
capabilities = {
|
|
260
|
+
specialisms: ['signal-marketplace'] as const,
|
|
261
|
+
config: {},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Single-tenant agent: AccountStore.resolve returns the same synthetic
|
|
265
|
+
// account every request. `upsert` and `list` are optional — omit them on
|
|
266
|
+
// stateless platforms (the framework returns `UNSUPPORTED_FEATURE` to
|
|
267
|
+
// buyers calling `sync_accounts` / `list_accounts`).
|
|
268
|
+
accounts: AccountStore = {
|
|
269
|
+
resolve: async () => ({
|
|
270
|
+
id: 'sg_acc_1',
|
|
271
|
+
operator: 'me',
|
|
272
|
+
ctx_metadata: {},
|
|
273
|
+
}),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
signals: SignalsPlatform = {
|
|
277
|
+
getSignals: async params => {
|
|
278
|
+
let results = signals;
|
|
279
|
+
if (params.signal_spec) {
|
|
280
|
+
const query = params.signal_spec.toLowerCase();
|
|
281
|
+
results = results.filter(
|
|
282
|
+
s => s.name.toLowerCase().includes(query) || s.description.toLowerCase().includes(query)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (params.signal_ids) {
|
|
286
|
+
results = results.filter(s => params.signal_ids!.some(id => id.id === s.signal_id.id));
|
|
287
|
+
}
|
|
288
|
+
return { signals: results, sandbox: true };
|
|
289
|
+
},
|
|
246
290
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
return { signals: results, sandbox: true };
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
activateSignal: async (params, ctx) => {
|
|
263
|
-
const signal = signals.find(s => s.signal_agent_segment_id === params.signal_agent_segment_id);
|
|
264
|
-
// Return the error — the framework echoes returned adcpError
|
|
265
|
-
// responses verbatim. Thrown errors are caught and converted to
|
|
266
|
-
// SERVICE_UNAVAILABLE, which hides your custom code from the buyer.
|
|
267
|
-
if (!signal)
|
|
268
|
-
return adcpError('SIGNAL_NOT_FOUND', { message: `Unknown segment: ${params.signal_agent_segment_id}` });
|
|
269
|
-
|
|
270
|
-
// Persist activation in state store
|
|
271
|
-
await ctx.store.put('activations', params.signal_agent_segment_id, {
|
|
272
|
-
signal_agent_segment_id: params.signal_agent_segment_id,
|
|
273
|
-
destinations: params.destinations,
|
|
274
|
-
activated_at: new Date().toISOString(),
|
|
291
|
+
activateSignal: async params => {
|
|
292
|
+
const signal = signals.find(s => s.signal_agent_segment_id === params.signal_agent_segment_id);
|
|
293
|
+
// Throw `AdcpError` for buyer-fixable rejection. The framework
|
|
294
|
+
// catches the throw, projects to the spec's error envelope, and
|
|
295
|
+
// returns it to the buyer. Returning `adcpError(...)` from the
|
|
296
|
+
// success arm doesn't typecheck — `activateSignal` is declared
|
|
297
|
+
// `Promise<ActivateSignalSuccess>`, error is the throw path.
|
|
298
|
+
if (!signal) {
|
|
299
|
+
throw new AdcpError('SIGNAL_NOT_FOUND', {
|
|
300
|
+
recovery: 'correctable',
|
|
301
|
+
message: `Unknown segment: ${params.signal_agent_segment_id}`,
|
|
275
302
|
});
|
|
303
|
+
}
|
|
276
304
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// reference it in downstream media buys. `is_live` just
|
|
297
|
-
// flags whether the DSP has confirmed provisioning;
|
|
298
|
-
// `activation_key` is the agent's commitment.
|
|
299
|
-
activation_key: {
|
|
300
|
-
type: 'segment_id' as const,
|
|
301
|
-
segment_id: `${dest.platform}_${signal.signal_id.id}`,
|
|
302
|
-
},
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
+
// Persist activation. Single-tenant: module-level Map is fine.
|
|
306
|
+
// Multi-tenant: partition by ctx.account.id (passed as 2nd arg).
|
|
307
|
+
activations.set(params.signal_agent_segment_id, {
|
|
308
|
+
destinations: params.destinations,
|
|
309
|
+
activated_at: new Date().toISOString(),
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Platform (DSP) activation is ASYNC per spec — return `is_live: false`
|
|
313
|
+
// with `estimated_activation_duration_minutes`. The buyer polls
|
|
314
|
+
// (a subsequent `activate_signal` with the same destinations, or a
|
|
315
|
+
// provider-specific status tool) until `is_live: true`.
|
|
316
|
+
//
|
|
317
|
+
// Agent (sales-agent) activation is SYNC — return `is_live: true` with
|
|
318
|
+
// `activation_key.type: 'key_value'` and a `deployed_at` timestamp.
|
|
319
|
+
//
|
|
320
|
+
// Both shapes include `activation_key` so the buyer knows how to
|
|
321
|
+
// reference the segment when building media buys through the DSP or SA.
|
|
322
|
+
const deployments = params.destinations.map(dest => {
|
|
323
|
+
if (dest.type === 'platform') {
|
|
305
324
|
return {
|
|
306
|
-
type: '
|
|
307
|
-
|
|
308
|
-
is_live:
|
|
309
|
-
|
|
310
|
-
|
|
325
|
+
type: 'platform' as const,
|
|
326
|
+
platform: dest.platform,
|
|
327
|
+
is_live: false,
|
|
328
|
+
estimated_activation_duration_minutes: 30,
|
|
329
|
+
// Return activation_key even while is_live is false — the
|
|
330
|
+
// buyer needs to know the planned segment_id now so it can
|
|
331
|
+
// reference it in downstream media buys. `is_live` just
|
|
332
|
+
// flags whether the DSP has confirmed provisioning;
|
|
333
|
+
// `activation_key` is the agent's commitment.
|
|
334
|
+
activation_key: {
|
|
335
|
+
type: 'segment_id' as const,
|
|
336
|
+
segment_id: `${dest.platform}_${signal.signal_id.id}`,
|
|
337
|
+
},
|
|
311
338
|
};
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
type: 'agent' as const,
|
|
342
|
+
agent_url: dest.agent_url,
|
|
343
|
+
is_live: true,
|
|
344
|
+
activation_key: { type: 'key_value' as const, key: 'audience', value: signal.signal_id.id },
|
|
345
|
+
deployed_at: new Date().toISOString(),
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
return { deployments, sandbox: true };
|
|
315
349
|
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const platform = new MySignals();
|
|
354
|
+
|
|
355
|
+
serve(() =>
|
|
356
|
+
createAdcpServerFromPlatform(platform, {
|
|
357
|
+
name: 'My Signals Agent',
|
|
358
|
+
version: '1.0.0',
|
|
359
|
+
idempotency,
|
|
360
|
+
// Principal scoping for idempotency. MUST never return undefined.
|
|
361
|
+
resolveSessionKey: () => 'default-principal',
|
|
316
362
|
})
|
|
317
363
|
);
|
|
318
364
|
```
|
|
@@ -335,7 +381,7 @@ Scoping is per-principal via `resolveSessionKey` (override with `resolveIdempote
|
|
|
335
381
|
|
|
336
382
|
```ts
|
|
337
383
|
const store = createIdempotencyStore({ backend: pgBackend(pool), ttlSeconds: 86400 });
|
|
338
|
-
pool.on('error',
|
|
384
|
+
pool.on('error', err => console.error('pg pool error', err)); // prevent crash on idle-client errors
|
|
339
385
|
serve(createAgent, {
|
|
340
386
|
readinessCheck: () => store.probe(), // throws with a descriptive error if pool/table is broken
|
|
341
387
|
});
|
|
@@ -417,9 +463,10 @@ Common failure decoder:
|
|
|
417
463
|
|
|
418
464
|
| Mistake | Fix |
|
|
419
465
|
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
420
|
-
| Using `createTaskCapableServer` + `server.tool()` | Use `
|
|
466
|
+
| Using `createTaskCapableServer` + `server.tool()` | Use `createAdcpServerFromPlatform` with `signals: SignalsPlatform` on a typed `DecisioningPlatform` class |
|
|
467
|
+
| Calling `createAdcpServer` directly in new code | Reach for `createAdcpServerFromPlatform`; `createAdcpServer` lives at `@adcp/sdk/server/legacy/v5` for mid-migration / escape-hatch only |
|
|
421
468
|
| Using module-level Maps for state | Use `ctx.store.put/get` — framework provides `InMemoryStateStore` by default |
|
|
422
|
-
| Manually registering `get_adcp_capabilities` | Auto-generated by `
|
|
469
|
+
| Manually registering `get_adcp_capabilities` | Auto-generated by `createAdcpServerFromPlatform` from your typed `DecisioningPlatform` |
|
|
423
470
|
| Calling response builders manually | Handlers return raw data — `getSignalsResponse`/`activateSignalResponse` are auto-applied |
|
|
424
471
|
| Missing `signal_agent_segment_id` on signals | Buyers can't activate without it |
|
|
425
472
|
| Wrong `signal_id` shape | Marketplace: `{ source: "catalog", data_provider_domain, id }`. Owned: `{ source: "agent", agent_url, id }` |
|
|
@@ -205,6 +205,7 @@ Returns `{ signals: [{ signal_agent_segment_id, match_rate, pricing, ... }] }`.
|
|
|
205
205
|
## Transport notes
|
|
206
206
|
|
|
207
207
|
- **MCP**: `tools/call` with `{ name: 'tool_name', arguments: {...} }`. Returns `{ content, structuredContent, isError? }`. Read `structuredContent` for the typed response.
|
|
208
|
+
- **Use the official `@modelcontextprotocol/sdk` client.** The Streamable HTTP transport requires `Accept: application/json, text/event-stream` on every request — a raw `fetch()` with only `Accept: application/json` gets an unhelpful `406 Not Acceptable` from the server before any AdCP framing runs. The official client sets the header correctly; reach for it instead of rolling your own HTTP plumbing.
|
|
208
209
|
- **A2A**: `message/send` with a `DataPart` of shape `{ skill: 'tool_name', input: {...} }` (the legacy key `parameters` is also accepted). Returns an A2A `Task`; the typed response is at `task.artifacts[0].parts[0].data`.
|
|
209
210
|
|
|
210
211
|
Both transports share: idempotency, error shape, schema enforcement, and handler semantics. If a call works on one, the equivalent call works on the other.
|
|
@@ -232,6 +233,10 @@ Quick lookup before reading the full envelope. Match what you see in `adcp_error
|
|
|
232
233
|
| `keyword: 'enum'` at `/destinations/*/type` | Made-up destination type | Use `'platform'` (with `platform`) or `'agent'` (with `agent_url`). |
|
|
233
234
|
| Response carries `status: 'submitted'` and `task_id` | Async — work is queued, NOT done | Poll via `tasks/get` (A2A) or the MCP async task extension using `task_id`. |
|
|
234
235
|
| `recovery: 'transient'` (rate limit, 5xx, timeout) | Server-side, retry-safe | Retry with the **same** `idempotency_key`. |
|
|
236
|
+
<<<<<<< Updated upstream
|
|
237
|
+
| `406 Not Acceptable` before any AdCP framing | Hand-rolled HTTP without `Accept: text/event-stream` (MCP transport) | Use `@modelcontextprotocol/sdk` client; it sets the right Accept header. |
|
|
238
|
+
=======
|
|
239
|
+
>>>>>>> Stashed changes
|
|
235
240
|
| `recovery: 'correctable'` | Buyer-side fix | Read `issues[]`, patch the pointers, resend. Most cases close in one attempt. |
|
|
236
241
|
| `recovery: 'terminal'` (account suspended, payment required, …) | Requires human action | Don't retry. Surface to the user. |
|
|
237
242
|
| HTTP 401 with `WWW-Authenticate` header | Missing or expired credential | Add `Authorization` per the agent's auth spec; re-auth if applicable. |
|