@adcp/client 4.21.0 → 4.22.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.
Files changed (160) hide show
  1. package/AGENTS.md +278 -0
  2. package/README.md +96 -61
  3. package/bin/adcp.js +342 -4
  4. package/dist/lib/agents/index.generated.d.ts +9 -1
  5. package/dist/lib/agents/index.generated.d.ts.map +1 -1
  6. package/dist/lib/agents/index.generated.js +12 -0
  7. package/dist/lib/agents/index.generated.js.map +1 -1
  8. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  9. package/dist/lib/core/SingleAgentClient.d.ts +2 -1
  10. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  11. package/dist/lib/core/SingleAgentClient.js +10 -1
  12. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  13. package/dist/lib/discovery/property-crawler.d.ts +4 -0
  14. package/dist/lib/discovery/property-crawler.d.ts.map +1 -1
  15. package/dist/lib/discovery/property-crawler.js +10 -2
  16. package/dist/lib/discovery/property-crawler.js.map +1 -1
  17. package/dist/lib/index.d.ts +4 -4
  18. package/dist/lib/index.d.ts.map +1 -1
  19. package/dist/lib/index.js +6 -4
  20. package/dist/lib/index.js.map +1 -1
  21. package/dist/lib/protocols/index.d.ts.map +1 -1
  22. package/dist/lib/protocols/index.js +8 -6
  23. package/dist/lib/protocols/index.js.map +1 -1
  24. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  25. package/dist/lib/protocols/mcp.js +24 -11
  26. package/dist/lib/protocols/mcp.js.map +1 -1
  27. package/dist/lib/server/index.d.ts +2 -0
  28. package/dist/lib/server/index.d.ts.map +1 -1
  29. package/dist/lib/server/index.js +3 -1
  30. package/dist/lib/server/index.js.map +1 -1
  31. package/dist/lib/server/serve.d.ts +45 -0
  32. package/dist/lib/server/serve.d.ts.map +1 -0
  33. package/dist/lib/server/serve.js +86 -0
  34. package/dist/lib/server/serve.js.map +1 -0
  35. package/dist/lib/testing/client.d.ts.map +1 -1
  36. package/dist/lib/testing/client.js +1 -0
  37. package/dist/lib/testing/client.js.map +1 -1
  38. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  39. package/dist/lib/testing/compliance/comply.js +48 -63
  40. package/dist/lib/testing/compliance/comply.js.map +1 -1
  41. package/dist/lib/testing/compliance/storyboard-tracks.d.ts +24 -0
  42. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -0
  43. package/dist/lib/testing/compliance/storyboard-tracks.js +157 -0
  44. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -0
  45. package/dist/lib/testing/compliance/types.d.ts +1 -1
  46. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  47. package/dist/lib/testing/index.d.ts +1 -0
  48. package/dist/lib/testing/index.d.ts.map +1 -1
  49. package/dist/lib/testing/index.js +23 -1
  50. package/dist/lib/testing/index.js.map +1 -1
  51. package/dist/lib/testing/orchestrator.d.ts +8 -0
  52. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  53. package/dist/lib/testing/orchestrator.js +8 -0
  54. package/dist/lib/testing/orchestrator.js.map +1 -1
  55. package/dist/lib/testing/storyboard/context.d.ts +34 -0
  56. package/dist/lib/testing/storyboard/context.d.ts.map +1 -0
  57. package/dist/lib/testing/storyboard/context.js +257 -0
  58. package/dist/lib/testing/storyboard/context.js.map +1 -0
  59. package/dist/lib/testing/storyboard/index.d.ts +15 -0
  60. package/dist/lib/testing/storyboard/index.d.ts.map +1 -0
  61. package/dist/lib/testing/storyboard/index.js +48 -0
  62. package/dist/lib/testing/storyboard/index.js.map +1 -0
  63. package/dist/lib/testing/storyboard/loader.d.ts +53 -0
  64. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -0
  65. package/dist/lib/testing/storyboard/loader.js +114 -0
  66. package/dist/lib/testing/storyboard/loader.js.map +1 -0
  67. package/dist/lib/testing/storyboard/path.d.ts +29 -0
  68. package/dist/lib/testing/storyboard/path.d.ts.map +1 -0
  69. package/dist/lib/testing/storyboard/path.js +121 -0
  70. package/dist/lib/testing/storyboard/path.js.map +1 -0
  71. package/dist/lib/testing/storyboard/request-builder.d.ts +28 -0
  72. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -0
  73. package/dist/lib/testing/storyboard/request-builder.js +410 -0
  74. package/dist/lib/testing/storyboard/request-builder.js.map +1 -0
  75. package/dist/lib/testing/storyboard/runner.d.ts +24 -0
  76. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -0
  77. package/dist/lib/testing/storyboard/runner.js +280 -0
  78. package/dist/lib/testing/storyboard/runner.js.map +1 -0
  79. package/dist/lib/testing/storyboard/task-map.d.ts +21 -0
  80. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -0
  81. package/dist/lib/testing/storyboard/task-map.js +84 -0
  82. package/dist/lib/testing/storyboard/task-map.js.map +1 -0
  83. package/dist/lib/testing/storyboard/types.d.ts +156 -0
  84. package/dist/lib/testing/storyboard/types.d.ts.map +1 -0
  85. package/dist/lib/testing/storyboard/types.js +10 -0
  86. package/dist/lib/testing/storyboard/types.js.map +1 -0
  87. package/dist/lib/testing/storyboard/validations.d.ts +17 -0
  88. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -0
  89. package/dist/lib/testing/storyboard/validations.js +166 -0
  90. package/dist/lib/testing/storyboard/validations.js.map +1 -0
  91. package/dist/lib/testing/types.d.ts +2 -0
  92. package/dist/lib/testing/types.d.ts.map +1 -1
  93. package/dist/lib/types/core.generated.d.ts +2 -2
  94. package/dist/lib/types/core.generated.d.ts.map +1 -1
  95. package/dist/lib/types/core.generated.js +1 -1
  96. package/dist/lib/types/schemas.generated.d.ts +193 -34
  97. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  98. package/dist/lib/types/schemas.generated.js +87 -5
  99. package/dist/lib/types/schemas.generated.js.map +1 -1
  100. package/dist/lib/types/tools.generated.d.ts +280 -3
  101. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  102. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  103. package/dist/lib/utils/response-schemas.js +34 -3
  104. package/dist/lib/utils/response-schemas.js.map +1 -1
  105. package/dist/lib/utils/validate-user-agent.d.ts +8 -0
  106. package/dist/lib/utils/validate-user-agent.d.ts.map +1 -0
  107. package/dist/lib/utils/validate-user-agent.js +15 -0
  108. package/dist/lib/utils/validate-user-agent.js.map +1 -0
  109. package/dist/lib/version.d.ts +6 -0
  110. package/dist/lib/version.d.ts.map +1 -1
  111. package/dist/lib/version.js +7 -1
  112. package/dist/lib/version.js.map +1 -1
  113. package/docs/README.md +42 -0
  114. package/docs/guides/BUILD-AN-AGENT.md +292 -0
  115. package/docs/llms.txt +634 -0
  116. package/examples/README.md +106 -0
  117. package/examples/adcp.config.json +30 -0
  118. package/examples/basic-a2a.ts +76 -0
  119. package/examples/basic-mcp.ts +50 -0
  120. package/examples/batch-preview-test.ts +266 -0
  121. package/examples/conversation-client.ts +291 -0
  122. package/examples/debug-preview-response.ts +73 -0
  123. package/examples/debug-preview-with-logging.ts +50 -0
  124. package/examples/easy-config-demo.ts +242 -0
  125. package/examples/env-config.ts +51 -0
  126. package/examples/error-compliant-server.ts +237 -0
  127. package/examples/generative-creative-demo.ts +205 -0
  128. package/examples/inspect-card-formats.ts +161 -0
  129. package/examples/logger-usage.ts +165 -0
  130. package/examples/oauth-cli-example.ts +154 -0
  131. package/examples/pr78-async-patterns-demo.ts +247 -0
  132. package/examples/signals-agent.ts +162 -0
  133. package/examples/simple-getting-started.ts +225 -0
  134. package/examples/simple-protocol-demo.ts +75 -0
  135. package/examples/test-helpers-demo.ts +239 -0
  136. package/examples/zod-validation-example.ts +126 -0
  137. package/package.json +12 -2
  138. package/skills/adcp/SKILL.md +13 -2
  139. package/storyboards/audience_sync.yaml +199 -0
  140. package/storyboards/behavioral_analysis.yaml +244 -0
  141. package/storyboards/brand_rights.yaml +131 -0
  142. package/storyboards/creative_ad_server.yaml +171 -0
  143. package/storyboards/creative_sales_agent.yaml +169 -0
  144. package/storyboards/creative_template.yaml +306 -0
  145. package/storyboards/deterministic_testing.yaml +925 -0
  146. package/storyboards/error_compliance.yaml +231 -0
  147. package/storyboards/governance_content_standards.yaml +213 -0
  148. package/storyboards/governance_property_lists.yaml +372 -0
  149. package/storyboards/media_buy_catalog_creative.yaml +457 -0
  150. package/storyboards/media_buy_governance_escalation.yaml +467 -0
  151. package/storyboards/media_buy_guaranteed_approval.yaml +396 -0
  152. package/storyboards/media_buy_non_guaranteed.yaml +288 -0
  153. package/storyboards/media_buy_proposal_mode.yaml +369 -0
  154. package/storyboards/media_buy_seller.yaml +560 -0
  155. package/storyboards/media_buy_state_machine.yaml +254 -0
  156. package/storyboards/schema.yaml +65 -0
  157. package/storyboards/schema_validation.yaml +166 -0
  158. package/storyboards/si_session.yaml +384 -0
  159. package/storyboards/signal_marketplace.yaml +283 -0
  160. package/storyboards/signal_owned.yaml +211 -0
