@adcp/client 3.5.2 โ 3.6.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/bin/adcp.js +267 -31
- package/package.json +1 -1
package/bin/adcp.js
CHANGED
|
@@ -19,6 +19,57 @@ const { readFileSync } = require('fs');
|
|
|
19
19
|
const { AsyncWebhookHandler } = require('./adcp-async-handler.js');
|
|
20
20
|
const { getAgent, listAgents, isAlias, interactiveSetup, removeAgent, getConfigPath } = require('./adcp-config.js');
|
|
21
21
|
|
|
22
|
+
// Test scenarios available
|
|
23
|
+
const TEST_SCENARIOS = [
|
|
24
|
+
'health_check',
|
|
25
|
+
'discovery',
|
|
26
|
+
'create_media_buy',
|
|
27
|
+
'full_sales_flow',
|
|
28
|
+
'creative_sync',
|
|
29
|
+
'creative_inline',
|
|
30
|
+
'creative_flow',
|
|
31
|
+
'signals_flow',
|
|
32
|
+
'error_handling',
|
|
33
|
+
'validation',
|
|
34
|
+
'pricing_edge_cases',
|
|
35
|
+
'temporal_validation',
|
|
36
|
+
'behavior_analysis',
|
|
37
|
+
'response_consistency',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// Built-in test agent aliases (shared between main CLI and test command)
|
|
41
|
+
// Note: These tokens are intentionally public for the AdCP test infrastructure.
|
|
42
|
+
// They provide rate-limited access to test agents for SDK development and examples.
|
|
43
|
+
const BUILT_IN_AGENTS = {
|
|
44
|
+
test: {
|
|
45
|
+
url: 'https://test-agent.adcontextprotocol.org/mcp/',
|
|
46
|
+
protocol: 'mcp',
|
|
47
|
+
auth_token: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ',
|
|
48
|
+
description: 'AdCP public test agent (MCP, with auth)',
|
|
49
|
+
},
|
|
50
|
+
'test-a2a': {
|
|
51
|
+
url: 'https://test-agent.adcontextprotocol.org',
|
|
52
|
+
protocol: 'a2a',
|
|
53
|
+
auth_token: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ',
|
|
54
|
+
description: 'AdCP public test agent (A2A, with auth)',
|
|
55
|
+
},
|
|
56
|
+
'test-no-auth': {
|
|
57
|
+
url: 'https://test-agent.adcontextprotocol.org/mcp/',
|
|
58
|
+
protocol: 'mcp',
|
|
59
|
+
description: 'AdCP public test agent (MCP, no auth - demonstrates errors)',
|
|
60
|
+
},
|
|
61
|
+
'test-a2a-no-auth': {
|
|
62
|
+
url: 'https://test-agent.adcontextprotocol.org',
|
|
63
|
+
protocol: 'a2a',
|
|
64
|
+
description: 'AdCP public test agent (A2A, no auth - demonstrates errors)',
|
|
65
|
+
},
|
|
66
|
+
creative: {
|
|
67
|
+
url: 'https://creative.adcontextprotocol.org/mcp',
|
|
68
|
+
protocol: 'mcp',
|
|
69
|
+
description: 'Official AdCP creative agent (MCP only)',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
22
73
|
/**
|
|
23
74
|
* Extract human-readable protocol message from conversation
|
|
24
75
|
*/
|
|
@@ -97,6 +148,201 @@ async function displayAgentInfo(agentConfig, jsonOutput) {
|
|
|
97
148
|
}
|
|
98
149
|
}
|
|
99
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Handle the 'test' subcommand for running agent test scenarios
|
|
153
|
+
*/
|
|
154
|
+
async function handleTestCommand(args) {
|
|
155
|
+
// Handle --list-scenarios
|
|
156
|
+
if (args.includes('--list-scenarios') || args.length === 0) {
|
|
157
|
+
console.log('\n๐ Available Test Scenarios:\n');
|
|
158
|
+
const descriptions = {
|
|
159
|
+
health_check: 'Basic connectivity check - verify agent responds',
|
|
160
|
+
discovery: 'Test get_products, list_creative_formats, list_authorized_properties',
|
|
161
|
+
create_media_buy: 'Discovery + create a test media buy (dry-run by default)',
|
|
162
|
+
full_sales_flow: 'Full lifecycle: discovery โ create โ update โ delivery',
|
|
163
|
+
creative_sync: 'Test sync_creatives flow',
|
|
164
|
+
creative_inline: 'Test inline creatives in create_media_buy',
|
|
165
|
+
creative_flow: 'Creative agent: list_formats โ build โ preview',
|
|
166
|
+
signals_flow: 'Signals agent: get_signals โ activate',
|
|
167
|
+
error_handling: 'Verify agent returns proper error responses',
|
|
168
|
+
validation: 'Test schema validation (invalid inputs should be rejected)',
|
|
169
|
+
pricing_edge_cases: 'Test auction vs fixed pricing, min spend, bid_price',
|
|
170
|
+
temporal_validation: 'Test date/time ordering and format validation',
|
|
171
|
+
behavior_analysis: 'Analyze agent behavior: auth, brief relevance, filtering',
|
|
172
|
+
response_consistency: 'Check for schema errors, pagination bugs, data mismatches',
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
for (const scenario of TEST_SCENARIOS) {
|
|
176
|
+
console.log(` ${scenario}`);
|
|
177
|
+
if (descriptions[scenario]) {
|
|
178
|
+
console.log(` ${descriptions[scenario]}`);
|
|
179
|
+
}
|
|
180
|
+
console.log('');
|
|
181
|
+
}
|
|
182
|
+
console.log('Usage: adcp test <agent> [scenario] [options]\n');
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Parse options with bounds checking
|
|
187
|
+
const authIndex = args.indexOf('--auth');
|
|
188
|
+
let authToken = process.env.ADCP_AUTH_TOKEN;
|
|
189
|
+
if (authIndex !== -1) {
|
|
190
|
+
if (authIndex + 1 >= args.length || args[authIndex + 1].startsWith('--')) {
|
|
191
|
+
console.error('ERROR: --auth requires a token value\n');
|
|
192
|
+
process.exit(2);
|
|
193
|
+
}
|
|
194
|
+
authToken = args[authIndex + 1];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const protocolIndex = args.indexOf('--protocol');
|
|
198
|
+
let protocolFlag = null;
|
|
199
|
+
if (protocolIndex !== -1) {
|
|
200
|
+
if (protocolIndex + 1 >= args.length || args[protocolIndex + 1].startsWith('--')) {
|
|
201
|
+
console.error('ERROR: --protocol requires a value (mcp or a2a)\n');
|
|
202
|
+
process.exit(2);
|
|
203
|
+
}
|
|
204
|
+
protocolFlag = args[protocolIndex + 1];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const briefIndex = args.indexOf('--brief');
|
|
208
|
+
let brief;
|
|
209
|
+
if (briefIndex !== -1) {
|
|
210
|
+
if (briefIndex + 1 >= args.length || args[briefIndex + 1].startsWith('--')) {
|
|
211
|
+
console.error('ERROR: --brief requires a value\n');
|
|
212
|
+
process.exit(2);
|
|
213
|
+
}
|
|
214
|
+
brief = args[briefIndex + 1];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const jsonOutput = args.includes('--json');
|
|
218
|
+
const debug = args.includes('--debug') || process.env.ADCP_DEBUG === 'true';
|
|
219
|
+
const dryRun = !args.includes('--no-dry-run');
|
|
220
|
+
|
|
221
|
+
// Filter out flag arguments to find positional arguments
|
|
222
|
+
const positionalArgs = args.filter(
|
|
223
|
+
arg => !arg.startsWith('--') && arg !== authToken && arg !== protocolFlag && arg !== brief
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
if (positionalArgs.length === 0) {
|
|
227
|
+
console.error('ERROR: test command requires an agent alias or URL\n');
|
|
228
|
+
console.error('Usage: adcp test <agent> [scenario] [options]');
|
|
229
|
+
console.error(' adcp test --list-scenarios\n');
|
|
230
|
+
process.exit(2);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const agentArg = positionalArgs[0];
|
|
234
|
+
const scenario = positionalArgs[1] || 'discovery';
|
|
235
|
+
|
|
236
|
+
// Validate scenario
|
|
237
|
+
if (!TEST_SCENARIOS.includes(scenario)) {
|
|
238
|
+
console.error(`ERROR: Unknown scenario '${scenario}'\n`);
|
|
239
|
+
console.error('Available scenarios:');
|
|
240
|
+
TEST_SCENARIOS.forEach(s => console.error(` - ${s}`));
|
|
241
|
+
console.error('\nUse: adcp test --list-scenarios for descriptions\n');
|
|
242
|
+
process.exit(2);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Validate protocol flag if provided
|
|
246
|
+
if (protocolFlag && protocolFlag !== 'mcp' && protocolFlag !== 'a2a') {
|
|
247
|
+
console.error(`ERROR: Invalid protocol '${protocolFlag}'. Must be 'mcp' or 'a2a'\n`);
|
|
248
|
+
process.exit(2);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let agentUrl;
|
|
252
|
+
let protocol = protocolFlag;
|
|
253
|
+
let finalAuthToken = authToken;
|
|
254
|
+
|
|
255
|
+
// Resolve agent
|
|
256
|
+
if (BUILT_IN_AGENTS[agentArg]) {
|
|
257
|
+
const builtIn = BUILT_IN_AGENTS[agentArg];
|
|
258
|
+
agentUrl = builtIn.url;
|
|
259
|
+
protocol = protocol || builtIn.protocol;
|
|
260
|
+
finalAuthToken = finalAuthToken || builtIn.auth_token;
|
|
261
|
+
} else if (isAlias(agentArg)) {
|
|
262
|
+
const savedAgent = getAgent(agentArg);
|
|
263
|
+
agentUrl = savedAgent.url;
|
|
264
|
+
protocol = protocol || savedAgent.protocol;
|
|
265
|
+
finalAuthToken = finalAuthToken || savedAgent.auth_token;
|
|
266
|
+
} else if (agentArg.startsWith('http://') || agentArg.startsWith('https://')) {
|
|
267
|
+
agentUrl = agentArg;
|
|
268
|
+
} else {
|
|
269
|
+
console.error(`ERROR: '${agentArg}' is not a valid agent alias or URL\n`);
|
|
270
|
+
console.error('Built-in aliases: test, test-a2a, creative');
|
|
271
|
+
console.error(`Saved aliases: ${Object.keys(listAgents()).join(', ') || 'none'}\n`);
|
|
272
|
+
process.exit(2);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Auto-detect protocol if not specified
|
|
276
|
+
if (!protocol) {
|
|
277
|
+
if (!jsonOutput) {
|
|
278
|
+
console.error('๐ Auto-detecting protocol...');
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
protocol = await detectProtocol(agentUrl);
|
|
282
|
+
if (!jsonOutput) {
|
|
283
|
+
console.error(`โ Detected protocol: ${protocol.toUpperCase()}\n`);
|
|
284
|
+
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error(`ERROR: Failed to detect protocol: ${error.message}\n`);
|
|
287
|
+
console.error('Please specify protocol: --protocol mcp or --protocol a2a\n');
|
|
288
|
+
process.exit(2);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Build test options
|
|
293
|
+
const testOptions = {
|
|
294
|
+
protocol,
|
|
295
|
+
dry_run: dryRun,
|
|
296
|
+
brief,
|
|
297
|
+
...(finalAuthToken && { auth: { type: 'bearer', token: finalAuthToken } }),
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
if (!jsonOutput) {
|
|
301
|
+
console.log(`\n๐งช Running '${scenario}' tests against ${agentUrl}`);
|
|
302
|
+
console.log(` Protocol: ${protocol.toUpperCase()}`);
|
|
303
|
+
console.log(` Dry Run: ${dryRun ? 'Yes (safe mode)' : 'No (real operations)'}`);
|
|
304
|
+
console.log(` Auth: ${finalAuthToken ? 'configured' : 'none'}\n`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Import and run tests
|
|
308
|
+
try {
|
|
309
|
+
const {
|
|
310
|
+
testAgent: runAgentTests,
|
|
311
|
+
formatTestResults,
|
|
312
|
+
formatTestResultsJSON,
|
|
313
|
+
} = await import('../dist/lib/testing/agent-tester.js');
|
|
314
|
+
|
|
315
|
+
// Silence default logger for cleaner output
|
|
316
|
+
const { setAgentTesterLogger } = await import('../dist/lib/testing/client.js');
|
|
317
|
+
if (!debug) {
|
|
318
|
+
setAgentTesterLogger({
|
|
319
|
+
info: () => {},
|
|
320
|
+
error: () => {},
|
|
321
|
+
warn: () => {},
|
|
322
|
+
debug: () => {},
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const result = await runAgentTests(agentUrl, scenario, testOptions);
|
|
327
|
+
|
|
328
|
+
if (jsonOutput) {
|
|
329
|
+
console.log(formatTestResultsJSON(result));
|
|
330
|
+
} else {
|
|
331
|
+
console.log(formatTestResults(result));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Exit with appropriate code
|
|
335
|
+
process.exit(result.overall_passed ? 0 : 3);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.error(`\nโ Test execution failed: ${error.message}`);
|
|
338
|
+
if (debug) {
|
|
339
|
+
console.error('\nStack trace:');
|
|
340
|
+
console.error(error.stack);
|
|
341
|
+
}
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
100
346
|
function printUsage() {
|
|
101
347
|
console.log(`
|
|
102
348
|
AdCP CLI Tool - Direct Agent Communication
|
|
@@ -137,6 +383,13 @@ AGENT MANAGEMENT:
|
|
|
137
383
|
--remove-agent <alias> Remove saved agent configuration
|
|
138
384
|
--show-config Show config file location
|
|
139
385
|
|
|
386
|
+
AGENT TESTING:
|
|
387
|
+
test <agent> [scenario] Run test scenarios against an agent
|
|
388
|
+
Scenarios: discovery, health_check, create_media_buy,
|
|
389
|
+
full_sales_flow, error_handling, validation, and more
|
|
390
|
+
Default scenario: discovery
|
|
391
|
+
test --list-scenarios List all available test scenarios
|
|
392
|
+
|
|
140
393
|
EXAMPLES:
|
|
141
394
|
# Use built-in test agent (zero config!)
|
|
142
395
|
npx @adcp/client test
|
|
@@ -185,6 +438,14 @@ EXAMPLES:
|
|
|
185
438
|
# JSON output for scripting
|
|
186
439
|
adcp myagent get_products '{"brief":"travel"}' --json | jq '.products[0]'
|
|
187
440
|
|
|
441
|
+
# Run agent tests
|
|
442
|
+
adcp test test # Test built-in test agent with discovery scenario
|
|
443
|
+
adcp test test discovery # Explicit discovery scenario
|
|
444
|
+
adcp test test full_sales_flow # Full media buy lifecycle test
|
|
445
|
+
adcp test https://my-agent.com discovery --auth $TOKEN
|
|
446
|
+
adcp test myagent error_handling --json # JSON output for CI
|
|
447
|
+
adcp test --list-scenarios # Show all available scenarios
|
|
448
|
+
|
|
188
449
|
ENVIRONMENT VARIABLES:
|
|
189
450
|
ADCP_AUTH_TOKEN Default authentication token (overridden by --auth)
|
|
190
451
|
ADCP_DEBUG Enable debug mode (set to 'true')
|
|
@@ -307,6 +568,12 @@ async function main() {
|
|
|
307
568
|
process.exit(0);
|
|
308
569
|
}
|
|
309
570
|
|
|
571
|
+
// Handle test command (handleTestCommand calls process.exit internally)
|
|
572
|
+
if (args[0] === 'test') {
|
|
573
|
+
await handleTestCommand(args.slice(1));
|
|
574
|
+
return; // handleTestCommand exits, but return for clarity
|
|
575
|
+
}
|
|
576
|
+
|
|
310
577
|
// Parse arguments
|
|
311
578
|
if (args.length < 1) {
|
|
312
579
|
console.error('ERROR: Missing required arguments\n');
|
|
@@ -351,37 +618,6 @@ async function main() {
|
|
|
351
618
|
|
|
352
619
|
const firstArg = positionalArgs[0];
|
|
353
620
|
|
|
354
|
-
// Built-in test helper aliases
|
|
355
|
-
const BUILT_IN_AGENTS = {
|
|
356
|
-
test: {
|
|
357
|
-
url: 'https://test-agent.adcontextprotocol.org/mcp/',
|
|
358
|
-
protocol: 'mcp',
|
|
359
|
-
auth_token: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ',
|
|
360
|
-
description: 'AdCP public test agent (MCP, with auth)',
|
|
361
|
-
},
|
|
362
|
-
'test-a2a': {
|
|
363
|
-
url: 'https://test-agent.adcontextprotocol.org',
|
|
364
|
-
protocol: 'a2a',
|
|
365
|
-
auth_token: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ',
|
|
366
|
-
description: 'AdCP public test agent (A2A, with auth)',
|
|
367
|
-
},
|
|
368
|
-
'test-no-auth': {
|
|
369
|
-
url: 'https://test-agent.adcontextprotocol.org/mcp/',
|
|
370
|
-
protocol: 'mcp',
|
|
371
|
-
description: 'AdCP public test agent (MCP, no auth - demonstrates auth errors)',
|
|
372
|
-
},
|
|
373
|
-
'test-a2a-no-auth': {
|
|
374
|
-
url: 'https://test-agent.adcontextprotocol.org',
|
|
375
|
-
protocol: 'a2a',
|
|
376
|
-
description: 'AdCP public test agent (A2A, no auth - demonstrates auth errors)',
|
|
377
|
-
},
|
|
378
|
-
creative: {
|
|
379
|
-
url: 'https://creative.adcontextprotocol.org/mcp',
|
|
380
|
-
protocol: 'mcp',
|
|
381
|
-
description: 'Official AdCP creative agent (MCP only)',
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
|
|
385
621
|
// Check if first arg is a built-in alias or saved alias
|
|
386
622
|
if (BUILT_IN_AGENTS[firstArg]) {
|
|
387
623
|
// Built-in test helper mode
|
package/package.json
CHANGED