@adcp/client 0.3.0 → 0.4.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 +362 -166
- package/dist/lib/core/ADCPClient.d.ts +66 -0
- package/dist/lib/core/ADCPClient.d.ts.map +1 -1
- package/dist/lib/core/ADCPClient.js +160 -11
- package/dist/lib/core/ADCPClient.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts +16 -0
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +20 -0
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AsyncHandler.d.ts +137 -0
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -0
- package/dist/lib/core/AsyncHandler.js +133 -0
- package/dist/lib/core/AsyncHandler.js.map +1 -0
- package/dist/lib/core/TaskEventTypes.d.ts +220 -0
- package/dist/lib/core/TaskEventTypes.d.ts.map +1 -0
- package/dist/lib/core/TaskEventTypes.js +80 -0
- package/dist/lib/core/TaskEventTypes.js.map +1 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +6 -1
- package/dist/lib/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,9 +3,18 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/@adcp%2Fclient)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
|
-
[](https://your-org.github.io/adcp-client/)
|
|
7
6
|
|
|
8
|
-
Official TypeScript/JavaScript client for the **Ad Context Protocol (AdCP)**.
|
|
7
|
+
Official TypeScript/JavaScript client for the **Ad Context Protocol (AdCP)**. Build distributed advertising operations that work synchronously OR asynchronously with the same code.
|
|
8
|
+
|
|
9
|
+
## The Core Concept
|
|
10
|
+
|
|
11
|
+
AdCP operations are **distributed and asynchronous by default**. An agent might:
|
|
12
|
+
- Complete your request **immediately** (synchronous)
|
|
13
|
+
- Need time to process and **send results via webhook** (asynchronous)
|
|
14
|
+
- Ask for **clarifications** before proceeding
|
|
15
|
+
- Send periodic **status updates** as work progresses
|
|
16
|
+
|
|
17
|
+
**Your code stays the same.** You write handlers once, and they work for both sync completions and webhook deliveries.
|
|
9
18
|
|
|
10
19
|
## Installation
|
|
11
20
|
|
|
@@ -13,239 +22,426 @@ Official TypeScript/JavaScript client for the **Ad Context Protocol (AdCP)**. Se
|
|
|
13
22
|
npm install @adcp/client
|
|
14
23
|
```
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- **Node.js**: Version 18.0.0 or higher (specified in `.nvmrc`)
|
|
19
|
-
- **TypeScript**: 5.3.0+ for full type safety
|
|
20
|
-
- **Peer Dependencies**: `@a2a-js/sdk` and `@modelcontextprotocol/sdk`
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
25
|
+
## Quick Start: Distributed Operations
|
|
23
26
|
|
|
24
27
|
```typescript
|
|
25
28
|
import { ADCPMultiAgentClient } from '@adcp/client';
|
|
26
29
|
|
|
27
|
-
//
|
|
28
|
-
const client = new ADCPMultiAgentClient([
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
// Configure agents and handlers
|
|
31
|
+
const client = new ADCPMultiAgentClient([
|
|
32
|
+
{
|
|
33
|
+
id: 'agent_x',
|
|
34
|
+
agent_uri: 'https://agent-x.com',
|
|
35
|
+
protocol: 'a2a'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'agent_y',
|
|
39
|
+
agent_uri: 'https://agent-y.com/mcp/',
|
|
40
|
+
protocol: 'mcp'
|
|
41
|
+
}
|
|
42
|
+
], {
|
|
43
|
+
// Webhook URL template (macros: {agent_id}, {task_type}, {operation_id})
|
|
44
|
+
webhookUrlTemplate: 'https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}',
|
|
45
|
+
|
|
46
|
+
// Activity callback - fires for ALL events (requests, responses, status changes, webhooks)
|
|
47
|
+
onActivity: (activity) => {
|
|
48
|
+
console.log(`[${activity.type}] ${activity.task_type} - ${activity.operation_id}`);
|
|
49
|
+
// Log to monitoring, update UI, etc.
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Status change handlers - called for ALL status changes (completed, failed, needs_input, working, etc)
|
|
53
|
+
handlers: {
|
|
54
|
+
onGetProductsStatusChange: (response, metadata) => {
|
|
55
|
+
// Called for sync completion, async webhook, AND status changes
|
|
56
|
+
console.log(`[${metadata.status}] Got products for ${metadata.operation_id}`);
|
|
57
|
+
|
|
58
|
+
if (metadata.status === 'completed') {
|
|
59
|
+
db.saveProducts(metadata.operation_id, response.products);
|
|
60
|
+
} else if (metadata.status === 'failed') {
|
|
61
|
+
db.markFailed(metadata.operation_id, metadata.error);
|
|
62
|
+
} else if (metadata.status === 'needs_input') {
|
|
63
|
+
// Handle clarification needed
|
|
64
|
+
console.log('Needs input:', response.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
42
68
|
});
|
|
43
|
-
// result is TaskResult<GetProductsResponse> with known properties
|
|
44
69
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
// Execute operation - library handles operation IDs, webhook URLs, context management
|
|
71
|
+
const agent = client.agent('agent_x');
|
|
72
|
+
const result = await agent.getProducts({ brief: 'Coffee brands' });
|
|
73
|
+
|
|
74
|
+
// onActivity fired: protocol_request
|
|
75
|
+
// onActivity fired: protocol_response
|
|
76
|
+
|
|
77
|
+
// Check result
|
|
78
|
+
if (result.status === 'completed') {
|
|
79
|
+
// Agent completed synchronously!
|
|
80
|
+
console.log('✅ Sync completion:', result.data.products.length, 'products');
|
|
81
|
+
// onGetProductsStatusChange handler ALREADY fired with status='completed' ✓
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (result.status === 'submitted') {
|
|
85
|
+
// Agent will send webhook when complete
|
|
86
|
+
console.log('⏳ Async - webhook registered at:', result.submitted?.webhookUrl);
|
|
87
|
+
// onGetProductsStatusChange handler will fire when webhook arrives ✓
|
|
49
88
|
}
|
|
50
89
|
```
|
|
51
90
|
|
|
52
|
-
|
|
91
|
+
### Handling Clarifications (needs_input)
|
|
53
92
|
|
|
54
|
-
|
|
55
|
-
Set your agent configuration once and auto-discover everywhere:
|
|
93
|
+
When an agent needs more information, you can continue the conversation:
|
|
56
94
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
const result = await agent.getProducts({ brief: 'Coffee brands' });
|
|
97
|
+
|
|
98
|
+
if (result.status === 'needs_input') {
|
|
99
|
+
console.log('❓ Agent needs clarification:', result.needs_input?.message);
|
|
100
|
+
// onActivity fired: status_change (needs_input)
|
|
101
|
+
|
|
102
|
+
// Continue the conversation with the same agent
|
|
103
|
+
const refined = await agent.continueConversation('Only premium brands above $50');
|
|
104
|
+
// onActivity fired: protocol_request
|
|
105
|
+
// onActivity fired: protocol_response
|
|
106
|
+
|
|
107
|
+
if (refined.status === 'completed') {
|
|
108
|
+
console.log('✅ Got refined results:', refined.data.products.length);
|
|
109
|
+
// onGetProductsStatusChange handler fired ✓
|
|
110
|
+
}
|
|
111
|
+
}
|
|
61
112
|
```
|
|
62
113
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
114
|
+
## Webhook Pattern
|
|
115
|
+
|
|
116
|
+
All webhooks (task completions AND notifications) use one endpoint with flexible URL templates.
|
|
117
|
+
|
|
118
|
+
### Configure Your Webhook URL Structure
|
|
68
119
|
|
|
69
120
|
```typescript
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
121
|
+
const client = new ADCPMultiAgentClient(agents, {
|
|
122
|
+
// Path-based (default pattern)
|
|
123
|
+
webhookUrlTemplate: 'https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}',
|
|
73
124
|
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
125
|
+
// OR query string
|
|
126
|
+
webhookUrlTemplate: 'https://myapp.com/webhook?agent={agent_id}&op={operation_id}&type={task_type}',
|
|
127
|
+
|
|
128
|
+
// OR custom path
|
|
129
|
+
webhookUrlTemplate: 'https://myapp.com/api/v1/adcp/{agent_id}?operation={operation_id}',
|
|
130
|
+
|
|
131
|
+
// OR namespace to avoid conflicts
|
|
132
|
+
webhookUrlTemplate: 'https://myapp.com/adcp-webhooks/{agent_id}/{task_type}/{operation_id}'
|
|
133
|
+
});
|
|
78
134
|
```
|
|
79
135
|
|
|
80
|
-
###
|
|
136
|
+
### Single Webhook Endpoint
|
|
137
|
+
|
|
81
138
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
139
|
+
// Handles ALL webhooks (task completions and notifications)
|
|
140
|
+
app.post('/webhook/:task_type/:agent_id/:operation_id', async (req, res) => {
|
|
141
|
+
const { task_type, agent_id, operation_id } = req.params;
|
|
142
|
+
|
|
143
|
+
// Inject URL parameters into payload
|
|
144
|
+
const payload = {
|
|
145
|
+
...req.body,
|
|
146
|
+
task_type,
|
|
147
|
+
operation_id
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Route to agent client - handlers fire automatically
|
|
151
|
+
const agent = client.agent(agent_id);
|
|
152
|
+
await agent.handleWebhook(payload, req.headers['x-adcp-signature']);
|
|
153
|
+
|
|
154
|
+
res.json({ received: true });
|
|
155
|
+
});
|
|
156
|
+
```
|
|
86
157
|
|
|
87
|
-
|
|
88
|
-
const premium = client.agent('premium');
|
|
89
|
-
const budget = client.agent('budget');
|
|
158
|
+
### URL Generation is Automatic
|
|
90
159
|
|
|
91
|
-
|
|
92
|
-
const
|
|
160
|
+
```typescript
|
|
161
|
+
const operationId = createOperationId();
|
|
162
|
+
const webhookUrl = agent.getWebhookUrl('sync_creatives', operationId);
|
|
163
|
+
// Returns: https://myapp.com/webhook/sync_creatives/agent_x/op_123
|
|
164
|
+
// (or whatever your template generates)
|
|
93
165
|
```
|
|
94
166
|
|
|
95
|
-
##
|
|
167
|
+
## Activity Events
|
|
96
168
|
|
|
97
|
-
Get
|
|
169
|
+
Get observability into everything happening:
|
|
98
170
|
|
|
99
171
|
```typescript
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
172
|
+
const client = new ADCPMultiAgentClient(agents, {
|
|
173
|
+
onActivity: (activity) => {
|
|
174
|
+
console.log({
|
|
175
|
+
type: activity.type, // 'protocol_request', 'webhook_received', etc.
|
|
176
|
+
operation_id: activity.operation_id,
|
|
177
|
+
agent_id: activity.agent_id,
|
|
178
|
+
status: activity.status
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Stream to UI, save to database, send to monitoring
|
|
182
|
+
eventStream.send(activity);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
```
|
|
106
186
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
187
|
+
Activity types:
|
|
188
|
+
- `protocol_request` - Request sent to agent
|
|
189
|
+
- `protocol_response` - Response received from agent
|
|
190
|
+
- `webhook_received` - Webhook received from agent
|
|
191
|
+
- `handler_called` - Completion handler fired
|
|
110
192
|
|
|
111
|
-
|
|
112
|
-
const customResult = await agent.executeTask<MyCustomResponse>('custom_task', params);
|
|
193
|
+
## Notifications (Agent-Initiated)
|
|
113
194
|
|
|
114
|
-
|
|
115
|
-
const withHandler = await agent.getProducts(
|
|
116
|
-
{ brief: "Premium inventory" },
|
|
117
|
-
async (inputRequest) => ({ approve: true })
|
|
118
|
-
);
|
|
119
|
-
```
|
|
195
|
+
**Mental Model**: Notifications are operations that get set up when you create a media buy. The agent sends periodic updates (like delivery reports) to the webhook URL you configured during media buy creation.
|
|
120
196
|
|
|
121
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
// When creating a media buy, agent registers for delivery notifications
|
|
199
|
+
const result = await agent.createMediaBuy({
|
|
200
|
+
campaign_id: 'camp_123',
|
|
201
|
+
budget: { amount: 10000, currency: 'USD' }
|
|
202
|
+
// Agent internally sets up recurring delivery_report notifications
|
|
203
|
+
});
|
|
122
204
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- **🔒 Security-First** - No hardcoded tokens, SSRF protection, secure defaults
|
|
129
|
-
- **🧪 Protocol Compliance** - 100% A2A and MCP specification compliance
|
|
130
|
-
- **🌐 Cross-Platform** - Works with Node.js 18+ and modern JavaScript environments
|
|
205
|
+
// Later, agent sends notifications to your webhook
|
|
206
|
+
const client = new ADCPMultiAgentClient(agents, {
|
|
207
|
+
handlers: {
|
|
208
|
+
onMediaBuyDeliveryNotification: (notification, metadata) => {
|
|
209
|
+
console.log(`Report #${metadata.sequence_number}: ${metadata.notification_type}`);
|
|
131
210
|
|
|
132
|
-
|
|
211
|
+
// notification_type indicates progress:
|
|
212
|
+
// 'scheduled' → Progress update (like status: 'working')
|
|
213
|
+
// 'final' → Operation complete (like status: 'completed')
|
|
214
|
+
// 'delayed' → Still waiting (extended timeline)
|
|
133
215
|
|
|
134
|
-
|
|
216
|
+
db.saveDeliveryUpdate(metadata.operation_id, notification);
|
|
135
217
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
authToken: 'token',
|
|
140
|
-
inputHandler: async (request) => {
|
|
141
|
-
// Handle 'input-required' status
|
|
142
|
-
if (request.type === 'user_approval') {
|
|
143
|
-
const approved = await getUserApproval(request.data);
|
|
144
|
-
return { approved };
|
|
218
|
+
if (metadata.notification_type === 'final') {
|
|
219
|
+
db.markOperationComplete(metadata.operation_id);
|
|
220
|
+
}
|
|
145
221
|
}
|
|
146
|
-
// Defer for human review
|
|
147
|
-
return { defer: true };
|
|
148
222
|
}
|
|
149
223
|
});
|
|
224
|
+
```
|
|
150
225
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
226
|
+
Notifications use the **same webhook URL pattern** as regular operations:
|
|
227
|
+
```
|
|
228
|
+
POST https://myapp.com/webhook/media_buy_delivery/agent_x/delivery_report_agent_x_2025-10
|
|
229
|
+
```
|
|
155
230
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
231
|
+
The `operation_id` is lazily generated from agent + month: `delivery_report_{agent_id}_{YYYY-MM}`
|
|
232
|
+
|
|
233
|
+
All intermediate reports for the same agent + month → same `operation_id`
|
|
234
|
+
|
|
235
|
+
## Type Safety
|
|
236
|
+
|
|
237
|
+
Full TypeScript support with IntelliSense:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// All responses are fully typed
|
|
241
|
+
const result = await agent.getProducts(params);
|
|
242
|
+
// result: TaskResult<GetProductsResponse>
|
|
243
|
+
|
|
244
|
+
if (result.success) {
|
|
245
|
+
result.data.products.forEach(p => {
|
|
246
|
+
console.log(p.name, p.price); // Full autocomplete!
|
|
247
|
+
});
|
|
160
248
|
}
|
|
161
249
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
continuation,
|
|
170
|
-
{ approved: true, budget: 50000 }
|
|
171
|
-
);
|
|
250
|
+
// Handlers receive typed responses
|
|
251
|
+
handlers: {
|
|
252
|
+
onCreateMediaBuyComplete: (response, metadata) => {
|
|
253
|
+
// response: CreateMediaBuyResponse
|
|
254
|
+
// metadata: WebhookMetadata
|
|
255
|
+
const buyId = response.media_buy_id; // Typed!
|
|
256
|
+
}
|
|
172
257
|
}
|
|
173
258
|
```
|
|
174
259
|
|
|
175
|
-
## Multi-Agent
|
|
260
|
+
## Multi-Agent Operations
|
|
261
|
+
|
|
262
|
+
Execute across multiple agents simultaneously:
|
|
176
263
|
|
|
177
264
|
```typescript
|
|
178
|
-
|
|
265
|
+
const client = new ADCPMultiAgentClient([agentX, agentY, agentZ]);
|
|
179
266
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
agent_uri: 'https://agent2.example.com',
|
|
193
|
-
protocol: 'a2a'
|
|
267
|
+
// Parallel execution across all agents
|
|
268
|
+
const results = await client.getProducts({ brief: 'Coffee brands' });
|
|
269
|
+
// results: TaskResult<GetProductsResponse>[]
|
|
270
|
+
|
|
271
|
+
results.forEach((result, i) => {
|
|
272
|
+
const agentId = client.agentIds[i];
|
|
273
|
+
console.log(`${agentId}: ${result.status}`);
|
|
274
|
+
|
|
275
|
+
if (result.status === 'completed') {
|
|
276
|
+
console.log(` Sync: ${result.data.products?.length} products`);
|
|
277
|
+
} else if (result.status === 'submitted') {
|
|
278
|
+
console.log(` Async: webhook to ${result.submitted?.webhookUrl}`);
|
|
194
279
|
}
|
|
195
|
-
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Security
|
|
196
284
|
|
|
197
|
-
|
|
198
|
-
|
|
285
|
+
### Webhook Signature Verification
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const client = new ADCPMultiAgentClient(agents, {
|
|
289
|
+
webhookSecret: process.env.WEBHOOK_SECRET
|
|
290
|
+
});
|
|
199
291
|
|
|
200
|
-
//
|
|
201
|
-
|
|
292
|
+
// Signatures verified automatically on handleWebhook()
|
|
293
|
+
// Returns 401 if signature invalid
|
|
202
294
|
```
|
|
203
295
|
|
|
204
|
-
|
|
296
|
+
### Authentication
|
|
205
297
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
298
|
+
```typescript
|
|
299
|
+
const agents = [{
|
|
300
|
+
id: 'agent_x',
|
|
301
|
+
agent_uri: 'https://agent-x.com',
|
|
302
|
+
protocol: 'a2a',
|
|
303
|
+
auth_token_env: process.env.AGENT_X_TOKEN, // ✅ Secure
|
|
304
|
+
requiresAuth: true
|
|
305
|
+
}];
|
|
306
|
+
```
|
|
211
307
|
|
|
212
|
-
##
|
|
308
|
+
## Environment Configuration
|
|
213
309
|
|
|
214
310
|
```bash
|
|
215
|
-
#
|
|
216
|
-
|
|
217
|
-
|
|
311
|
+
# .env
|
|
312
|
+
WEBHOOK_URL_TEMPLATE="https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}"
|
|
313
|
+
WEBHOOK_SECRET="your-webhook-secret"
|
|
218
314
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
315
|
+
ADCP_AGENTS='[
|
|
316
|
+
{
|
|
317
|
+
"id": "agent_x",
|
|
318
|
+
"agent_uri": "https://agent-x.com",
|
|
319
|
+
"protocol": "a2a",
|
|
320
|
+
"auth_token_env": "AGENT_X_TOKEN"
|
|
321
|
+
}
|
|
322
|
+
]'
|
|
323
|
+
AGENT_X_TOKEN="actual-token-here"
|
|
223
324
|
```
|
|
224
325
|
|
|
225
|
-
|
|
326
|
+
```typescript
|
|
327
|
+
// Auto-discover from environment
|
|
328
|
+
const client = ADCPMultiAgentClient.fromEnv();
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Available Tools
|
|
332
|
+
|
|
333
|
+
All AdCP tools with full type safety:
|
|
334
|
+
|
|
335
|
+
**Media Buy Lifecycle:**
|
|
336
|
+
- `getProducts()` - Discover advertising products
|
|
337
|
+
- `listCreativeFormats()` - Get supported creative formats
|
|
338
|
+
- `createMediaBuy()` - Create new media buy
|
|
339
|
+
- `updateMediaBuy()` - Update existing media buy
|
|
340
|
+
- `syncCreatives()` - Upload/sync creative assets
|
|
341
|
+
- `listCreatives()` - List creative assets
|
|
342
|
+
- `getMediaBuyDelivery()` - Get delivery performance
|
|
343
|
+
|
|
344
|
+
**Audience & Targeting:**
|
|
345
|
+
- `listAuthorizedProperties()` - Get authorized properties
|
|
346
|
+
- `getSignals()` - Get audience signals
|
|
347
|
+
- `activateSignal()` - Activate audience signals
|
|
348
|
+
- `providePerformanceFeedback()` - Send performance feedback
|
|
349
|
+
|
|
350
|
+
## Database Schema
|
|
351
|
+
|
|
352
|
+
Simple unified event log for all operations:
|
|
353
|
+
|
|
354
|
+
```sql
|
|
355
|
+
CREATE TABLE webhook_events (
|
|
356
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
357
|
+
operation_id TEXT NOT NULL, -- Groups related events
|
|
358
|
+
agent_id TEXT NOT NULL,
|
|
359
|
+
task_type TEXT NOT NULL, -- 'sync_creatives', 'media_buy_delivery', etc.
|
|
360
|
+
status TEXT, -- For tasks: 'submitted', 'working', 'completed'
|
|
361
|
+
notification_type TEXT, -- For notifications: 'scheduled', 'final', 'delayed'
|
|
362
|
+
sequence_number INTEGER, -- For notifications: report sequence
|
|
363
|
+
payload JSONB NOT NULL,
|
|
364
|
+
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
CREATE INDEX idx_events_operation ON webhook_events(operation_id);
|
|
368
|
+
CREATE INDEX idx_events_agent ON webhook_events(agent_id);
|
|
369
|
+
CREATE INDEX idx_events_timestamp ON webhook_events(timestamp DESC);
|
|
370
|
+
|
|
371
|
+
-- Query all events for an operation
|
|
372
|
+
SELECT * FROM webhook_events
|
|
373
|
+
WHERE operation_id = 'op_123'
|
|
374
|
+
ORDER BY timestamp;
|
|
375
|
+
|
|
376
|
+
-- Get all delivery reports for agent + month
|
|
377
|
+
SELECT * FROM webhook_events
|
|
378
|
+
WHERE operation_id = 'delivery_report_agent_x_2025-10'
|
|
379
|
+
ORDER BY sequence_number;
|
|
380
|
+
```
|
|
226
381
|
|
|
227
|
-
|
|
382
|
+
## Testing
|
|
383
|
+
|
|
384
|
+
Try the live testing UI at `http://localhost:8080` when running the server:
|
|
228
385
|
|
|
229
386
|
```bash
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
387
|
+
npm start
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Features:
|
|
391
|
+
- Configure multiple agents (test agents + your own)
|
|
392
|
+
- Execute ONE operation across all agents
|
|
393
|
+
- See live activity stream (protocol requests, webhooks, handlers)
|
|
394
|
+
- View sync vs async completions side-by-side
|
|
395
|
+
- Test different scenarios (clarifications, errors, timeouts)
|
|
396
|
+
|
|
397
|
+
## Examples
|
|
398
|
+
|
|
399
|
+
### Basic Operation
|
|
400
|
+
```typescript
|
|
401
|
+
const result = await agent.getProducts({ brief: 'Coffee brands' });
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### With Clarification Handler
|
|
405
|
+
```typescript
|
|
406
|
+
const result = await agent.createMediaBuy(
|
|
407
|
+
{ brief: 'Coffee campaign' },
|
|
408
|
+
(context) => {
|
|
409
|
+
// Agent needs more info
|
|
410
|
+
if (context.inputRequest.field === 'budget') {
|
|
411
|
+
return 50000; // Provide programmatically
|
|
412
|
+
}
|
|
413
|
+
return context.deferToHuman(); // Or defer to human
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### With Webhook for Long-Running Operations
|
|
419
|
+
```typescript
|
|
420
|
+
const operationId = createOperationId();
|
|
421
|
+
|
|
422
|
+
const result = await agent.syncCreatives(
|
|
423
|
+
{ creatives: largeCreativeList },
|
|
424
|
+
null, // No clarification handler = webhook mode
|
|
425
|
+
{
|
|
426
|
+
contextId: operationId,
|
|
427
|
+
webhookUrl: agent.getWebhookUrl('sync_creatives', operationId)
|
|
428
|
+
}
|
|
429
|
+
);
|
|
235
430
|
|
|
236
|
-
|
|
431
|
+
// Result will be 'submitted', webhook arrives later
|
|
432
|
+
// Handler fires when webhook received
|
|
237
433
|
```
|
|
238
434
|
|
|
239
435
|
## Contributing
|
|
240
436
|
|
|
241
|
-
|
|
437
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
242
438
|
|
|
243
439
|
## License
|
|
244
440
|
|
|
245
|
-
MIT - see [LICENSE](
|
|
441
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
246
442
|
|
|
247
|
-
##
|
|
443
|
+
## Support
|
|
248
444
|
|
|
249
|
-
- [
|
|
250
|
-
- [
|
|
251
|
-
- [
|
|
445
|
+
- **Documentation**: [GitHub Pages](https://your-org.github.io/adcp-client/)
|
|
446
|
+
- **Issues**: [GitHub Issues](https://github.com/your-org/adcp-client/issues)
|
|
447
|
+
- **Protocol Spec**: [AdCP Specification](https://github.com/adcontextprotocol/adcp)
|