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.
194 lines • 6.23 kB
JavaScript
import { existsSync } from 'fs';
import { join } from 'path';
import { isWindows, isMacOS, isLinux } from '../platform-utils.js';
/**
* Detects the execution context of the Twenty MCP Server CLI
* Determines if running via npx, global install, or local development
*/
export function detectExecutionContext() {
const env = extractNPXEnvironment();
// Check if running via npx
if (isNPXExecution(env)) {
return {
type: 'npx',
isTemporary: true,
packageCached: checkNPMCache(env),
npmCacheDir: env.npmConfigCache,
installLocation: 'temporary'
};
}
// Check if globally installed
if (isGlobalInstallation()) {
return {
type: 'global',
isTemporary: false,
packageCached: false,
installLocation: getGlobalInstallPath()
};
}
// Local development or project-specific installation
return {
type: 'local',
isTemporary: false,
packageCached: false,
installLocation: process.cwd()
};
}
/**
* Extract NPX-related environment variables
*/
function extractNPXEnvironment() {
return {
npmExecPath: process.env.npm_execpath,
npmCommand: process.env.npm_command,
npmConfigCache: process.env.npm_config_cache,
initCwd: process.env.INIT_CWD,
npmLifecycleEvent: process.env.npm_lifecycle_event
};
}
/**
* Determine if the current execution is via npx
*/
function isNPXExecution(env) {
// Primary indicators of npx execution
const indicators = [
// npm_command is 'exec' when using npx
env.npmCommand === 'exec',
// npm_execpath contains 'npx' in the path
env.npmExecPath && env.npmExecPath.includes('npx'),
// Check for npx-specific environment patterns
env.npmExecPath && env.npmExecPath.includes('_npx'),
// Alternative detection for newer npm versions
process.env.npm_config_user_config?.includes('npx'),
// Process arguments analysis
process.argv[1] && process.argv[1].includes('_npx')
];
return indicators.some(indicator => indicator === true);
}
/**
* Check if the package is running from a global installation
*/
function isGlobalInstallation() {
const execPath = process.argv[1];
if (!execPath)
return false;
// Platform-specific global installation paths
const globalPaths = getGlobalNodeModulesPaths();
return globalPaths.some(globalPath => execPath.includes(globalPath));
}
/**
* Get platform-specific global node_modules paths
*/
function getGlobalNodeModulesPaths() {
const paths = [];
if (isWindows()) {
// Windows global paths
paths.push('node_modules\\.bin', 'AppData\\Roaming\\npm\\node_modules', 'Program Files\\nodejs\\node_modules');
}
else if (isMacOS()) {
// macOS global paths
paths.push('/usr/local/lib/node_modules', '/usr/local/bin', '/opt/homebrew/lib/node_modules', '/opt/homebrew/bin');
}
else if (isLinux()) {
// Linux global paths
paths.push('/usr/lib/node_modules', '/usr/local/lib/node_modules', '/usr/bin', '/usr/local/bin');
}
// Node Version Manager paths (cross-platform)
const homeDir = process.env.HOME || process.env.USERPROFILE;
if (homeDir) {
paths.push(join(homeDir, '.nvm'), join(homeDir, '.nodenv'), join(homeDir, '.node_modules'));
}
return paths;
}
/**
* Check if the package is cached by npm for faster subsequent runs
*/
function checkNPMCache(env) {
if (!env.npmConfigCache) {
return false;
}
try {
// Check if twenty-mcp-server is in the npm cache
const cachePath = join(env.npmConfigCache, '_npx');
const packageCachePath = join(cachePath, 'twenty-mcp-server');
return existsSync(packageCachePath);
}
catch {
return false;
}
}
/**
* Get the global installation path if available
*/
function getGlobalInstallPath() {
try {
const execPath = process.argv[1];
if (!execPath)
return undefined;
// Extract the global installation directory
const globalPaths = getGlobalNodeModulesPaths();
const matchingPath = globalPaths.find(path => execPath.includes(path));
return matchingPath ? execPath : undefined;
}
catch {
return undefined;
}
}
/**
* Get execution context summary for debugging
*/
export function getExecutionContextSummary(context) {
const lines = [
`Execution Type: ${context.type}`,
`Temporary: ${context.isTemporary}`,
`Cached: ${context.packageCached}`
];
if (context.installLocation) {
lines.push(`Location: ${context.installLocation}`);
}
if (context.npmCacheDir) {
lines.push(`Cache Dir: ${context.npmCacheDir}`);
}
return lines.join('\n');
}
/**
* Validate execution context detection
* Returns true if detection was successful and confident
*/
export function validateExecutionContext(context) {
// Basic validation - we should always be able to determine the type
if (!['global', 'npx', 'local'].includes(context.type)) {
return false;
}
// npx executions should be temporary
if (context.type === 'npx' && !context.isTemporary) {
return false;
}
// global installations should not be temporary
if (context.type === 'global' && context.isTemporary) {
return false;
}
return true;
}
/**
* Performance-optimized context detection
* Caches the result to avoid repeated environment analysis
*/
let cachedContext = null;
export function getExecutionContext() {
if (cachedContext === null) {
cachedContext = detectExecutionContext();
// Validate the detection result
if (!validateExecutionContext(cachedContext)) {
// Fallback to local if detection is uncertain
cachedContext = {
type: 'local',
isTemporary: false,
packageCached: false,
installLocation: process.cwd()
};
}
}
return cachedContext;
}
//# sourceMappingURL=execution-context.js.map