UNPKG

twenty-mcp-server

Version:

Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.

236 lines • 9.3 kB
import chalk from 'chalk'; import { existsSync, readFileSync, statSync } from 'fs'; import { join } from 'path'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); export async function statusCommand(options) { if (!options.json) { console.log(chalk.bold.blue('šŸ“Š Twenty MCP Server Status')); console.log(chalk.gray('Checking server status and configuration\n')); } try { const status = await gatherStatus(options.verbose || false); if (options.json) { console.log(JSON.stringify(status, null, 2)); } else { displayStatus(status, options.verbose || false); } } catch (error) { if (options.json) { console.log(JSON.stringify({ error: error instanceof Error ? error.message : error }, null, 2)); } else { console.error(chalk.red('āŒ Failed to get status:'), error instanceof Error ? error.message : error); } process.exit(1); } } async function gatherStatus(verbose) { const status = { installation: { projectBuilt: false, nodeModulesInstalled: false, configurationExists: false, }, configuration: { twentyApiKey: false, twentyBaseUrl: null, authEnabled: false, ipProtectionEnabled: false, environment: 'development', }, server: { httpServerRunning: false, }, validation: { configValid: false, apiConnectionWorking: false, }, }; // Check installation await checkInstallation(status); // Check configuration await checkConfiguration(status); // Check server status await checkServerStatus(status); // Check validation (if verbose) if (verbose) { await checkValidation(status); } return status; } async function checkInstallation(status) { // Check if project is built const distPath = join(process.cwd(), 'dist/index.js'); status.installation.projectBuilt = existsSync(distPath); if (status.installation.projectBuilt) { const stats = statSync(distPath); status.installation.lastBuilt = stats.mtime.toISOString(); } // Check node modules const nodeModulesPath = join(process.cwd(), 'node_modules'); status.installation.nodeModulesInstalled = existsSync(nodeModulesPath); // Check configuration file const envPath = join(process.cwd(), '.env'); status.installation.configurationExists = existsSync(envPath); } async function checkConfiguration(status) { const envPath = join(process.cwd(), '.env'); if (!existsSync(envPath)) { return; } const envContent = readFileSync(envPath, 'utf8'); const envVars = new Map(); envContent.split('\n').forEach(line => { const match = line.match(/^([A-Z_]+)=(.*)$/); if (match) { envVars.set(match[1], match[2]); } }); // Check Twenty configuration status.configuration.twentyApiKey = envVars.has('TWENTY_API_KEY') && !!envVars.get('TWENTY_API_KEY'); status.configuration.twentyBaseUrl = envVars.get('TWENTY_BASE_URL') || null; // Check auth configuration status.configuration.authEnabled = envVars.get('AUTH_ENABLED') === 'true'; // Check IP protection status.configuration.ipProtectionEnabled = envVars.get('IP_PROTECTION_ENABLED') === 'true'; // Determine environment if (envVars.has('NODE_ENV')) { status.configuration.environment = envVars.get('NODE_ENV') || 'development'; } } async function checkServerStatus(status) { try { // Try to connect to HTTP server on default port const response = await checkHttpServer(3000); if (response) { status.server.httpServerRunning = true; status.server.port = 3000; } } catch { // Server not running or not accessible status.server.httpServerRunning = false; } // Try to find running Node.js processes try { const { stdout } = await execAsync('pgrep -f "twenty-mcp"'); const pids = stdout.trim().split('\n').filter(pid => pid); if (pids.length > 0) { status.server.processId = parseInt(pids[0], 10); } } catch { // No process found or command failed } } async function checkHttpServer(port) { return new Promise((resolve) => { const http = require('http'); const req = http.request({ hostname: 'localhost', port: port, path: '/health', method: 'GET', timeout: 2000, }, (res) => { resolve(res.statusCode === 200); }); req.on('error', () => resolve(false)); req.on('timeout', () => { req.destroy(); resolve(false); }); req.end(); }); } async function checkValidation(status) { try { // Run validation script const { stdout, stderr } = await execAsync('npm run validate'); status.validation.configValid = !stderr && stdout.includes('SUCCESS'); status.validation.lastValidated = new Date().toISOString(); // Basic API connection test if (status.configuration.twentyApiKey && status.configuration.twentyBaseUrl) { status.validation.apiConnectionWorking = await testApiConnection(status.configuration.twentyBaseUrl); } } catch { status.validation.configValid = false; status.validation.apiConnectionWorking = false; } } async function testApiConnection(baseUrl) { try { const response = await fetch(`${baseUrl}/graphql`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.TWENTY_API_KEY}`, }, body: JSON.stringify({ query: '{ __typename }', }), }); return response.ok; } catch { return false; } } function displayStatus(status, verbose) { // Installation Status console.log(chalk.bold.yellow('šŸ—ļø Installation:')); console.log(` Project Built: ${status.installation.projectBuilt ? chalk.green('āœ… Yes') : chalk.red('āŒ No')}`); console.log(` Dependencies: ${status.installation.nodeModulesInstalled ? chalk.green('āœ… Installed') : chalk.red('āŒ Missing')}`); console.log(` Configuration: ${status.installation.configurationExists ? chalk.green('āœ… Found') : chalk.red('āŒ Missing')}`); if (verbose && status.installation.lastBuilt) { console.log(` Last Built: ${chalk.gray(new Date(status.installation.lastBuilt).toLocaleString())}`); } // Configuration Status console.log(chalk.bold.yellow('\nāš™ļø Configuration:')); console.log(` Twenty API Key: ${status.configuration.twentyApiKey ? chalk.green('āœ… Set') : chalk.red('āŒ Missing')}`); console.log(` Twenty Base URL: ${status.configuration.twentyBaseUrl ? chalk.green(status.configuration.twentyBaseUrl) : chalk.red('āŒ Not set')}`); console.log(` OAuth Auth: ${status.configuration.authEnabled ? chalk.green('āœ… Enabled') : chalk.gray('āŒ Disabled')}`); console.log(` IP Protection: ${status.configuration.ipProtectionEnabled ? chalk.green('āœ… Enabled') : chalk.gray('āŒ Disabled')}`); console.log(` Environment: ${chalk.blue(status.configuration.environment)}`); // Server Status console.log(chalk.bold.yellow('\nšŸš€ Server:')); console.log(` HTTP Server: ${status.server.httpServerRunning ? chalk.green('āœ… Running') : chalk.red('āŒ Not running')}`); if (status.server.port) { console.log(` Port: ${chalk.cyan(status.server.port)}`); } if (status.server.processId) { console.log(` Process ID: ${chalk.gray(status.server.processId)}`); } // Validation Status (if verbose) if (verbose) { console.log(chalk.bold.yellow('\nšŸ” Validation:')); console.log(` Config Valid: ${status.validation.configValid ? chalk.green('āœ… Yes') : chalk.red('āŒ No')}`); console.log(` API Connection: ${status.validation.apiConnectionWorking ? chalk.green('āœ… Working') : chalk.red('āŒ Failed')}`); if (status.validation.lastValidated) { console.log(` Last Checked: ${chalk.gray(new Date(status.validation.lastValidated).toLocaleString())}`); } } // Overall Status Summary console.log(chalk.bold.yellow('\nšŸ“‹ Summary:')); const isHealthy = status.installation.projectBuilt && status.installation.nodeModulesInstalled && status.installation.configurationExists && status.configuration.twentyApiKey; if (isHealthy) { console.log(chalk.green('āœ… Server is ready to use')); if (!status.server.httpServerRunning) { console.log(chalk.blue('šŸ’” Run "twenty-mcp start" to start the server')); } } else { console.log(chalk.red('āŒ Server needs configuration')); console.log(chalk.blue('šŸ’” Run "twenty-mcp setup" to configure')); } console.log(''); } //# sourceMappingURL=status.js.map