package/AGENTS.md ADDED
@@ -0,0 +1,278 @@
1
+ # AI Coding Assistant Instructions for AdCP Client
2
+
3
+ This document contains essential guidelines for AI coding assistants (Claude, Copilot, etc.) working on the AdCP Client project.
4
+
5
+ ## Start Here
6
+
7
+ **Protocol overview** — Read `docs/llms.txt` for a single-file summary of AdCP: all 46 tools, key types, error codes, common flows, and test scenarios. Also available at https://adcontextprotocol.github.io/adcp-client/llms.txt
8
+
9
+ **Type reference** — Read `docs/TYPE-SUMMARY.md` for curated type signatures (AgentConfig, TaskResult, ConversationContext, and all tool request/response shapes).
10
+
11
+ **Do NOT read these files** — they are large generated files that waste context:
12
+ - `src/lib/types/tools.generated.ts` (~13,000 lines) — use TYPE-SUMMARY.md instead
13
+ - `src/lib/types/core.generated.ts` (~2,000 lines) — use TYPE-SUMMARY.md instead
14
+ - `src/lib/types/schemas.generated.ts` (~8,000 lines) — Zod runtime schemas, rarely needed directly
15
+ - `src/lib/agents/index.generated.ts` — generated Agent classes, use the client API instead
16
+
17
+ **Building a server-side agent?** — Read `docs/guides/BUILD-AN-AGENT.md` and the `storyboards/` directory for expected tool call sequences.
18
+
19
+ ## Project Overview
20
+
21
+ **@adcp/client** is the official TypeScript client library for the Ad Context Protocol (AdCP), documented at [docs.adcontextprotocol.org](https://docs.adcontextprotocol.org/docs/).
22
+
23
+ **Components:**
24
+
25
+ 1. **Library** (`src/lib/`) - NPM package for AdCP agent communication
26
+ 2. **CLI** (`bin/`) - Command-line tooling for testing agents
27
+
28
+ ## 🚨 CRITICAL REQUIREMENTS - MUST FOLLOW 🚨
29
+
30
+ ### 1. ALWAYS USE OFFICIAL PROTOCOL CLIENTS
31
+
32
+ - **A2A Protocol**: ALWAYS use the official `@a2a-js/sdk` client
33
+ - **MCP Protocol**: ALWAYS use the official `@modelcontextprotocol/sdk` client
34
+ - **NEVER** implement custom HTTP fallbacks or protocol implementations
35
+ - **NEVER** parse SSE responses manually
36
+ - **NEVER** make direct fetch() calls to agent endpoints
37
+ - If an official client fails to import, FIX THE IMPORT - don't create workarounds
38
+
39
+ ### 2. NEVER USE MOCK DATA
40
+
41
+ - **NEVER** inject mock products, formats, or any other fake data
42
+ - **NEVER** provide fallback data when agents return empty responses
43
+ - **ALWAYS** return exactly what the agents provide
44
+ - If an agent returns empty arrays or errors, show that to the user
45
+ - Real data only - no exceptions
46
+
47
+ ### 3. MCP CLIENT AUTHENTICATION - CRITICAL
48
+
49
+ The MCP SDK automatically handles initialization. Authentication must be provided via headers:
50
+
51
+ ```javascript
52
+ const transport = new StreamableHTTPClientTransport(url, {
53
+ requestInit: {
54
+ headers: {
55
+ 'x-adcp-auth': authToken,
56
+ },
57
+ },
58
+ });
59
+ await client.connect(transport); // This automatically calls initialize internally
60
+ ```
61
+
62
+ ## Critical Architecture Patterns
63
+
64
+ ### Protocol Abstraction Layer
65
+
66
+ The library supports both **A2A** and **MCP** protocols via unified interface:
67
+
68
+ ```typescript
69
+ import { ProtocolClient } from './src/lib/protocols';
70
+ // Routes to: callA2ATool() or callMCPTool() based on agent.protocol
71
+ ```
72
+
73
+ **🚨 CRITICAL: A2A Protocol Implementation Requirements**
74
+
75
+ The A2A protocol has specific implementation requirements that differ from MCP:
76
+
77
+ **1. Artifact Field Names**
78
+
79
+ A2A artifacts use `artifactId` per @a2a-js/sdk Artifact interface, NOT `name`:
80
+
81
+ ```typescript
82
+ // Correct (per @a2a-js/sdk)
83
+ if (!artifact.artifactId) {
84
+ warnings.push('A2A artifact missing artifactId field');
85
+ }
86
+
87
+ // Incorrect - 'name' doesn't exist in A2A SDK
88
+ if (!artifact.name) { ... }
89
+ ```
90
+
91
+ **2. Two Types of Webhooks - Do Not Confuse!**
92
+
93
+ **1. `push_notification_config` - For Async Task Status Updates**
94
+
95
+ Used for receiving task completion/progress notifications. Placement differs by protocol:
96
+
97
+ - **A2A Protocol**: Goes in `params.configuration.pushNotificationConfig` (camelCase)
98
+
99
+ ```typescript
100
+ await a2aClient.sendMessage({
101
+ message: { /* task content */ },
102
+ configuration: {
103
+ pushNotificationConfig: { // ← For async task status
104
+ url: webhookUrl,
105
+ token?: clientToken,
106
+ authentication: { schemes: ['HMAC-SHA256'], credentials: secret }
107
+ }
108
+ }
109
+ });
110
+ ```
111
+
112
+ - **MCP Protocol**: Goes in tool arguments as `push_notification_config` (snake_case)
113
+ ```typescript
114
+ await mcpClient.callTool('create_media_buy', {
115
+ buyer_ref: '...',
116
+ packages: [...],
117
+ push_notification_config: { // ← For async task status
118
+ url: webhookUrl,
119
+ token?: clientToken,
120
+ authentication: { schemes: ['HMAC-SHA256'], credentials: secret }
121
+ }
122
+ });
123
+ ```
124
+
125
+ **2. `reporting_webhook` - For Reporting Data Delivery**
126
+
127
+ Used for receiving periodic performance metrics. **Always stays in skill parameters** (both A2A and MCP):
128
+
129
+ ```typescript
130
+ // Both protocols - reporting_webhook in skill parameters
131
+ {
132
+ buyer_ref: '...',
133
+ packages: [...],
134
+ reporting_webhook: { // ← Stays in parameters for BOTH protocols
135
+ url: reportingUrl,
136
+ token?: clientToken,
137
+ authentication: { schemes: ['HMAC-SHA256'], credentials: secret },
138
+ reporting_frequency: 'daily', // Additional fields specific to reporting
139
+ requested_metrics: ['impressions', 'spend', 'clicks']
140
+ }
141
+ }
142
+ ```
143
+
144
+ Schema: https://adcontextprotocol.org/schemas/v1/core/push-notification-config.json
145
+
146
+ The `ProtocolClient.callTool()` method in `src/lib/protocols/index.ts` handles this routing automatically.
147
+
148
+ ### Async Operation Patterns (AdCP PR 78)
149
+
150
+ All operations follow 5 status patterns based on agent response:
151
+
152
+ - `completed` - Sync completion with result
153
+ - `working` - Long-running, poll with `tasks/get`
154
+ - `submitted` - Webhook delivery required
155
+ - `input_required` - Agent needs clarification via input handlers
156
+ - `deferred` - Client defers decision to human/external system
157
+
158
+ ```typescript
159
+ // Core flow in src/lib/core/TaskExecutor.ts
160
+ const result = await executor.executeTask(agent, 'get_products', params, inputHandler);
161
+ // Status determines next steps: polling, webhook wait, or input handling
162
+ ```
163
+
164
+ ### Conversation-Aware Input Handling
165
+
166
+ Agents may need clarifications during execution. Use input handlers:
167
+
168
+ ```typescript
169
+ const client = new AdCPClient(agent, {
170
+ handlers: {
171
+ onGetProductsStatusChange: (response, metadata) => {
172
+ // Fires for ALL status changes: sync completion, webhook delivery, etc.
173
+ },
174
+ },
175
+ });
176
+
177
+ // Input handler pattern for clarifications
178
+ const handler = async context => {
179
+ return context.inputRequest.field === 'budget' ? 50000 : context.deferToHuman();
180
+ };
181
+ ```
182
+
183
+ ## Build & Release Process
184
+
185
+ ### Changesets-Based Releases
186
+
187
+ **🚨 NEVER manually edit `package.json` version** - Use changesets:
188
+
189
+ ```bash
190
+ npm run changeset # Create changeset for changes
191
+ # Merge PR to main → Auto Release PR created
192
+ # Merge Release PR → Auto publish to npm
193
+ ```
194
+
195
+ **The correct separation:**
196
+
197
+ - `package.json` version = **Library version** (managed by changesets)
198
+ - `src/lib/version.ts` ADCP_VERSION = **AdCP schema version** (can differ from library version)
199
+
200
+ ### Schema Generation Workflow
201
+
202
+ Library types auto-generated from AdCP schemas:
203
+
204
+ ```bash
205
+ npm run sync-schemas # Download from protocol repo
206
+ npm run generate-types # Generate TypeScript types
207
+ npm run generate-zod-schemas # Generate runtime validation
208
+ ```
209
+
210
+ ## Testing Strategies
211
+
212
+ ### Protocol-Level Mocking
213
+
214
+ Test TaskExecutor patterns by mocking `ProtocolClient.callTool()`:
215
+
216
+ ```javascript
217
+ ProtocolClient.callTool = mock.fn(async (agent, taskName, params) => {
218
+ if (taskName === 'tasks/get') return { task: { status: 'working' } };
219
+ return { status: 'submitted' };
220
+ });
221
+ ```
222
+
223
+ ### Test Commands
224
+
225
+ ```bash
226
+ npm test # Unit tests
227
+ npm run test:protocols # Protocol compliance tests
228
+ npm run test:e2e # End-to-end against live server
229
+ npm run test:all # Full test suite
230
+ ```
231
+
232
+ ## File Organization
233
+
234
+ - `docs/llms.txt` - **Start here**: protocol overview, all tools, types, flows (generated)
235
+ - `docs/TYPE-SUMMARY.md` - Curated type reference (generated)
236
+ - `src/lib/core/` - Main client classes (AdCPClient, TaskExecutor)
237
+ - `src/lib/protocols/` - A2A/MCP protocol implementations
238
+ - `src/lib/types/` - Generated TypeScript types from schemas (prefer TYPE-SUMMARY.md)
239
+ - `storyboards/` - YAML definitions of tool call sequences for each agent type
240
+ - `test/lib/` - Library unit tests
241
+ - `test/e2e/` - Integration tests
242
+ - `examples/` - Usage examples and demos
243
+
244
+ ## Backwards Compatibility
245
+
246
+ **This is a published npm library. Callers on older versions must not break when we add new required fields.**
247
+
248
+ When adding a new required field to a request schema:
249
+
250
+ 1. **Infer it from existing fields** in `SingleAgentClient.normalizeRequestParams()` so callers that don't send it still work
251
+ 2. **Update all internal callers** (testing scenarios) to send the field explicitly
252
+ 3. **Add tests** verifying the inference works and that explicit values are preserved
253
+
254
+ Example: `buying_mode` was added as required on `get_products`. The client infers it from `brief` presence — callers that only sent `{ brief: '...' }` keep working.
255
+
256
+ The pattern:
257
+
258
+ - `normalizeRequestParams()` runs before validation, filling in derivable fields
259
+ - `validateRequest()` runs Zod schemas after normalization
260
+ - `adaptRequestForServerVersion()` handles v3→v2 downgrades for older servers
261
+
262
+ **Never add a required field without a backwards-compatible default or inference path.**
263
+
264
+ ## Common Gotchas
265
+
266
+ 1. **Protocol clients**: Always use official `@a2a-js/sdk` and `@modelcontextprotocol/sdk`
267
+ 2. **Mock data**: Never inject fallback data when agents return empty responses
268
+ 3. **Version management**: Let changesets handle package.json, edit ADCP_VERSION separately
269
+ 4. **Backwards compatibility**: New required schema fields need inference in `normalizeRequestParams()`
270
+
271
+ ## References
272
+
273
+ - [Protocol overview (llms.txt)](https://adcontextprotocol.github.io/adcp-client/llms.txt)
274
+ - [API Documentation](https://adcontextprotocol.github.io/adcp-client/api/index.html)
275
+ - [AdCP Specification](https://adcontextprotocol.org)
276
+ - [A2A SDK](https://github.com/a2a/a2a-js-sdk)
277
+ - [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk)
278
+ - [Changesets Documentation](https://github.com/changesets/changesets)
package/README.md CHANGED
@@ -9,9 +9,16 @@
9
9
 
10
10
  Official TypeScript/JavaScript client for the **Ad Context Protocol (AdCP)**. Build distributed advertising operations that work synchronously OR asynchronously with the same code.
11
11
 
12
+ ## For AI Agents
13
+
14
+ Start with [`docs/llms.txt`](./docs/llms.txt) — the full protocol spec in one file (tools, types, error codes, examples). Building a server? See [`docs/guides/BUILD-AN-AGENT.md`](./docs/guides/BUILD-AN-AGENT.md). For type signatures, use [`docs/TYPE-SUMMARY.md`](./docs/TYPE-SUMMARY.md). Skip `src/lib/types/*.generated.ts` — they're machine-generated and will burn context.
15
+
16
+ These docs are also available in `node_modules/@adcp/client/docs/` after install.
17
+
12
18
  ## The Core Concept
13
19
 
14
20
  AdCP operations are **distributed and asynchronous by default**. An agent might:
21
+
15
22
  - Complete your request **immediately** (synchronous)
16
23
  - Need time to process and **send results via webhook** (asynchronous)
17
24
  - Ask for **clarifications** before proceeding
@@ -31,44 +38,47 @@ npm install @adcp/client
31
38
  import { ADCPMultiAgentClient } from '@adcp/client';
32
39
 
33
40
  // Configure agents and handlers
34
- const client = new ADCPMultiAgentClient([
35
- {
36
- id: 'agent_x',
37
- agent_uri: 'https://agent-x.com',
38
- protocol: 'a2a'
39
- },
41
+ const client = new ADCPMultiAgentClient(
42
+ [
43
+ {
44
+ id: 'agent_x',
45
+ agent_uri: 'https://agent-x.com',
46
+ protocol: 'a2a',
47
+ },
48
+ {
49
+ id: 'agent_y',
50
+ agent_uri: 'https://agent-y.com/mcp/',
51
+ protocol: 'mcp',
52
+ },
53
+ ],
40
54
  {
41
- id: 'agent_y',
42
- agent_uri: 'https://agent-y.com/mcp/',
43
- protocol: 'mcp'
44
- }
45
- ], {
46
- // Webhook URL template (macros: {agent_id}, {task_type}, {operation_id})
47
- webhookUrlTemplate: 'https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}',
48
-
49
- // Activity callback - fires for ALL events (requests, responses, status changes, webhooks)
50
- onActivity: (activity) => {
51
- console.log(`[${activity.type}] ${activity.task_type} - ${activity.operation_id}`);
52
- // Log to monitoring, update UI, etc.
53
- },
54
-
55
- // Status change handlers - called for ALL status changes (completed, failed, input-required, working, etc)
56
- handlers: {
57
- onGetProductsStatusChange: (response, metadata) => {
58
- // Called for sync completion, async webhook, AND status changes
59
- console.log(`[${metadata.status}] Got products for ${metadata.operation_id}`);
60
-
61
- if (metadata.status === 'completed') {
62
- db.saveProducts(metadata.operation_id, response.products);
63
- } else if (metadata.status === 'failed') {
64
- db.markFailed(metadata.operation_id, metadata.message);
65
- } else if (metadata.status === 'input-required') {
66
- // Handle clarification needed
67
- console.log('Needs input:', metadata.message);
68
- }
69
- }
55
+ // Webhook URL template (macros: {agent_id}, {task_type}, {operation_id})
56
+ webhookUrlTemplate: 'https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}',
57
+
58
+ // Activity callback - fires for ALL events (requests, responses, status changes, webhooks)
59
+ onActivity: activity => {
60
+ console.log(`[${activity.type}] ${activity.task_type} - ${activity.operation_id}`);
61
+ // Log to monitoring, update UI, etc.
62
+ },
63
+
64
+ // Status change handlers - called for ALL status changes (completed, failed, input-required, working, etc)
65
+ handlers: {
66
+ onGetProductsStatusChange: (response, metadata) => {
67
+ // Called for sync completion, async webhook, AND status changes
68
+ console.log(`[${metadata.status}] Got products for ${metadata.operation_id}`);
69
+
70
+ if (metadata.status === 'completed') {
71
+ db.saveProducts(metadata.operation_id, response.products);
72
+ } else if (metadata.status === 'failed') {
73
+ db.markFailed(metadata.operation_id, metadata.message);
74
+ } else if (metadata.status === 'input-required') {
75
+ // Handle clarification needed
76
+ console.log('Needs input:', metadata.message);
77
+ }
78
+ },
79
+ },
70
80
  }
71
- });
81
+ );
72
82
 
73
83
  // Execute operation - library handles operation IDs, webhook URLs, context management
74
84
  const agent = client.agent('agent_x');
@@ -132,7 +142,7 @@ const client = new ADCPMultiAgentClient(agents, {
132
142
  webhookUrlTemplate: 'https://myapp.com/api/v1/adcp/{agent_id}?operation={operation_id}',
133
143
 
134
144
  // OR namespace to avoid conflicts
135
- webhookUrlTemplate: 'https://myapp.com/adcp-webhooks/{agent_id}/{task_type}/{operation_id}'
145
+ webhookUrlTemplate: 'https://myapp.com/adcp-webhooks/{agent_id}/{task_type}/{operation_id}',
136
146
  });
137
147
  ```
138
148
 
@@ -172,21 +182,22 @@ Get observability into everything happening:
172
182
 
173
183
  ```typescript
174
184
  const client = new ADCPMultiAgentClient(agents, {
175
- onActivity: (activity) => {
185
+ onActivity: activity => {
176
186
  console.log({
177
- type: activity.type, // 'protocol_request', 'webhook_received', etc.
187
+ type: activity.type, // 'protocol_request', 'webhook_received', etc.
178
188
  operation_id: activity.operation_id,
179
189
  agent_id: activity.agent_id,
180
- status: activity.status
190
+ status: activity.status,
181
191
  });
182
192
 
183
193
  // Stream to UI, save to database, send to monitoring
184
194
  eventStream.send(activity);
185
- }
195
+ },
186
196
  });
187
197
  ```
188
198
 
189
199
  Activity types:
200
+
190
201
  - `protocol_request` - Request sent to agent
191
202
  - `protocol_response` - Response received from agent
192
203
  - `status_change` - Task status changed
@@ -200,7 +211,7 @@ Activity types:
200
211
  // When creating a media buy, agent registers for delivery notifications
201
212
  const result = await agent.createMediaBuy({
202
213
  campaign_id: 'camp_123',
203
- budget: { amount: 10000, currency: 'USD' }
214
+ budget: { amount: 10000, currency: 'USD' },
204
215
  // Agent internally sets up recurring delivery_report notifications
205
216
  });
206
217
 
@@ -220,12 +231,13 @@ const client = new ADCPMultiAgentClient(agents, {
220
231
  if (metadata.notification_type === 'final') {
221
232
  db.markOperationComplete(metadata.operation_id);
222
233
  }
223
- }
224
- }
234
+ },
235
+ },
225
236
  });
226
237
  ```
227
238
 
228
239
  Notifications use the **same webhook URL pattern** as regular operations:
240
+
229
241
  ```
230
242
  POST https://myapp.com/webhook/media_buy_delivery/agent_x/delivery_report_agent_x_2025-10
231
243
  ```
@@ -257,7 +269,7 @@ handlers: {
257
269
  if (metadata.status === 'completed') {
258
270
  const buyId = (response as CreateMediaBuyResponse).media_buy_id; // Typed!
259
271
  }
260
- }
272
+ };
261
273
  }
262
274
  ```
263
275
 
@@ -266,11 +278,7 @@ handlers: {
266
278
  Building a server that receives AdCP tool calls? Import request types for handler signatures and Zod schemas for validation:
267
279
 
268
280
  ```typescript
269
- import {
270
- CreateMediaBuyRequest,
271
- CreateMediaBuyResponse,
272
- CreateMediaBuyRequestSchema,
273
- } from '@adcp/client';
281
+ import { CreateMediaBuyRequest, CreateMediaBuyResponse, CreateMediaBuyRequestSchema } from '@adcp/client';
274
282
 
275
283
  function handleCreateMediaBuy(rawParams: unknown): CreateMediaBuyResponse {
276
284
  const request: CreateMediaBuyRequest = CreateMediaBuyRequestSchema.parse(rawParams);
@@ -309,7 +317,7 @@ results.forEach((result, i) => {
309
317
 
310
318
  ```typescript
311
319
  const client = new ADCPMultiAgentClient(agents, {
312
- webhookSecret: process.env.WEBHOOK_SECRET
320
+ webhookSecret: process.env.WEBHOOK_SECRET,
313
321
  });
314
322
 
315
323
  // Signatures verified automatically on handleWebhook()
@@ -319,13 +327,15 @@ const client = new ADCPMultiAgentClient(agents, {
319
327
  ### Authentication
320
328
 
321
329
  ```typescript
322
- const agents = [{
323
- id: 'agent_x',
324
- name: 'Agent X',
325
- agent_uri: 'https://agent-x.com',
326
- protocol: 'a2a',
327
- auth_token: process.env.AGENT_X_TOKEN // ✅ Secure - load from env
328
- }];
330
+ const agents = [
331
+ {
332
+ id: 'agent_x',
333
+ name: 'Agent X',
334
+ agent_uri: 'https://agent-x.com',
335
+ protocol: 'a2a',
336
+ auth_token: process.env.AGENT_X_TOKEN, // ✅ Secure - load from env
337
+ },
338
+ ];
329
339
  ```
330
340
 
331
341
  ## Environment Configuration
@@ -356,6 +366,7 @@ const client = ADCPMultiAgentClient.fromEnv();
356
366
  All AdCP tools with full type safety:
357
367
 
358
368
  **Media Buy Lifecycle:**
369
+
359
370
  - `getProducts()` - Discover advertising products
360
371
  - `listCreativeFormats()` - Get supported creative formats
361
372
  - `createMediaBuy()` - Create new media buy
@@ -365,11 +376,13 @@ All AdCP tools with full type safety:
365
376
  - `getMediaBuyDelivery()` - Get delivery performance
366
377
 
367
378
  **Audience & Targeting:**
379
+
368
380
  - `getSignals()` - Get audience signals
369
381
  - `activateSignal()` - Activate audience signals
370
382
  - `providePerformanceFeedback()` - Send performance feedback
371
383
 
372
384
  **Protocol:**
385
+
373
386
  - `getAdcpCapabilities()` - Get agent capabilities (v3)
374
387
 
375
388
  ## Property Discovery (AdCP v2.2.0)
@@ -391,7 +404,7 @@ import { PropertyCrawler, getPropertyIndex } from '@adcp/client';
391
404
  const crawler = new PropertyCrawler();
392
405
  await crawler.crawlAgents([
393
406
  { agent_url: 'https://agent-x.com', protocol: 'a2a' },
394
- { agent_url: 'https://agent-y.com/mcp/', protocol: 'mcp' }
407
+ { agent_url: 'https://agent-y.com/mcp/', protocol: 'mcp' },
395
408
  ]);
396
409
 
397
410
  const index = getPropertyIndex();
@@ -418,7 +431,7 @@ const crawler = new PropertyCrawler();
418
431
  // Crawl agents - gets publisher_domains from each, then fetches adagents.json
419
432
  const result = await crawler.crawlAgents([
420
433
  { agent_url: 'https://sales.cnn.com' },
421
- { agent_url: 'https://sales.espn.com' }
434
+ { agent_url: 'https://sales.espn.com' },
422
435
  ]);
423
436
 
424
437
  console.log(`✅ ${result.successfulAgents} agents`);
@@ -441,6 +454,7 @@ Supports 18 identifier types: `domain`, `subdomain`, `ios_bundle`, `android_pack
441
454
  ### Use Case
442
455
 
443
456
  Build a registry service that:
457
+
444
458
  - Periodically crawls agents with `PropertyCrawler`
445
459
  - Persists discovered properties to a database
446
460
  - Exposes fast query APIs using the in-memory index patterns
@@ -600,6 +614,7 @@ npm start
600
614
  ```
601
615
 
602
616
  Features:
617
+
603
618
  - Configure multiple agents (test agents + your own)
604
619
  - Execute ONE operation across all agents
605
620
  - See live activity stream (protocol requests, webhooks, handlers)
@@ -609,11 +624,13 @@ Features:
609
624
  ## Examples
610
625
 
611
626
  ### Basic Operation
627
+
612
628
  ```typescript
613
629
  const result = await agent.getProducts({ brief: 'Coffee brands' });
614
630
  ```
615
631
 
616
632
  ### With Clarification Handler
633
+
617
634
  ```typescript
618
635
  const result = await agent.createMediaBuy(
619
636
  { buyer_ref: 'campaign-123', account_id: 'acct-456', packages: [...] },
@@ -628,6 +645,7 @@ const result = await agent.createMediaBuy(
628
645
  ```
629
646
 
630
647
  ### With Webhook for Long-Running Operations
648
+
631
649
  ```typescript
632
650
  const operationId = createOperationId();
633
651
 
@@ -636,7 +654,7 @@ const result = await agent.syncCreatives(
636
654
  null, // No clarification handler = webhook mode
637
655
  {
638
656
  contextId: operationId,
639
- webhookUrl: agent.getWebhookUrl('sync_creatives', operationId)
657
+ webhookUrl: agent.getWebhookUrl('sync_creatives', operationId),
640
658
  }
641
659
  );
642
660
 
@@ -644,6 +662,23 @@ const result = await agent.syncCreatives(
644
662
  // Handler fires when webhook received
645
663
  ```
646
664
 
665
+ ## Building an Agent (Server)
666
+
667
+ The examples above show client-side usage — calling existing agents. To build your own agent that serves AdCP tools:
668
+
669
+ ```typescript
670
+ import { createTaskCapableServer, taskToolResponse, GetSignalsRequestSchema } from '@adcp/client';
671
+
672
+ const server = createTaskCapableServer('My Signals Agent', '1.0.0');
673
+
674
+ server.tool('get_signals', 'Discover audience segments.', GetSignalsRequestSchema.shape, async args => {
675
+ const signals = queryYourDatabase(args.signal_spec);
676
+ return taskToolResponse({ signals, sandbox: true }, `Found ${signals.length} segment(s)`);
677
+ });
678
+ ```
679
+
680
+ See the [Build an Agent guide](docs/guides/BUILD-AN-AGENT.md) for the full walkthrough, and [`examples/signals-agent.ts`](examples/signals-agent.ts) for a complete runnable example.
681
+
647
682
  ## Contributing
648
683
 
649
684
  Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.