Skip to main content
Glama

greptile-mcp

cli.ts15 kB
#!/usr/bin/env node import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import chalk from 'chalk'; import ora from 'ora'; import { config } from 'dotenv'; import { GreptileMCPServer } from './server.js'; import { validateConfig, checkEnvironmentVariables } from './utils/index.js'; import type { Config, Repository } from './types/index.js'; // Load environment variables config(); /** * Get platform-specific environment variable setup instructions */ function getPlatformSpecificEnvInstructions(): string { const platform = process.platform; const isWindows = platform === 'win32'; if (isWindows) { return ` ${chalk.gray('Windows PowerShell:')} ${chalk.green('$env:GREPTILE_API_KEY="your_api_key_here"')} ${chalk.green('$env:GITHUB_TOKEN="your_github_token_here"')} ${chalk.gray('Windows Command Prompt:')} ${chalk.green('set GREPTILE_API_KEY=your_api_key_here')} ${chalk.green('set GITHUB_TOKEN=your_github_token_here')} ${chalk.gray('Permanent (Windows):')} ${chalk.green('setx GREPTILE_API_KEY "your_api_key_here"')} ${chalk.green('setx GITHUB_TOKEN "your_github_token_here"')} ${chalk.yellow('Note: Restart terminal after using setx')}`; } else { // Unix-like systems (Linux, macOS) const shell = process.env.SHELL?.split('/').pop() || 'bash'; const configFile = shell === 'zsh' ? '~/.zshrc' : shell === 'fish' ? '~/.config/fish/config.fish' : '~/.bashrc'; return ` ${chalk.gray('Current session (Linux/macOS):')} ${chalk.green('export GREPTILE_API_KEY="your_api_key_here"')} ${chalk.green('export GITHUB_TOKEN="your_github_token_here"')} ${chalk.gray(`Permanent (add to ${configFile}):`)} ${chalk.green('echo \'export GREPTILE_API_KEY="your_api_key_here"\' >> ' + configFile)} ${chalk.green('echo \'export GITHUB_TOKEN="your_github_token_here"\' >> ' + configFile)} ${chalk.green('source ' + configFile)} ${chalk.gray('Or use a one-liner:')} ${chalk.green('GREPTILE_API_KEY="your_key" GITHUB_TOKEN="your_token" npx greptile-mcp-server')}`; } } interface CliArgs { 'api-key'?: string; 'github-token'?: string; 'base-url'?: string; config?: string; repositories?: string; stream?: boolean; 'session-id'?: string; timeout?: number; verbose?: boolean; help?: boolean; version?: boolean; } /** * Create CLI configuration from arguments and environment */ function createConfig(args: CliArgs): Config { // Parse repositories if provided let repositories: Repository[] | undefined; if (args.repositories) { try { repositories = JSON.parse(args.repositories) as Repository[]; } catch (error) { console.error(chalk.red('Error: Invalid repositories JSON format')); process.exit(1); } } return validateConfig({ apiKey: args['api-key'], githubToken: args['github-token'], baseUrl: args['base-url'] || 'https://api.greptile.com/v2', repositories, features: { streaming: args.stream ?? true, orchestration: true, flowEnhancement: true, }, }); } /** * Display banner and version information */ function displayBanner(): void { console.log( chalk.cyan(` ╔══════════════════════════════════════════════════════════╗ ║ 🚀 GREPTILE MCP SERVER ║ ║ TypeScript Edition v3.0.4 ║ ║ ║ ║ AI-powered code search and querying via MCP ║ ║ Built with Model Context Protocol SDK ║ ╚══════════════════════════════════════════════════════════╝ `) ); } /** * Interactive setup wizard */ async function runSetupWizard(): Promise<void> { displayBanner(); console.log(chalk.yellow('\n📋 Interactive Setup Wizard\n')); console.log(`To use Greptile MCP Server, you need: ${chalk.cyan('1. Greptile API Key')} • Get it from: ${chalk.underline('https://app.greptile.com/settings/api')} • Set as: ${chalk.gray('GREPTILE_API_KEY')} environment variable • Or use: ${chalk.gray('--api-key')} CLI argument ${chalk.cyan('2. GitHub Token')} • Generate at: ${chalk.underline('https://github.com/settings/tokens')} • Needs: ${chalk.gray('repo')} permissions for repositories you want to index • Set as: ${chalk.gray('GITHUB_TOKEN')} environment variable • Or use: ${chalk.gray('--github-token')} CLI argument ${chalk.yellow('Quick Start Examples:')} ${chalk.gray('# Start MCP server (stdio mode)')} ${chalk.green('npx greptile-mcp-server')} ${chalk.gray('# With inline credentials')} ${chalk.green('npx greptile-mcp-server --api-key=xxx --github-token=xxx')} ${chalk.gray('# Index and query a repository')} ${chalk.green('npx greptile-mcp-server --repositories=\'[{"remote":"github","repository":"microsoft/vscode","branch":"main"}]\'')} ${chalk.yellow('Environment Setup:')} ${chalk.cyan('Option 1: .env file (Recommended for local development)')} Create a ${chalk.cyan('.env')} file in your project directory: ${chalk.gray(`GREPTILE_API_KEY=your_api_key_here GITHUB_TOKEN=your_github_token_here`)} ${chalk.cyan('Option 2: System Environment Variables')} ${getPlatformSpecificEnvInstructions()} ${chalk.yellow('MCP Client Integration:')} Add to your MCP client configuration: ${chalk.gray(`{ "mcpServers": { "greptile": { "command": "npx", "args": ["greptile-mcp-server"] } } }`)} ${chalk.yellow('Testing Your Setup:')} After setting up your environment variables, test your configuration: ${chalk.green('npx greptile-mcp-server test')} ${chalk.yellow('Common Issues & Solutions:')} ${chalk.gray('❌ "Environment variables missing"')} ${chalk.white('→ Make sure you restart your terminal after setting permanent env vars')} ${chalk.white('→ Verify with:')} ${chalk.green('echo $GREPTILE_API_KEY')} ${chalk.gray('(Linux/macOS)')} ${chalk.white('→ Verify with:')} ${chalk.green('echo $env:GREPTILE_API_KEY')} ${chalk.gray('(Windows PowerShell)')} ${chalk.gray('❌ "GitHub token validation failed"')} ${chalk.white('→ Ensure your token has "repo" permissions')} ${chalk.white('→ Generate a new token at: https://github.com/settings/tokens')} ${chalk.white('→ Select "Fine-grained personal access tokens" for better security')} ${chalk.gray('❌ "Greptile API authentication failed"')} ${chalk.white('→ Get your API key from: https://app.greptile.com/settings/api')} ${chalk.white('→ Check if your API key has expired')} ${chalk.white('→ Verify the key is correctly copied (no extra spaces)')} ${chalk.yellow('Need Help?')} ${chalk.white('Run')} ${chalk.green('npx greptile-mcp-server test')} ${chalk.white('for detailed diagnostics')} `); } /** * Test API connectivity */ async function testConnection(config: Config): Promise<void> { console.log(chalk.cyan('🔍 Testing Greptile MCP Server Configuration...\n')); // Test 1: Environment Variables const spinner1 = ora('Checking environment variables...').start(); const envStatus = checkEnvironmentVariables(); if (!envStatus.isFullyConfigured) { spinner1.fail(chalk.red('❌ Environment variables missing')); console.log(chalk.yellow('Missing variables:'), envStatus.missingVars.join(', ')); console.log(chalk.yellow('\n💡 Run "npx greptile-mcp-server init" for setup help')); process.exit(1); } spinner1.succeed(chalk.green('✅ Environment variables configured')); // Test 2: Greptile API Authentication const spinner2 = ora('Testing Greptile API authentication...').start(); try { const { GreptileClient } = await import('./clients/greptile.js'); const client = new GreptileClient(config); // Make an actual authenticated API call to validate credentials const isHealthy = await client.healthCheck(); if (!isHealthy) { throw new Error( 'Greptile API health check failed - service may be down or credentials invalid' ); } spinner2.succeed(chalk.green('✅ Greptile API connection verified')); } catch (error) { spinner2.fail(chalk.red('❌ Greptile API authentication failed')); console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`)); console.log(chalk.yellow('\n🔧 Troubleshooting:')); console.log('• Verify your GREPTILE_API_KEY is correct'); console.log('• Get your API key from: https://app.greptile.com/settings/api'); console.log('• Check that your API key has not expired'); process.exit(1); } // Test 3: GitHub Token Validation const spinner3 = ora('Testing GitHub token permissions...').start(); try { const githubResponse = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${config.githubToken}`, 'User-Agent': 'greptile-mcp-server/3.0.0', }, }); if (!githubResponse.ok) { throw new Error(`GitHub API returned ${githubResponse.status}: ${githubResponse.statusText}`); } const userData = (await githubResponse.json()) as { login: string }; spinner3.succeed(chalk.green(`✅ GitHub token verified (user: ${userData.login})`)); } catch (error) { spinner3.fail(chalk.red('❌ GitHub token validation failed')); console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`)); console.log(chalk.yellow('\n🔧 Troubleshooting:')); console.log('• Verify your GITHUB_TOKEN is correct'); console.log('• Generate a new token at: https://github.com/settings/tokens'); console.log('• Ensure token has "repo" permissions for repositories you want to index'); process.exit(1); } console.log(chalk.green('\n🎉 All tests passed! Your Greptile MCP Server is ready.')); console.log(chalk.cyan('Server is ready to accept MCP connections')); } /** * Start the MCP server */ async function startServer(args: CliArgs): Promise<void> { try { const config = createConfig(args); if (args.verbose) { displayBanner(); console.log(chalk.gray('Configuration:')); console.log(chalk.gray(`• Base URL: ${config.baseUrl}`)); console.log( chalk.gray(`• Streaming: ${config.features?.streaming ? 'enabled' : 'disabled'}`) ); console.log(chalk.gray(`• Repositories: ${config.repositories?.length || 0} configured`)); console.log(''); } const spinner = ora('Initializing Greptile MCP Server...').start(); const server = await GreptileMCPServer.create(config); spinner.succeed(chalk.green('✅ Server initialized successfully')); if (args.verbose) { console.log(chalk.cyan('🚀 Starting MCP server on stdio transport...')); console.log(chalk.gray('Server is now ready to accept MCP requests')); console.log(chalk.gray('Use Ctrl+C to stop the server')); console.log(''); } // Start the server await server.start(); } catch (error) { console.error(chalk.red('Failed to start server:')); console.error(chalk.red(error instanceof Error ? error.message : String(error))); if (error instanceof Error) { if (error.message.includes('API key') || error.message.includes('GITHUB_TOKEN')) { console.log(chalk.yellow('\n🔧 Quick Setup:')); console.log( chalk.yellow('• Run'), chalk.green('npx greptile-mcp-server init'), chalk.yellow('for interactive setup') ); console.log( chalk.yellow('• Run'), chalk.green('npx greptile-mcp-server test'), chalk.yellow('to validate your configuration') ); } } process.exit(1); } } /** * Main CLI handler */ async function main(): Promise<void> { const argv = await yargs(hideBin(process.argv)) .scriptName('greptile-mcp-server') .usage('$0 [options]') .example('$0', 'Start MCP server with environment variables') .example('$0 --api-key xxx --github-token yyy', 'Start with inline credentials') .example('$0 init', 'Run interactive setup wizard') .example('$0 test', 'Test API connectivity') .command('init', 'Run interactive setup wizard', {}, async () => { await runSetupWizard(); }) .command('test', 'Test API connectivity', {}, async (args: any) => { try { const config = createConfig(args); await testConnection(config); } catch (error) { console.error(chalk.red('Configuration error:')); console.error(chalk.red(error instanceof Error ? error.message : String(error))); console.log(chalk.yellow('\n💡 Run "npx greptile-mcp-server init" for setup help')); process.exit(1); } }) .option('api-key', { type: 'string', description: 'Greptile API key (or set GREPTILE_API_KEY env var)', }) .option('github-token', { type: 'string', description: 'GitHub personal access token (or set GITHUB_TOKEN env var)', }) .option('base-url', { type: 'string', description: 'Greptile API base URL', default: 'https://api.greptile.com/v2', }) .option('config', { type: 'string', description: 'Path to configuration file', }) .option('repositories', { type: 'string', description: 'JSON array of repositories to configure', }) .option('stream', { type: 'boolean', description: 'Enable streaming responses by default', default: true, }) .option('session-id', { type: 'string', description: 'Default session ID for queries', }) .option('timeout', { type: 'number', description: 'Default request timeout in milliseconds', default: 60000, }) .option('verbose', { alias: 'v', type: 'boolean', description: 'Enable verbose output', default: false, }) .help('help') .alias('help', 'h') .version('3.0.0') .alias('version', 'V') .strict() .parseAsync(); // Handle default command (start server) if (argv._.length === 0) { await startServer(argv as CliArgs); } } // Handle unhandled rejections and exceptions process.on('unhandledRejection', (reason, promise) => { console.error(chalk.red('Unhandled Rejection at:'), promise, chalk.red('reason:'), reason); process.exit(1); }); process.on('uncaughtException', error => { console.error(chalk.red('Uncaught Exception:'), error); process.exit(1); }); // Handle graceful shutdown process.on('SIGINT', () => { console.log(chalk.yellow('\n🛑 Shutting down server gracefully...')); process.exit(0); }); process.on('SIGTERM', () => { console.log(chalk.yellow('\n🛑 Received SIGTERM, shutting down...')); process.exit(0); }); // Start the CLI main().catch(error => { console.error(chalk.red('CLI Error:'), error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sosacrazy126/greptile-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server