From c228f9c2180562e56ee50ac1d824fa5270538932 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 25 Oct 2025 13:55:32 -0400 Subject: [PATCH] fix: improve --save-auth UX with better error messages and auth handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problem 1: URL without alias causes hang** When running `adcp --save-auth https://url`, the CLI treated the URL as the alias and entered interactive mode, asking for inputs with hidden password prompts that appeared to hang. **Problem 2: Auth token not requested in non-interactive mode** When providing URL on command line, auth token was silently skipped with no way to provide it non-interactively. **Changes:** - Detect when URL is provided without alias and show clear error message - Add `--auth ` flag for non-interactive mode with authentication - Add `--no-auth` flag for non-interactive mode without authentication - Interactive mode now always prompts for auth token (can be left blank) - Improve password prompt visibility by printing prompt before hidden input - Update help text with clearer examples and usage patterns **New Behavior:** - `adcp --save-auth alias https://url --auth token` → Non-interactive with auth - `adcp --save-auth alias https://url --no-auth` → Non-interactive without auth - `adcp --save-auth alias https://url` → Interactive (asks for protocol & auth) - `adcp --save-auth alias` → Interactive (asks for URL, protocol & auth) šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- bin/adcp-config.js | 13 +++++--- bin/adcp.js | 77 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/bin/adcp-config.js b/bin/adcp-config.js index 86b25b6c0..48e9865c3 100644 --- a/bin/adcp-config.js +++ b/bin/adcp-config.js @@ -163,13 +163,15 @@ async function promptSecure(prompt, hidden = true) { * @param {string} protocol Protocol (optional) * @param {string} authToken Auth token (optional) * @param {boolean} nonInteractive Skip prompts if all required args provided + * @param {boolean} noAuth Explicitly skip auth (--no-auth flag) */ -async function interactiveSetup(alias, url = null, protocol = null, authToken = null, nonInteractive = false) { - // Non-interactive mode: if URL is provided, just save it +async function interactiveSetup(alias, url = null, protocol = null, authToken = null, nonInteractive = false, noAuth = false) { + // Non-interactive mode: save immediately without prompts if (nonInteractive && url) { const agentConfig = { url }; if (protocol) agentConfig.protocol = protocol; if (authToken) agentConfig.auth_token = authToken; + // noAuth flag means explicitly don't save auth saveAgent(alias, agentConfig); console.log(`\nāœ… Agent '${alias}' saved to ${CONFIG_FILE}`); @@ -190,9 +192,10 @@ async function interactiveSetup(alias, url = null, protocol = null, authToken = protocol = protocolInput || null; } - // Get auth token if not provided - if (!authToken) { - authToken = await promptSecure('Auth token (leave blank if not needed): ', true); + // Get auth token if not provided and not explicitly disabled + if (!authToken && !noAuth) { + process.stdout.write('Auth token (leave blank if not needed): '); + authToken = await promptSecure('', true); authToken = authToken || null; } diff --git a/bin/adcp.js b/bin/adcp.js index 0133fd3f1..33b9cec93 100755 --- a/bin/adcp.js +++ b/bin/adcp.js @@ -130,18 +130,26 @@ OPTIONS: --debug Show debug information AGENT MANAGEMENT: - --save-auth [url] Save agent configuration interactively + --save-auth [url] [protocol] [--auth token | --no-auth] + Save agent configuration with an alias name + Requires --auth or --no-auth for non-interactive mode --list-agents List all saved agents --remove-agent Remove saved agent configuration --show-config Show config file location EXAMPLES: - # Save an agent for easy access - adcp --save-auth test https://test-agent.adcontextprotocol.org + # Non-interactive: save with auth token + adcp --save-auth myagent https://test-agent.adcontextprotocol.org --auth your_token + + # Non-interactive: save without auth + adcp --save-auth myagent https://test-agent.adcontextprotocol.org --no-auth + + # Interactive setup (prompts for URL, protocol, and auth) + adcp --save-auth myagent # Use saved agent alias (auto-detect protocol) - adcp test - adcp test get_products '{"brief":"travel"}' + adcp myagent + adcp myagent get_products '{"brief":"travel"}' # List saved agents adcp --list-agents @@ -151,20 +159,20 @@ EXAMPLES: # Force specific protocol adcp https://agent.example.com get_products '{"brief":"coffee"}' --protocol mcp - adcp test list_authorized_properties --protocol a2a + adcp myagent list_authorized_properties --protocol a2a # Override saved auth token - adcp test get_products '{"brief":"..."}' --auth different-token + adcp myagent get_products '{"brief":"..."}' --auth different-token # Wait for async response (requires ngrok) - adcp test create_media_buy @payload.json --wait + adcp myagent create_media_buy @payload.json --wait # From file or stdin - adcp test create_media_buy @payload.json - echo '{"brief":"travel"}' | adcp test get_products - + adcp myagent create_media_buy @payload.json + echo '{"brief":"travel"}' | adcp myagent get_products - # JSON output for scripting - adcp test get_products '{"brief":"travel"}' --json | jq '.products[0]' + adcp myagent get_products '{"brief":"travel"}' --json | jq '.products[0]' ENVIRONMENT VARIABLES: ADCP_AUTH_TOKEN Default authentication token (overridden by --auth) @@ -192,19 +200,52 @@ async function main() { // Handle agent management commands if (args[0] === '--save-auth') { - const alias = args[1]; - const url = args[2] || null; - const protocol = args[3] || null; + // Parse flags first + const authFlagIndex = args.indexOf('--auth'); + const noAuthFlag = args.includes('--no-auth'); + const providedAuthToken = authFlagIndex !== -1 ? args[authFlagIndex + 1] : null; + + // Filter out flags to get positional args + const saveAuthPositional = args.slice(1).filter(arg => + arg !== '--auth' && + arg !== '--no-auth' && + arg !== providedAuthToken + ); + + let alias = saveAuthPositional[0]; + let url = saveAuthPositional[1] || null; + const protocol = saveAuthPositional[2] || null; if (!alias) { console.error('ERROR: --save-auth requires an alias\n'); - console.error('Usage: adcp --save-auth [url] [protocol]\n'); + console.error('Usage: adcp --save-auth [url] [protocol] [--auth token | --no-auth]\n'); + console.error('Example: adcp --save-auth myagent https://agent.example.com --auth your_token\n'); + process.exit(2); + } + + // Check if first arg looks like a URL (common mistake) + if (alias.startsWith('http://') || alias.startsWith('https://')) { + console.error('\nāš ļø It looks like you provided a URL without an alias.\n'); + console.error('The --save-auth command requires an alias name first:\n'); + console.error(` adcp --save-auth \n`); + console.error('Example:\n'); + console.error(` adcp --save-auth myagent ${alias}\n`); process.exit(2); } - // Non-interactive if URL is provided - const nonInteractive = !!url; - await interactiveSetup(alias, url, protocol, null, nonInteractive); + // Validate flags + if (providedAuthToken && noAuthFlag) { + console.error('ERROR: Cannot use both --auth and --no-auth\n'); + process.exit(2); + } + + // Determine mode: + // - If URL provided AND (--auth or --no-auth): fully non-interactive + // - Otherwise: interactive (prompts for missing values) + const hasAuthDecision = providedAuthToken !== null || noAuthFlag; + const nonInteractive = url && hasAuthDecision; + + await interactiveSetup(alias, url, protocol, providedAuthToken, nonInteractive, noAuthFlag); process.exit(0); }