#!/usr/bin/env node /** * GOAP MCP CLI * Command-line interface for the GOAP MCP server */ import { Command } from 'commander'; import { GoapMCPServer } from './mcp/server.js'; import { SearchResult } from './core/types.js'; import dotenv from 'dotenv'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; // Load environment variables dotenv.config(); // Get package.json version const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')); const program = new Command(); program .name('goalie') .description('AI-powered research assistant using Goal-Oriented Action Planning') .version(packageJson.version); // Start MCP Server Command program .command('start') .description('Start the MCP server') .option('--verbose', 'Enable verbose logging') .option('--plugins ', 'Comma-separated paths to external plugins') .option('--extensions ', 'Comma-separated paths to external extensions') .option('--port ', 'Port to run HTTP server on (if not stdio)') .action(async (options) => { try { // Set environment variables from options if (options.plugins) { process.env.GOAP_PLUGINS = options.plugins; } if (options.extensions) { process.env.GOAP_EXTENSIONS = options.extensions; } if (options.verbose) { console.error('๐Ÿ”ง Verbose logging enabled'); console.error('๐ŸŒ Environment:'); console.error(` โ€ข Perplexity API Key: ${process.env.PERPLEXITY_API_KEY ? 'โœ… Set' : 'โŒ Missing'}`); console.error(` โ€ข Plugins: ${process.env.GOAP_PLUGINS || 'None'}`); console.error(` โ€ข Extensions: ${process.env.GOAP_EXTENSIONS || 'None'}`); } // Validate required environment variables if (!process.env.PERPLEXITY_API_KEY) { console.error('โŒ ERROR: PERPLEXITY_API_KEY environment variable is required'); console.error('๐Ÿ’ก Get your API key from: https://www.perplexity.ai/settings/api'); process.exit(1); } const server = new GoapMCPServer(); await server.initialize(); await server.run(); } catch (error) { console.error('๐Ÿ’ฅ Failed to start GOAP MCP server:', error); process.exit(1); } }); // Main Search Command (goap.search equivalent) program .command('search ') .description('Execute intelligent search using GOAP planning') .option('-d, --domains ', 'Domain restrictions (comma-separated, e.g., edu,gov)') .option('-r, --recency ', 'Recency filter (hour|day|week|month|year)') .option('-m, --mode ', 'Search mode (web|academic)', 'web') .option('--max-results ', 'Maximum search results (1-20)', '10') .option('--model ', 'Perplexity model (sonar|sonar-pro|sonar-deep-research)', 'sonar-pro') .option('--no-reasoning', 'Disable Advanced Reasoning Engine') .option('--timeout ', 'Planning timeout in seconds', '30') .option('--output ', 'Output directory', '.research') .option('--format ', 'Output format (json|markdown|both)', 'both') .option('--no-save', 'Do not save to file') .option('--no-subfolder', 'Do not create query-based subfolder') .option('--page ', 'Page number for pagination', '1') .option('--page-size ', 'Items per page (5-50)', '10') .option('--verify', 'Enable Ed25519 signature verification') .option('--strict-verify', 'Require all citations to be signed') .option('--sign', 'Sign result with Ed25519') .option('--sign-key ', 'Base64 encoded Ed25519 private key') .option('--key-id ', 'Key identifier for signing') .option('--cert-id ', 'Certificate ID for mandate chain') .option('--trusted-issuers ', 'Trusted certificate issuers (comma-separated)') .action(async (query, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿ” Executing GOAP search...'); console.log(`๐Ÿ“ Query: ${query}`); // Parse comma-separated values const domains = options.domains ? options.domains.split(',').map((d: string) => d.trim()) : undefined; const trustedIssuers = options.trustedIssuers ? options.trustedIssuers.split(',').map((i: string) => i.trim()) : ['perplexity-ai', 'openai', 'anthropic']; // Build Ed25519 verification config if needed const ed25519Verification = (options.verify || options.strictVerify || options.sign) ? { enabled: true, requireSignatures: options.strictVerify || false, signResult: options.sign || false, privateKey: options.signKey, keyId: options.keyId, certId: options.certId, trustedIssuers } : undefined; // Add timeout wrapper const timeout = parseInt(options.timeout) * 1000 || 30000; const resultPromise = tools.executeGoapSearch({ query, domains, recency: options.recency, mode: options.mode, maxResults: parseInt(options.maxResults), model: options.model, enableReasoning: options.reasoning !== false, planningTimeout: parseInt(options.timeout), outputToFile: options.save !== false, outputFormat: options.format, outputPath: options.output, useQuerySubfolder: options.subfolder !== false, pagination: { page: parseInt(options.page), pageSize: parseInt(options.pageSize) }, ed25519Verification }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(`Search timed out after ${timeout/1000} seconds`)), timeout); }); const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult; // Display results console.log('\nโœ… Search completed!'); console.log('โ”'.repeat(50)); const answerPreview = result.answer.length > 500 ? result.answer.substring(0, 500) + '...\n\n[Full answer in files]' : result.answer; console.log('\n๐Ÿ“„ Answer:'); console.log(answerPreview); console.log('\n๐Ÿ“Š Metadata:'); console.log(` โ€ข Citations: ${result.citations.length}`); console.log(` โ€ข Execution time: ${result.metadata.executionTime}ms`); console.log(` โ€ข Replanned: ${result.metadata.replanned ? 'Yes' : 'No'}`); if (result.paginationInfo) { console.log(` โ€ข Page: ${result.paginationInfo.currentPage || 1}/${result.paginationInfo.totalPages || 1}`); console.log(` โ€ข Total results: ${result.paginationInfo.totalResults || result.citations?.length || 0}`); } if (result.metadata.ed25519Verification) { const v = result.metadata.ed25519Verification; console.log(`\n๐Ÿ” Verification:`); console.log(` โ€ข Verified citations: ${v.verified}/${v.total}`); if (v.untrusted.length > 0) { console.log(` โ€ข Untrusted sources: ${v.untrusted.join(', ')}`); } } if (result.metadata.savedFiles && options.save !== false) { console.log('\n๐Ÿ’พ Files saved:'); for (const file of result.metadata.savedFiles) { console.log(` โ€ข ${file}`); } } // Force exit immediately process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Search failed:', error); process.exit(1); } }); // Plan Explanation Command program .command('explain ') .description('Explain GOAP planning for a query without executing') .option('--no-steps', 'Hide step-by-step breakdown') .option('--no-reasoning', 'Hide reasoning analysis') .action(async (query, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿง  Generating plan explanation...'); console.log(`๐Ÿ“ Query: ${query}`); const explanation = await tools.executePlanExplain({ query, showSteps: options.steps !== false, showReasoning: options.reasoning !== false }); console.log('\n๐Ÿ“‹ Plan Explanation:'); console.log(JSON.stringify(explanation, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Explanation failed:', error); process.exit(1); } }); // Raw Perplexity Search Command program .command('raw ') .description('Direct Perplexity search without GOAP planning') .option('-d, --domains ', 'Domain restrictions (comma-separated)') .option('-r, --recency ', 'Recency filter (hour|day|week|month|year)') .option('-m, --mode ', 'Search mode (web|academic)', 'web') .option('--max-results ', 'Maximum results (1-20)', '10') .action(async (queries, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿ” Executing raw Perplexity search...'); console.log(`๐Ÿ“ Queries: ${queries.join(', ')}`); const domains = options.domains ? options.domains.split(',').map((d: string) => d.trim()) : undefined; const result = await tools.executeRawSearch({ query: queries, domains, recency: options.recency, mode: options.mode, maxResults: parseInt(options.maxResults) }); console.log('\nโœ… Raw search completed!'); console.log(JSON.stringify(result, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Raw search failed:', error); process.exit(1); } }); // Plugin Management Commands const pluginsCmd = program .command('plugins') .description('Plugin management'); pluginsCmd .command('list') .description('List all available plugins') .action(async () => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); const plugins = await tools.executeToolByName('plugin.list', {}); console.log('๐Ÿ”Œ Available Plugins:'); console.log(JSON.stringify(plugins, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Failed to list plugins:', error); process.exit(1); } }); pluginsCmd .command('enable ') .description('Enable a plugin') .action(async (name) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); await tools.executeToolByName('plugin.enable', { name }); console.log(`โœ… Plugin '${name}' enabled`); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Failed to enable plugin:', error); process.exit(1); } }); pluginsCmd .command('disable ') .description('Disable a plugin') .action(async (name) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); await tools.executeToolByName('plugin.disable', { name }); console.log(`โœ… Plugin '${name}' disabled`); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Failed to disable plugin:', error); process.exit(1); } }); pluginsCmd .command('info ') .description('Get plugin information') .action(async (name) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); const info = await tools.executeToolByName('plugin.info', { name }); console.log(`๐Ÿ”Œ Plugin Information for '${name}':`); console.log(JSON.stringify(info, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Failed to get plugin info:', error); process.exit(1); } }); // Advanced Reasoning Commands const reasoningCmd = program .command('reasoning') .description('Advanced reasoning capabilities'); reasoningCmd .command('chain-of-thought ') .description('Apply Chain-of-Thought reasoning with Tree-of-Thoughts') .option('--depth ', 'Reasoning depth (1-5)', '3') .option('--branches ', 'Number of branches (2-10)', '3') .action(async (query, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿง  Applying Chain-of-Thought reasoning...'); const result = await tools.executeToolByName('reasoning.chain_of_thought', { query, depth: parseInt(options.depth), branches: parseInt(options.branches) }); console.log('\nโœ… Reasoning complete:'); console.log(JSON.stringify(result, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Reasoning failed:', error); process.exit(1); } }); reasoningCmd .command('consistency ') .description('Check reasoning consistency with majority voting') .option('--samples ', 'Number of samples (3-10)', '5') .action(async (query, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿง  Checking reasoning consistency...'); const result = await tools.executeToolByName('reasoning.self_consistency', { query, samples: parseInt(options.samples) }); console.log('\nโœ… Consistency check complete:'); console.log(JSON.stringify(result, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Consistency check failed:', error); process.exit(1); } }); reasoningCmd .command('verify ') .description('Verify claims with citation grounding') .option('--citations ', 'Available citations (comma-separated)') .action(async (claims, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿง  Verifying claims...'); const citations = options.citations ? options.citations.split(',').map((c: string) => c.trim()) : []; const result = await tools.executeToolByName('reasoning.anti_hallucination', { claims, citations }); console.log('\nโœ… Verification complete:'); console.log(JSON.stringify(result, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Verification failed:', error); process.exit(1); } }); reasoningCmd .command('agents ') .description('Orchestrate multiple research agents') .option('--agents ', 'Agent types (comma-separated)', 'researcher,fact_checker,synthesizer,critic,summarizer') .option('--sequential', 'Execute agents sequentially instead of in parallel') .action(async (query, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿค– Orchestrating research agents...'); const agents = options.agents.split(',').map((a: string) => a.trim()); const result = await tools.executeToolByName('reasoning.agentic_research', { query, agents, parallel: !options.sequential }); console.log('\nโœ… Agent orchestration complete:'); console.log(JSON.stringify(result, null, 2)); process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Agent orchestration failed:', error); process.exit(1); } }); // Legacy test command with updated features program .command('test') .description('Test the GOAP planner with a sample query') .option('--query ', 'Test query', 'What are the latest developments in AI?') .option('--explain', 'Show plan explanation without executing') .action(async (options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿงช Testing GOAP planner...'); console.log(`๐Ÿ“ Query: ${options.query}`); if (options.explain) { const explanation = await tools.executePlanExplain({ query: options.query, showSteps: true, showReasoning: true }); console.log('๐Ÿ“‹ Plan Explanation:'); console.log(JSON.stringify(explanation, null, 2)); } else { // Add timeout wrapper const timeout = 30000; // 30 seconds const resultPromise = tools.executeGoapSearch({ query: options.query, enableReasoning: true, maxResults: 5, outputToFile: true, outputPath: '.research', useQuerySubfolder: false, outputFormat: 'both' }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Test timed out after 30 seconds')), timeout); }); const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult; console.log('โœ… Test Results:'); console.log(`๐Ÿ“ Answer: ${result.answer.substring(0, 200)}...`); console.log(`๐Ÿ“š Citations: ${result.citations.length}`); console.log(`โฑ๏ธ Execution time: ${result.metadata.executionTime}ms`); console.log(`๐Ÿ”„ Replanned: ${result.metadata.replanned}`); if (result.metadata.savedFiles) { console.log(`๐Ÿ’พ Output saved to: ${result.metadata.savedFiles.join(', ')}`); } } // Force exit after a brief delay to ensure all output is flushed setTimeout(() => { process.exit(0); }, 100); } catch (error) { console.error('๐Ÿ’ฅ Test failed:', error); process.exit(1); } }); // Legacy query command for backward compatibility program .command('query ') .description('Execute a research query (legacy, use "search" instead)') .option('--no-save', 'Do not save output to files') .option('--output ', 'Output directory path', '.research') .option('--format ', 'Output format (json, markdown, both)', 'both') .option('--max-results ', 'Maximum search results', '10') .option('--model ', 'Perplexity model', 'sonar-pro') .option('--explain', 'Show plan explanation without executing') .action(async (question, options) => { try { const { GoapMCPTools } = await import('./mcp/tools.js'); const tools = new GoapMCPTools(); await tools.initialize(); console.log('๐Ÿ” Executing research query...'); console.log(`๐Ÿ“ Query: ${question}`); if (options.explain) { const explanation = await tools.executePlanExplain({ query: question, showSteps: true, showReasoning: true }); console.log('๐Ÿ“‹ Plan Explanation:'); console.log(JSON.stringify(explanation, null, 2)); process.exit(0); return; } // Add timeout wrapper const timeout = 30000; // 30 seconds const resultPromise = tools.executeGoapSearch({ query: question, enableReasoning: true, maxResults: parseInt(options.maxResults), model: options.model, outputToFile: options.save !== false, outputPath: options.output, useQuerySubfolder: true, outputFormat: options.format }); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Query timed out after 30 seconds')), timeout); }); const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult; // Display summary console.log('\nโœ… Research completed!'); console.log('โ”'.repeat(50)); const answerPreview = result.answer.length > 500 ? result.answer.substring(0, 500) + '...\n\n[Full answer saved to file]' : result.answer; console.log('\n๐Ÿ“„ Answer:'); console.log(answerPreview); console.log('\n๐Ÿ“Š Statistics:'); console.log(` โ€ข Citations: ${result.citations.length}`); console.log(` โ€ข Execution time: ${result.metadata.executionTime}ms`); console.log(` โ€ข Replanned: ${result.metadata.replanned ? 'Yes' : 'No'}`); if (result.usage) { console.log(` โ€ข Tokens used: ${result.usage.tokens || 'N/A'}`); } if (result.metadata.savedFiles && options.save !== false) { console.log('\n๐Ÿ’พ Files saved:'); for (const file of result.metadata.savedFiles) { console.log(` โ€ข ${file}`); } } // Force exit immediately process.exit(0); } catch (error) { console.error('๐Ÿ’ฅ Query failed:', error); process.exit(1); } }); // Validation command program .command('validate') .description('Validate configuration and dependencies') .action(async () => { console.log('๐Ÿ” Validating GOAP MCP configuration...'); // Check environment variables const checks = [ { name: 'Perplexity API Key', check: () => !!process.env.PERPLEXITY_API_KEY, fix: 'Set PERPLEXITY_API_KEY environment variable' }, { name: 'Node.js version', check: () => { const version = process.version; const major = parseInt(version.slice(1).split('.')[0]); return major >= 18; }, fix: 'Update Node.js to version 18 or higher' } ]; let allPassed = true; for (const check of checks) { const passed = check.check(); const status = passed ? 'โœ…' : 'โŒ'; console.log(`${status} ${check.name}`); if (!passed) { console.log(` ๐Ÿ’ก ${check.fix}`); allPassed = false; } } // Test Advanced Reasoning Engine WASM try { const { AdvancedReasoningEngine } = await import('./core/advanced-reasoning-engine'); const engine = new AdvancedReasoningEngine(); await engine.initialize(); console.log('โœ… Advanced Reasoning Engine integration'); } catch (error) { console.log('โš ๏ธ Advanced Reasoning Engine (will use fallback)'); } // Test MCP SDK try { await import('@modelcontextprotocol/sdk/server/index.js'); console.log('โœ… MCP SDK'); } catch (error) { console.log('โŒ MCP SDK - npm install required'); allPassed = false; } if (allPassed) { console.log('๐ŸŽ‰ All validations passed! Ready to run GOAP MCP server.'); } else { console.log('โš ๏ธ Some validations failed. Please fix the issues above.'); process.exit(1); } }); // Info command program .command('info') .description('Show system information and capabilities') .action(async () => { console.log('๐ŸŽฏ GOAP MCP Server Information'); console.log('=============================='); console.log(''); console.log('๐Ÿ“‹ Core Features:'); console.log(' โ€ข STRIPS-style preconditions and effects'); console.log(' โ€ข A* pathfinding for optimal plans'); console.log(' โ€ข Dynamic re-planning on failure'); console.log(' โ€ข Advanced Reasoning Engine enhanced reasoning'); console.log(' โ€ข Perplexity API integration'); console.log(' โ€ข Extensible plugin system'); console.log(''); console.log('๐Ÿ”ง Available Tools:'); console.log(' โ€ข goap.search - Intelligent search with planning'); console.log(' โ€ข goap.plan.explain - Plan explanation'); console.log(' โ€ข search.raw - Direct Perplexity search'); console.log(' โ€ข plugin.* - Plugin management tools'); console.log(' โ€ข reasoning.* - Advanced reasoning tools'); console.log(''); console.log('๐ŸŽช Plugin System:'); console.log(' โ€ข cost-tracker - Track execution costs'); console.log(' โ€ข performance-monitor - Monitor execution performance'); console.log(' โ€ข logger - Comprehensive logging'); console.log(' โ€ข query-diversifier - Enhance search queries'); console.log(' โ€ข chain-of-thought - CoT reasoning'); console.log(' โ€ข self-consistency - Consistency checking'); console.log(' โ€ข anti-hallucination - Citation grounding'); console.log(' โ€ข agentic-research - Multi-agent coordination'); console.log(''); console.log('๐Ÿง  Advanced Reasoning Engine:'); console.log(' โ€ข Pattern analysis algorithms'); console.log(' โ€ข Predictive modeling capabilities'); console.log(' โ€ข State-enhanced reasoning'); console.log(' โ€ข Multi-agent coordination'); console.log(' โ€ข Ed25519 cryptographic verification'); console.log(''); console.log('๐ŸŒŸ Advantages over standard web search:'); console.log(' โ€ข Multi-step planning with dependencies'); console.log(' โ€ข Automatic query optimization'); console.log(' โ€ข Enhanced reasoning with Advanced Reasoning Engine'); console.log(' โ€ข Dynamic re-planning on failures'); console.log(' โ€ข Comprehensive answer verification'); console.log(' โ€ข Cost optimization with A* pathfinding'); console.log(' โ€ข Extensible plugin architecture'); console.log(' โ€ข Cryptographic citation verification'); console.log(''); console.log('๐Ÿ’ก Quick Start Examples:'); console.log(' npx goalie search "latest AI developments"'); console.log(' npx goalie explain "quantum computing breakthroughs"'); console.log(' npx goalie reasoning chain-of-thought "solve climate change"'); console.log(' npx goalie plugins list'); }); // Default command runs the server program.parse(); // If no command provided, show help if (!process.argv.slice(2).length) { program.outputHelp(); }