@adcp/client 4.21.0 → 4.22.1
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/AGENTS.md +278 -0
- package/README.md +96 -61
- package/bin/adcp.js +342 -4
- package/dist/lib/agents/index.generated.d.ts +9 -1
- package/dist/lib/agents/index.generated.d.ts.map +1 -1
- package/dist/lib/agents/index.generated.js +12 -0
- package/dist/lib/agents/index.generated.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +2 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +10 -1
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/discovery/property-crawler.d.ts +4 -0
- package/dist/lib/discovery/property-crawler.d.ts.map +1 -1
- package/dist/lib/discovery/property-crawler.js +10 -2
- package/dist/lib/discovery/property-crawler.js.map +1 -1
- package/dist/lib/index.d.ts +4 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +6 -4
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +8 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +24 -11
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/server/index.d.ts +2 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +3 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/serve.d.ts +73 -0
- package/dist/lib/server/serve.d.ts.map +1 -0
- package/dist/lib/server/serve.js +94 -0
- package/dist/lib/server/serve.js.map +1 -0
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +1 -0
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +48 -63
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts +24 -0
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js +157 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -0
- package/dist/lib/testing/compliance/types.d.ts +1 -1
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -0
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +23 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts +8 -0
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +8 -0
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/storyboard/context.d.ts +34 -0
- package/dist/lib/testing/storyboard/context.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/context.js +257 -0
- package/dist/lib/testing/storyboard/context.js.map +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +15 -0
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/index.js +48 -0
- package/dist/lib/testing/storyboard/index.js.map +1 -0
- package/dist/lib/testing/storyboard/loader.d.ts +53 -0
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/loader.js +114 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -0
- package/dist/lib/testing/storyboard/path.d.ts +29 -0
- package/dist/lib/testing/storyboard/path.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/path.js +121 -0
- package/dist/lib/testing/storyboard/path.js.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts +28 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.js +410 -0
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/runner.js +280 -0
- package/dist/lib/testing/storyboard/runner.js.map +1 -0
- package/dist/lib/testing/storyboard/task-map.d.ts +21 -0
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/task-map.js +84 -0
- package/dist/lib/testing/storyboard/task-map.js.map +1 -0
- package/dist/lib/testing/storyboard/types.d.ts +156 -0
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/types.js +10 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -0
- package/dist/lib/testing/storyboard/validations.d.ts +17 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/validations.js +166 -0
- package/dist/lib/testing/storyboard/validations.js.map +1 -0
- package/dist/lib/testing/types.d.ts +2 -0
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +2 -2
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/schemas.generated.d.ts +193 -34
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +87 -5
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +280 -3
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +34 -3
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/utils/validate-user-agent.d.ts +8 -0
- package/dist/lib/utils/validate-user-agent.d.ts.map +1 -0
- package/dist/lib/utils/validate-user-agent.js +15 -0
- package/dist/lib/utils/validate-user-agent.js.map +1 -0
- package/dist/lib/version.d.ts +6 -0
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +7 -1
- package/dist/lib/version.js.map +1 -1
- package/docs/README.md +42 -0
- package/docs/guides/BUILD-AN-AGENT.md +294 -0
- package/docs/llms.txt +634 -0
- package/examples/README.md +106 -0
- package/examples/adcp.config.json +30 -0
- package/examples/basic-a2a.ts +76 -0
- package/examples/basic-mcp.ts +50 -0
- package/examples/batch-preview-test.ts +266 -0
- package/examples/conversation-client.ts +291 -0
- package/examples/debug-preview-response.ts +73 -0
- package/examples/debug-preview-with-logging.ts +50 -0
- package/examples/easy-config-demo.ts +242 -0
- package/examples/env-config.ts +51 -0
- package/examples/error-compliant-server.ts +237 -0
- package/examples/generative-creative-demo.ts +205 -0
- package/examples/inspect-card-formats.ts +161 -0
- package/examples/logger-usage.ts +165 -0
- package/examples/oauth-cli-example.ts +154 -0
- package/examples/pr78-async-patterns-demo.ts +247 -0
- package/examples/signals-agent.ts +163 -0
- package/examples/simple-getting-started.ts +225 -0
- package/examples/simple-protocol-demo.ts +75 -0
- package/examples/test-helpers-demo.ts +239 -0
- package/examples/zod-validation-example.ts +126 -0
- package/package.json +12 -2
- package/skills/adcp/SKILL.md +13 -2
- package/storyboards/audience_sync.yaml +199 -0
- package/storyboards/behavioral_analysis.yaml +244 -0
- package/storyboards/brand_rights.yaml +131 -0
- package/storyboards/creative_ad_server.yaml +171 -0
- package/storyboards/creative_sales_agent.yaml +169 -0
- package/storyboards/creative_template.yaml +306 -0
- package/storyboards/deterministic_testing.yaml +925 -0
- package/storyboards/error_compliance.yaml +231 -0
- package/storyboards/governance_content_standards.yaml +213 -0
- package/storyboards/governance_property_lists.yaml +372 -0
- package/storyboards/media_buy_catalog_creative.yaml +457 -0
- package/storyboards/media_buy_governance_escalation.yaml +467 -0
- package/storyboards/media_buy_guaranteed_approval.yaml +396 -0
- package/storyboards/media_buy_non_guaranteed.yaml +288 -0
- package/storyboards/media_buy_proposal_mode.yaml +369 -0
- package/storyboards/media_buy_seller.yaml +560 -0
- package/storyboards/media_buy_state_machine.yaml +254 -0
- package/storyboards/schema.yaml +65 -0
- package/storyboards/schema_validation.yaml +166 -0
- package/storyboards/si_session.yaml +384 -0
- package/storyboards/signal_marketplace.yaml +283 -0
- package/storyboards/signal_owned.yaml +211 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Example: Using Zod Schemas for Runtime Validation
|
|
4
|
+
*
|
|
5
|
+
* This example demonstrates how to use the generated Zod schemas to validate
|
|
6
|
+
* AdCP data structures at runtime. This is particularly useful for:
|
|
7
|
+
*
|
|
8
|
+
* - Validating API responses from agents
|
|
9
|
+
* - Ensuring data integrity before sending requests
|
|
10
|
+
* - Building forms with runtime validation
|
|
11
|
+
* - Integrating with zod-form libraries
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
MediaBuySchema,
|
|
16
|
+
ProductSchema,
|
|
17
|
+
GetProductsRequestSchema,
|
|
18
|
+
GetProductsResponseSchema,
|
|
19
|
+
CreateMediaBuyRequestSchema,
|
|
20
|
+
CreateMediaBuyResponseSchema,
|
|
21
|
+
} from '../src/lib/types/schemas.generated';
|
|
22
|
+
|
|
23
|
+
// Example 1: Validate a media buy structure
|
|
24
|
+
console.log('📦 Example 1: Validating a MediaBuy\n');
|
|
25
|
+
|
|
26
|
+
const mediaBuy = {
|
|
27
|
+
media_buy_id: 'mb_12345',
|
|
28
|
+
status: 'active',
|
|
29
|
+
promoted_offering: 'Nike Spring Collection 2024',
|
|
30
|
+
total_budget: 50000,
|
|
31
|
+
packages: [],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mediaBuyResult = MediaBuySchema.safeParse(mediaBuy);
|
|
35
|
+
|
|
36
|
+
if (mediaBuyResult.success) {
|
|
37
|
+
console.log('✅ MediaBuy is valid!');
|
|
38
|
+
console.log('Validated data:', mediaBuyResult.data);
|
|
39
|
+
} else {
|
|
40
|
+
console.log('❌ MediaBuy validation failed:');
|
|
41
|
+
console.log(mediaBuyResult.error.format());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Example 2: Validate and catch errors in a product
|
|
45
|
+
console.log('\n📦 Example 2: Validating a Product (with intentional error)\n');
|
|
46
|
+
|
|
47
|
+
const invalidProduct = {
|
|
48
|
+
product_id: 'prod_123',
|
|
49
|
+
// Missing required fields like 'name', 'description', etc.
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const productResult = ProductSchema.safeParse(invalidProduct);
|
|
53
|
+
|
|
54
|
+
if (productResult.success) {
|
|
55
|
+
console.log('✅ Product is valid!');
|
|
56
|
+
} else {
|
|
57
|
+
console.log('❌ Product validation failed (as expected):');
|
|
58
|
+
console.log(
|
|
59
|
+
'Issues found:',
|
|
60
|
+
productResult.error.issues.map(issue => ({
|
|
61
|
+
path: issue.path.join('.'),
|
|
62
|
+
message: issue.message,
|
|
63
|
+
}))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Example 3: Validate request before sending to agent
|
|
68
|
+
console.log('\n📦 Example 3: Validating a GetProducts Request\n');
|
|
69
|
+
|
|
70
|
+
const getProductsRequest = {
|
|
71
|
+
brief: 'Looking for premium display inventory targeting tech professionals',
|
|
72
|
+
brand_manifest: {
|
|
73
|
+
brand_name: 'TechCorp',
|
|
74
|
+
product_catalog: [],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const requestResult = GetProductsRequestSchema.safeParse(getProductsRequest);
|
|
79
|
+
|
|
80
|
+
if (requestResult.success) {
|
|
81
|
+
console.log('✅ Request is valid and ready to send!');
|
|
82
|
+
console.log('Brief:', requestResult.data.brief);
|
|
83
|
+
} else {
|
|
84
|
+
console.log('❌ Request validation failed:');
|
|
85
|
+
console.log(requestResult.error.format());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Example 4: Validate response from agent
|
|
89
|
+
console.log('\n📦 Example 4: Validating a GetProducts Response\n');
|
|
90
|
+
|
|
91
|
+
const getProductsResponse = {
|
|
92
|
+
products: [],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const responseResult = GetProductsResponseSchema.safeParse(getProductsResponse);
|
|
96
|
+
|
|
97
|
+
if (responseResult.success) {
|
|
98
|
+
console.log('✅ Response is valid!');
|
|
99
|
+
console.log('Number of products:', responseResult.data.products.length);
|
|
100
|
+
} else {
|
|
101
|
+
console.log('❌ Response validation failed:');
|
|
102
|
+
console.log(responseResult.error.format());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Example 5: Using parse() instead of safeParse() to throw errors
|
|
106
|
+
console.log('\n📦 Example 5: Using parse() for stricter validation\n');
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// This will throw a ZodError if validation fails
|
|
110
|
+
const strictMediaBuy = MediaBuySchema.parse(mediaBuy);
|
|
111
|
+
console.log('✅ Strict validation passed!');
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.log('❌ Strict validation failed and threw an error');
|
|
114
|
+
console.log(error);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('\n✨ Examples complete!\n');
|
|
118
|
+
|
|
119
|
+
// Additional use cases:
|
|
120
|
+
console.log('💡 Additional use cases for Zod schemas:\n');
|
|
121
|
+
console.log('1. Form validation with React Hook Form + Zod');
|
|
122
|
+
console.log('2. API middleware for validating requests/responses');
|
|
123
|
+
console.log('3. Testing: Validate mock data matches schema');
|
|
124
|
+
console.log('4. OpenAPI generation with zod-to-openapi');
|
|
125
|
+
console.log('5. Database schema validation before persistence');
|
|
126
|
+
console.log('6. Type-safe transformations with .transform()');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adcp/client",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.22.1",
|
|
4
4
|
"description": "AdCP client library with protocol support for MCP and A2A",
|
|
5
5
|
"main": "dist/lib/index.js",
|
|
6
6
|
"types": "dist/lib/index.d.ts",
|
|
@@ -56,6 +56,11 @@
|
|
|
56
56
|
"bin/**/*.js",
|
|
57
57
|
"skills/**/*",
|
|
58
58
|
".claude-plugin/**/*",
|
|
59
|
+
"storyboards/**/*",
|
|
60
|
+
"docs/llms.txt",
|
|
61
|
+
"docs/guides/BUILD-AN-AGENT.md",
|
|
62
|
+
"examples/**/*",
|
|
63
|
+
"AGENTS.md",
|
|
59
64
|
"README.md",
|
|
60
65
|
"LICENSE"
|
|
61
66
|
],
|
|
@@ -81,6 +86,7 @@
|
|
|
81
86
|
"generate-types": "tsx scripts/generate-types.ts",
|
|
82
87
|
"generate-registry-types": "tsx scripts/generate-registry-types.ts",
|
|
83
88
|
"generate-zod-schemas": "tsx scripts/generate-zod-from-ts.ts",
|
|
89
|
+
"generate-agent-docs": "tsx scripts/generate-agent-docs.ts",
|
|
84
90
|
"sync-version": "tsx scripts/sync-version.ts",
|
|
85
91
|
"validate-schemas": "tsx scripts/validate-schemas.ts",
|
|
86
92
|
"lint": "eslint 'src/lib/testing/**/*.ts'",
|
|
@@ -94,6 +100,7 @@
|
|
|
94
100
|
"ci:validate": "node scripts/ci-validate.js",
|
|
95
101
|
"ci:quick": "npm run format:check && npm run typecheck && npm run build:lib && npm test",
|
|
96
102
|
"ci:schema-check": "npm run sync-schemas && npm run generate-types && npm run generate-registry-types && git diff --exit-code src/lib/types/ src/lib/agents/ src/lib/registry/types.generated.ts schemas/registry/registry.yaml || (echo '⚠️ Generated files are out of sync. Run: npm run sync-schemas && npm run generate-types && npm run generate-registry-types' && exit 1)",
|
|
103
|
+
"ci:docs-check": "npm run generate-agent-docs && git diff --exit-code -I '> Generated at:' docs/llms.txt docs/TYPE-SUMMARY.md || (echo '⚠️ Agent docs are out of sync. Run: npm run generate-agent-docs' && exit 1)",
|
|
97
104
|
"ci:pre-push": "npm run ci:schema-check && npm run ci:quick",
|
|
98
105
|
"hooks:install": "node scripts/install-hooks.js",
|
|
99
106
|
"hooks:uninstall": "rm -f .git/hooks/pre-push",
|
|
@@ -129,6 +136,9 @@
|
|
|
129
136
|
"url": "https://github.com/adcontextprotocol/adcp-client/issues",
|
|
130
137
|
"email": "bugs@adcontextprotocol.org"
|
|
131
138
|
},
|
|
139
|
+
"dependencies": {
|
|
140
|
+
"yaml": "^2.7.1"
|
|
141
|
+
},
|
|
132
142
|
"peerDependencies": {
|
|
133
143
|
"@a2a-js/sdk": "^0.3.4",
|
|
134
144
|
"@modelcontextprotocol/sdk": "^1.17.5",
|
|
@@ -144,7 +154,7 @@
|
|
|
144
154
|
"@changesets/cli": "^2.29.7",
|
|
145
155
|
"@commitlint/cli": "^19.6.0",
|
|
146
156
|
"@commitlint/config-conventional": "^19.6.0",
|
|
147
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
157
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
148
158
|
"@opentelemetry/api": "^1.9.0",
|
|
149
159
|
"@types/node": "^20.19.13",
|
|
150
160
|
"eslint": "^10.0.3",
|
package/skills/adcp/SKILL.md
CHANGED
|
@@ -73,30 +73,40 @@ adcp https://agent.example.com get_products '{}' --protocol a2a
|
|
|
73
73
|
|
|
74
74
|
## Test runner
|
|
75
75
|
|
|
76
|
-
Run protocol compliance tests against any AdCP agent.
|
|
76
|
+
Run protocol compliance tests against any AdCP agent. 24 built-in scenarios.
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
79
|
adcp test <agent> [scenario] [options]
|
|
80
80
|
adcp test --list-scenarios
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
### Scenarios (run `adcp test --list-scenarios` for all
|
|
83
|
+
### Scenarios (run `adcp test --list-scenarios` for all 24 with descriptions)
|
|
84
84
|
| Scenario | What it tests |
|
|
85
85
|
|----------|---------------|
|
|
86
86
|
| `health_check` | Basic connectivity |
|
|
87
87
|
| `discovery` | get_products, list_creative_formats, list_authorized_properties |
|
|
88
88
|
| `create_media_buy` | Discovery + create a media buy (dry-run by default) |
|
|
89
89
|
| `full_sales_flow` | Full lifecycle: discovery, create, update, delivery |
|
|
90
|
+
| `creative_sync` | Test sync_creatives flow |
|
|
91
|
+
| `creative_inline` | Test inline creatives in create_media_buy |
|
|
90
92
|
| `creative_flow` | Creative agent: list_formats, build, preview |
|
|
91
93
|
| `signals_flow` | Signals: get_signals, activate |
|
|
92
94
|
| `validation` | Schema validation (invalid inputs should be rejected) |
|
|
93
95
|
| `error_handling` | Verify proper error responses |
|
|
94
96
|
| `pricing_edge_cases` | Auction vs fixed pricing, min spend, bid_price |
|
|
97
|
+
| `temporal_validation` | Date/time ordering and format validation |
|
|
95
98
|
| `behavior_analysis` | Auth, brief relevance, filtering behavior |
|
|
99
|
+
| `response_consistency` | Schema errors, pagination bugs, data mismatches |
|
|
96
100
|
| `capability_discovery` | v3: get_adcp_capabilities |
|
|
97
101
|
| `governance_property_lists` | v3: property list CRUD |
|
|
98
102
|
| `governance_content_standards` | v3: content standards listing and calibration |
|
|
99
103
|
| `si_session_lifecycle` | v3: structured interaction sessions |
|
|
104
|
+
| `si_availability` | v3: SI offering availability check |
|
|
105
|
+
| `campaign_governance` | v3: full governance lifecycle: sync_plans → check → execute → report |
|
|
106
|
+
| `campaign_governance_denied` | v3: denied flow: over-budget, unauthorized market |
|
|
107
|
+
| `campaign_governance_conditions` | v3: apply conditions → re-check |
|
|
108
|
+
| `campaign_governance_delivery` | v3: delivery monitoring with drift detection |
|
|
109
|
+
| `seller_governance_context` | v3: verify seller persists governance_context |
|
|
100
110
|
|
|
101
111
|
### Examples
|
|
102
112
|
```bash
|
|
@@ -204,6 +214,7 @@ If not installed, prefix all commands with `npx @adcp/client`. Requires Node.js
|
|
|
204
214
|
- **"brand" / "property" / "registry" / "look up" / "validate domain"** — `adcp registry <command>`
|
|
205
215
|
- **Specific tool name mentioned** — `adcp <agent> <tool> '<payload>'`
|
|
206
216
|
- **"compare protocols"** — Run same call with `--protocol mcp` then `--protocol a2a`, diff results
|
|
217
|
+
- **"build an agent" / "implement AdCP" / "server side"** — Read `docs/guides/BUILD-AN-AGENT.md` for server setup, and `storyboards/` for expected tool call sequences. Use `docs/llms.txt` for the protocol overview.
|
|
207
218
|
|
|
208
219
|
### Step 3: Handle authentication
|
|
209
220
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
id: audience_sync
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
title: "Audience sync"
|
|
4
|
+
category: audiences
|
|
5
|
+
summary: "Tests sync_audiences: account discovery, audience creation with hashed identifiers, and audience deletion."
|
|
6
|
+
track: audiences
|
|
7
|
+
required_tools:
|
|
8
|
+
- sync_audiences
|
|
9
|
+
platform_types:
|
|
10
|
+
- display_ad_server
|
|
11
|
+
- social_platform
|
|
12
|
+
- retail_media
|
|
13
|
+
- dsp
|
|
14
|
+
- pmax_platform
|
|
15
|
+
|
|
16
|
+
narrative: |
|
|
17
|
+
You support audience syncing via the sync_audiences task. Buyers push first-party
|
|
18
|
+
audience segments to your platform using hashed identifiers (emails, phones). Your
|
|
19
|
+
platform matches those identifiers against your user graph and returns match rates.
|
|
20
|
+
|
|
21
|
+
This storyboard verifies the full audience lifecycle: discover an account to sync
|
|
22
|
+
against, create a test audience with hashed identifiers, and clean up by deleting it.
|
|
23
|
+
|
|
24
|
+
agent:
|
|
25
|
+
interaction_model: media_buy_seller
|
|
26
|
+
capabilities:
|
|
27
|
+
- sells_media
|
|
28
|
+
- accepts_audiences
|
|
29
|
+
examples:
|
|
30
|
+
- "Retail media networks"
|
|
31
|
+
- "Social platforms"
|
|
32
|
+
- "DSPs with audience onboarding"
|
|
33
|
+
|
|
34
|
+
caller:
|
|
35
|
+
role: buyer_agent
|
|
36
|
+
example: "Scope3 (DSP)"
|
|
37
|
+
|
|
38
|
+
prerequisites:
|
|
39
|
+
description: |
|
|
40
|
+
The caller needs an active account on the seller platform. The account_setup phase
|
|
41
|
+
discovers or creates the account relationship before syncing audiences.
|
|
42
|
+
test_kit: "test-kits/acme-outdoor.yaml"
|
|
43
|
+
|
|
44
|
+
phases:
|
|
45
|
+
- id: account_setup
|
|
46
|
+
title: "Account setup"
|
|
47
|
+
narrative: |
|
|
48
|
+
Before syncing audiences, the buyer needs an account on the seller platform.
|
|
49
|
+
This phase discovers an existing account via list_accounts or establishes one
|
|
50
|
+
via sync_accounts.
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- id: discover_account
|
|
54
|
+
title: "Discover or create account"
|
|
55
|
+
narrative: |
|
|
56
|
+
List existing accounts to find one suitable for audience sync. If no accounts
|
|
57
|
+
exist, sync_accounts creates one. The account_id is captured for use in
|
|
58
|
+
subsequent audience operations.
|
|
59
|
+
task: list_accounts
|
|
60
|
+
schema_ref: "account/list-accounts-request.json"
|
|
61
|
+
response_schema_ref: "account/list-accounts-response.json"
|
|
62
|
+
doc_ref: "/accounts/tasks/list_accounts"
|
|
63
|
+
comply_scenario: account_setup
|
|
64
|
+
stateful: false
|
|
65
|
+
context_outputs:
|
|
66
|
+
- name: account_id
|
|
67
|
+
path: "accounts[0].account_id"
|
|
68
|
+
- name: account_ref
|
|
69
|
+
path: "accounts[0]"
|
|
70
|
+
expected: |
|
|
71
|
+
Return at least one account with:
|
|
72
|
+
- account_id: platform-assigned identifier
|
|
73
|
+
- status: active (required for audience sync)
|
|
74
|
+
|
|
75
|
+
sample_request: {}
|
|
76
|
+
|
|
77
|
+
validations:
|
|
78
|
+
- check: response_schema
|
|
79
|
+
description: "Response matches list-accounts-response.json schema"
|
|
80
|
+
- check: field_present
|
|
81
|
+
path: "accounts[0].account_id"
|
|
82
|
+
description: "At least one account exists with an account_id"
|
|
83
|
+
|
|
84
|
+
- id: audience_sync
|
|
85
|
+
title: "Audience sync"
|
|
86
|
+
narrative: |
|
|
87
|
+
The buyer syncs a test audience containing hashed email and phone identifiers.
|
|
88
|
+
The seller platform matches those identifiers and returns per-audience results
|
|
89
|
+
including action (created/updated), status, and match rates.
|
|
90
|
+
|
|
91
|
+
After verifying creation, the test audience is deleted to clean up.
|
|
92
|
+
|
|
93
|
+
steps:
|
|
94
|
+
- id: discover_audiences
|
|
95
|
+
title: "Discover existing audiences"
|
|
96
|
+
narrative: |
|
|
97
|
+
Call sync_audiences with no audiences array to discover what audiences
|
|
98
|
+
already exist on the platform for this account. This is a read-only
|
|
99
|
+
discovery call.
|
|
100
|
+
task: sync_audiences
|
|
101
|
+
schema_ref: "audience/sync-audiences-request.json"
|
|
102
|
+
response_schema_ref: "audience/sync-audiences-response.json"
|
|
103
|
+
doc_ref: "/audiences/tasks/sync_audiences"
|
|
104
|
+
comply_scenario: audience_sync
|
|
105
|
+
stateful: false
|
|
106
|
+
context_outputs:
|
|
107
|
+
- name: existing_audience_count
|
|
108
|
+
path: "audiences.length"
|
|
109
|
+
expected: |
|
|
110
|
+
Return existing audiences for the account (may be empty).
|
|
111
|
+
Each audience should have an audience_id and status.
|
|
112
|
+
|
|
113
|
+
sample_request:
|
|
114
|
+
account:
|
|
115
|
+
brand:
|
|
116
|
+
domain: "acmeoutdoor.com"
|
|
117
|
+
operator: "pinnacle-agency.com"
|
|
118
|
+
|
|
119
|
+
validations:
|
|
120
|
+
- check: response_schema
|
|
121
|
+
description: "Response matches sync-audiences-response.json schema"
|
|
122
|
+
|
|
123
|
+
- id: create_audience
|
|
124
|
+
title: "Create test audience"
|
|
125
|
+
narrative: |
|
|
126
|
+
Push a test audience with hashed email and phone identifiers. The seller
|
|
127
|
+
matches identifiers against their user graph and returns the audience with
|
|
128
|
+
action: created, match counts, and match rate.
|
|
129
|
+
task: sync_audiences
|
|
130
|
+
schema_ref: "audience/sync-audiences-request.json"
|
|
131
|
+
response_schema_ref: "audience/sync-audiences-response.json"
|
|
132
|
+
doc_ref: "/audiences/tasks/sync_audiences"
|
|
133
|
+
comply_scenario: audience_sync
|
|
134
|
+
stateful: true
|
|
135
|
+
context_outputs:
|
|
136
|
+
- name: test_audience_id
|
|
137
|
+
path: "audiences[0].audience_id"
|
|
138
|
+
expected: |
|
|
139
|
+
Accept the audience and return:
|
|
140
|
+
- audience_id: matches the submitted ID
|
|
141
|
+
- action: created
|
|
142
|
+
- status: active or processing
|
|
143
|
+
- uploaded_count: number of identifiers received
|
|
144
|
+
- matched_count: number of identifiers matched
|
|
145
|
+
- effective_match_rate: ratio of matched to uploaded
|
|
146
|
+
|
|
147
|
+
sample_request:
|
|
148
|
+
account:
|
|
149
|
+
brand:
|
|
150
|
+
domain: "acmeoutdoor.com"
|
|
151
|
+
operator: "pinnacle-agency.com"
|
|
152
|
+
audiences:
|
|
153
|
+
- audience_id: "adcp-test-audience-001"
|
|
154
|
+
name: "AdCP E2E Test Audience"
|
|
155
|
+
add:
|
|
156
|
+
- hashed_email: "a000000000000000000000000000000000000000000000000000000000000000"
|
|
157
|
+
- hashed_phone: "b000000000000000000000000000000000000000000000000000000000000000"
|
|
158
|
+
|
|
159
|
+
validations:
|
|
160
|
+
- check: response_schema
|
|
161
|
+
description: "Response matches sync-audiences-response.json schema"
|
|
162
|
+
- check: field_present
|
|
163
|
+
path: "audiences[0].audience_id"
|
|
164
|
+
description: "Audience has an audience_id"
|
|
165
|
+
- check: field_present
|
|
166
|
+
path: "audiences[0].action"
|
|
167
|
+
description: "Audience has an action (created or updated)"
|
|
168
|
+
|
|
169
|
+
- id: delete_audience
|
|
170
|
+
title: "Delete test audience"
|
|
171
|
+
narrative: |
|
|
172
|
+
Clean up by deleting the test audience. The seller should acknowledge the
|
|
173
|
+
deletion with action: deleted.
|
|
174
|
+
task: sync_audiences
|
|
175
|
+
schema_ref: "audience/sync-audiences-request.json"
|
|
176
|
+
response_schema_ref: "audience/sync-audiences-response.json"
|
|
177
|
+
doc_ref: "/audiences/tasks/sync_audiences"
|
|
178
|
+
comply_scenario: audience_sync
|
|
179
|
+
stateful: true
|
|
180
|
+
expected: |
|
|
181
|
+
Delete the test audience and return:
|
|
182
|
+
- audience_id: matches the test audience
|
|
183
|
+
- action: deleted
|
|
184
|
+
|
|
185
|
+
sample_request:
|
|
186
|
+
account:
|
|
187
|
+
brand:
|
|
188
|
+
domain: "acmeoutdoor.com"
|
|
189
|
+
operator: "pinnacle-agency.com"
|
|
190
|
+
audiences:
|
|
191
|
+
- audience_id: "adcp-test-audience-001"
|
|
192
|
+
delete: true
|
|
193
|
+
|
|
194
|
+
validations:
|
|
195
|
+
- check: response_schema
|
|
196
|
+
description: "Response matches sync-audiences-response.json schema"
|
|
197
|
+
- check: field_present
|
|
198
|
+
path: "audiences[0].action"
|
|
199
|
+
description: "Audience deletion acknowledged with action field"
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
id: behavioral_analysis
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
title: "Behavioral analysis"
|
|
4
|
+
category: products
|
|
5
|
+
summary: "Validates product discovery behavior, response consistency, and pricing edge cases."
|
|
6
|
+
track: products
|
|
7
|
+
required_tools:
|
|
8
|
+
- get_products
|
|
9
|
+
platform_types:
|
|
10
|
+
- display_ad_server
|
|
11
|
+
- video_ad_server
|
|
12
|
+
- social_platform
|
|
13
|
+
- retail_media
|
|
14
|
+
- search_platform
|
|
15
|
+
- audio_platform
|
|
16
|
+
- linear_tv_platform
|
|
17
|
+
- dsp
|
|
18
|
+
- pmax_platform
|
|
19
|
+
- ai_ad_network
|
|
20
|
+
- ai_platform
|
|
21
|
+
- generative_dsp
|
|
22
|
+
|
|
23
|
+
narrative: |
|
|
24
|
+
You expose a get_products task that accepts natural-language briefs and returns structured
|
|
25
|
+
product listings with pricing, delivery forecasts, and targeting. This storyboard verifies
|
|
26
|
+
three properties of that task:
|
|
27
|
+
|
|
28
|
+
1. Behavior analysis — do results change when the brief changes? Does the agent filter
|
|
29
|
+
products by brief content or return a static catalog?
|
|
30
|
+
2. Response consistency — given the same brief twice, does the agent return the same
|
|
31
|
+
product IDs? Deterministic responses are expected for identical inputs.
|
|
32
|
+
3. Pricing edge cases — do products declare valid pricing_options structures with
|
|
33
|
+
recognized delivery_type values (guaranteed or non_guaranteed)?
|
|
34
|
+
|
|
35
|
+
agent:
|
|
36
|
+
interaction_model: media_buy_seller
|
|
37
|
+
capabilities:
|
|
38
|
+
- sells_media
|
|
39
|
+
- accepts_briefs
|
|
40
|
+
examples:
|
|
41
|
+
- "Yahoo"
|
|
42
|
+
- "Retail media networks"
|
|
43
|
+
- "Publisher platforms"
|
|
44
|
+
|
|
45
|
+
caller:
|
|
46
|
+
role: buyer_agent
|
|
47
|
+
example: "Scope3 (DSP)"
|
|
48
|
+
|
|
49
|
+
prerequisites:
|
|
50
|
+
description: |
|
|
51
|
+
The caller needs a brand identity for product discovery requests.
|
|
52
|
+
The test kit provides a sample brand (Acme Outdoor) with campaign parameters.
|
|
53
|
+
test_kit: "test-kits/acme-outdoor.yaml"
|
|
54
|
+
|
|
55
|
+
phases:
|
|
56
|
+
- id: behavior_analysis
|
|
57
|
+
title: "Behavior analysis"
|
|
58
|
+
narrative: |
|
|
59
|
+
Tests whether the agent filters products based on the brief content. Two different
|
|
60
|
+
briefs are sent — one narrow (podcast audio only) and one broad (all products across
|
|
61
|
+
all channels). If the agent interprets briefs, the narrow brief should return fewer
|
|
62
|
+
products than the broad brief.
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- id: get_products_narrow
|
|
66
|
+
title: "Get products with narrow brief"
|
|
67
|
+
narrative: |
|
|
68
|
+
Send a narrow brief requesting only podcast audio advertising. Capture the
|
|
69
|
+
returned product IDs so they can be compared with a broader request.
|
|
70
|
+
task: get_products
|
|
71
|
+
schema_ref: "media-buy/get-products-request.json"
|
|
72
|
+
response_schema_ref: "media-buy/get-products-response.json"
|
|
73
|
+
doc_ref: "/media-buy/task-reference/get_products"
|
|
74
|
+
comply_scenario: behavior_analysis
|
|
75
|
+
stateful: false
|
|
76
|
+
context_outputs:
|
|
77
|
+
- name: narrow_product_ids
|
|
78
|
+
path: "products[*].product_id"
|
|
79
|
+
- name: narrow_product_count
|
|
80
|
+
path: "products.length"
|
|
81
|
+
expected: |
|
|
82
|
+
Return products matching the narrow brief. The result set should be scoped
|
|
83
|
+
to audio/podcast inventory if the agent performs brief filtering.
|
|
84
|
+
|
|
85
|
+
sample_request:
|
|
86
|
+
buying_mode: "brief"
|
|
87
|
+
brief: "Looking specifically for podcast audio advertising only"
|
|
88
|
+
brand:
|
|
89
|
+
domain: "acmeoutdoor.com"
|
|
90
|
+
|
|
91
|
+
validations:
|
|
92
|
+
- check: response_schema
|
|
93
|
+
description: "Response matches get-products-response.json schema"
|
|
94
|
+
- check: field_present
|
|
95
|
+
path: "products"
|
|
96
|
+
description: "Response contains a products array"
|
|
97
|
+
|
|
98
|
+
- id: get_products_broad
|
|
99
|
+
title: "Get products with broad brief"
|
|
100
|
+
narrative: |
|
|
101
|
+
Send a broad brief requesting all products across all channels. Compare the
|
|
102
|
+
result count against the narrow brief to determine whether the agent filters
|
|
103
|
+
by brief content.
|
|
104
|
+
task: get_products
|
|
105
|
+
schema_ref: "media-buy/get-products-request.json"
|
|
106
|
+
response_schema_ref: "media-buy/get-products-response.json"
|
|
107
|
+
doc_ref: "/media-buy/task-reference/get_products"
|
|
108
|
+
comply_scenario: behavior_analysis
|
|
109
|
+
stateful: false
|
|
110
|
+
expected: |
|
|
111
|
+
Return all available products. If the agent performs brief filtering, this
|
|
112
|
+
response should contain more products than the narrow brief response.
|
|
113
|
+
|
|
114
|
+
sample_request:
|
|
115
|
+
buying_mode: "brief"
|
|
116
|
+
brief: "Show all products across all channels and formats"
|
|
117
|
+
brand:
|
|
118
|
+
domain: "acmeoutdoor.com"
|
|
119
|
+
|
|
120
|
+
validations:
|
|
121
|
+
- check: response_schema
|
|
122
|
+
description: "Response matches get-products-response.json schema"
|
|
123
|
+
- check: field_present
|
|
124
|
+
path: "products"
|
|
125
|
+
description: "Response contains a products array"
|
|
126
|
+
- check: compare_context
|
|
127
|
+
description: "Broad brief returns >= narrow brief product count"
|
|
128
|
+
context_ref: narrow_product_count
|
|
129
|
+
comparison: ">="
|
|
130
|
+
|
|
131
|
+
- id: response_consistency
|
|
132
|
+
title: "Response consistency"
|
|
133
|
+
narrative: |
|
|
134
|
+
Calls get_products twice with the identical brief and brand. The agent should
|
|
135
|
+
return the same product IDs both times. Non-deterministic responses indicate
|
|
136
|
+
randomness in product selection or caching issues.
|
|
137
|
+
|
|
138
|
+
steps:
|
|
139
|
+
- id: get_products_first
|
|
140
|
+
title: "First call with fixed brief"
|
|
141
|
+
narrative: |
|
|
142
|
+
Send a specific brief and capture the returned product IDs for comparison.
|
|
143
|
+
task: get_products
|
|
144
|
+
schema_ref: "media-buy/get-products-request.json"
|
|
145
|
+
response_schema_ref: "media-buy/get-products-response.json"
|
|
146
|
+
doc_ref: "/media-buy/task-reference/get_products"
|
|
147
|
+
comply_scenario: response_consistency
|
|
148
|
+
stateful: false
|
|
149
|
+
context_outputs:
|
|
150
|
+
- name: first_call_product_ids
|
|
151
|
+
path: "products[*].product_id"
|
|
152
|
+
expected: |
|
|
153
|
+
Return products matching the brief. Product IDs are captured for comparison
|
|
154
|
+
with a second identical call.
|
|
155
|
+
|
|
156
|
+
sample_request:
|
|
157
|
+
buying_mode: "brief"
|
|
158
|
+
brief: "Premium video inventory on sports and outdoor lifestyle publishers. Q2 flight, $50K budget. Adults 25-54, US and Canada."
|
|
159
|
+
brand:
|
|
160
|
+
domain: "acmeoutdoor.com"
|
|
161
|
+
|
|
162
|
+
validations:
|
|
163
|
+
- check: response_schema
|
|
164
|
+
description: "Response matches get-products-response.json schema"
|
|
165
|
+
- check: field_present
|
|
166
|
+
path: "products[0].product_id"
|
|
167
|
+
description: "At least one product returned with a product_id"
|
|
168
|
+
|
|
169
|
+
- id: get_products_second
|
|
170
|
+
title: "Second call with identical brief"
|
|
171
|
+
narrative: |
|
|
172
|
+
Repeat the exact same request. The returned product IDs should match
|
|
173
|
+
those from the first call.
|
|
174
|
+
task: get_products
|
|
175
|
+
schema_ref: "media-buy/get-products-request.json"
|
|
176
|
+
response_schema_ref: "media-buy/get-products-response.json"
|
|
177
|
+
doc_ref: "/media-buy/task-reference/get_products"
|
|
178
|
+
comply_scenario: response_consistency
|
|
179
|
+
stateful: false
|
|
180
|
+
expected: |
|
|
181
|
+
Return the same product IDs as the first call. Consistent responses
|
|
182
|
+
for identical inputs are expected.
|
|
183
|
+
|
|
184
|
+
sample_request:
|
|
185
|
+
buying_mode: "brief"
|
|
186
|
+
brief: "Premium video inventory on sports and outdoor lifestyle publishers. Q2 flight, $50K budget. Adults 25-54, US and Canada."
|
|
187
|
+
brand:
|
|
188
|
+
domain: "acmeoutdoor.com"
|
|
189
|
+
|
|
190
|
+
validations:
|
|
191
|
+
- check: response_schema
|
|
192
|
+
description: "Response matches get-products-response.json schema"
|
|
193
|
+
- check: match_context
|
|
194
|
+
description: "Product IDs match the first call"
|
|
195
|
+
context_ref: first_call_product_ids
|
|
196
|
+
path: "products[*].product_id"
|
|
197
|
+
|
|
198
|
+
- id: pricing_edge_cases
|
|
199
|
+
title: "Pricing edge cases"
|
|
200
|
+
narrative: |
|
|
201
|
+
Validates the structure of pricing_options on returned products. Each product
|
|
202
|
+
should declare a delivery_type (guaranteed or non_guaranteed) and well-formed
|
|
203
|
+
pricing options with recognized fields.
|
|
204
|
+
|
|
205
|
+
steps:
|
|
206
|
+
- id: get_products_pricing
|
|
207
|
+
title: "Fetch products for pricing analysis"
|
|
208
|
+
narrative: |
|
|
209
|
+
Request all products with pricing details. Validate that each product has
|
|
210
|
+
a valid delivery_type and that pricing_options contain recognized fields
|
|
211
|
+
(fixed_price, floor_price, price_guidance, min_spend_per_package).
|
|
212
|
+
task: get_products
|
|
213
|
+
schema_ref: "media-buy/get-products-request.json"
|
|
214
|
+
response_schema_ref: "media-buy/get-products-response.json"
|
|
215
|
+
doc_ref: "/media-buy/task-reference/get_products"
|
|
216
|
+
comply_scenario: pricing_edge_cases
|
|
217
|
+
stateful: false
|
|
218
|
+
expected: |
|
|
219
|
+
Return products with well-formed pricing structures:
|
|
220
|
+
- Each product has a delivery_type: guaranteed or non_guaranteed
|
|
221
|
+
- Each pricing_option has a pricing_option_id
|
|
222
|
+
- Pricing fields are valid (no negative values, recognized pricing models)
|
|
223
|
+
|
|
224
|
+
sample_request:
|
|
225
|
+
buying_mode: "brief"
|
|
226
|
+
brief: "Show all products with pricing details"
|
|
227
|
+
brand:
|
|
228
|
+
domain: "acmeoutdoor.com"
|
|
229
|
+
|
|
230
|
+
validations:
|
|
231
|
+
- check: response_schema
|
|
232
|
+
description: "Response matches get-products-response.json schema"
|
|
233
|
+
- check: field_present
|
|
234
|
+
path: "products"
|
|
235
|
+
description: "Response contains products array"
|
|
236
|
+
- check: field_present
|
|
237
|
+
path: "products[0].delivery_type"
|
|
238
|
+
description: "Each product declares guaranteed or non_guaranteed delivery"
|
|
239
|
+
- check: field_value
|
|
240
|
+
path: "products[*].delivery_type"
|
|
241
|
+
allowed_values:
|
|
242
|
+
- "guaranteed"
|
|
243
|
+
- "non_guaranteed"
|
|
244
|
+
description: "delivery_type is a recognized value"
|