@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.
Files changed (2) hide show
  1. package/bin/adcp.js +267 -31
  2. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/client",
3
- "version": "3.5.2",
3
+ "version": "3.6.0",
4
4
  "description": "AdCP client library with protocol support for MCP and A2A, includes testing framework",
5
5
  "main": "dist/lib/index.js",
6
6
  "types": "dist/lib/index.d.ts",