UNPKG

@aurracloud/mcp-cli

Version:

A command-line tool to install, manage, and setup MCP (Model Context Protocol) servers with Docker support

324 lines 12.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.checkPythonPackageExistence = checkPythonPackageExistence; exports.checkPythonPackageExecutability = checkPythonPackageExecutability; exports.extractPythonExecutableInfo = extractPythonExecutableInfo; exports.isUvxExecutable = isUvxExecutable; exports.getUvxCommand = getUvxCommand; exports.readPyProjectToml = readPyProjectToml; exports.hasPythonPackageFiles = hasPythonPackageFiles; exports.checkUvxAvailability = checkUvxAvailability; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const logger_1 = require("./utils/logger"); const child_process_1 = require("child_process"); const util_1 = require("util"); const execPromise = (0, util_1.promisify)(child_process_1.exec); /** * Checks if a Python package exists on PyPI and whether it's executable via uvx * * @param packageName - The name of the package to check * @returns Promise resolving to package existence and executability information */ async function checkPythonPackageExistence(packageName) { try { const response = await fetch(`https://pypi.org/pypi/${packageName}/json`); if (!response.ok) { // If response status is 404, package doesn't exist if (response.status === 404) { return { exists: false, isExecutable: false }; } // For other errors, throw const errorText = await response.text(); throw new Error(`Failed to check package existence: ${response.status} ${response.statusText} - ${errorText}`); } const data = await response.json(); // Get package information const packageInfo = data.info; // Check if the package is executable (has console_scripts entry points or scripts) const isExecutable = checkPythonPackageExecutability(packageInfo); // Determine executable name and path if it's executable let executableName; let executablePath; if (isExecutable) { const execInfo = extractPythonExecutableInfo(packageInfo); executableName = execInfo.executableName; executablePath = execInfo.executablePath; } return { exists: true, isExecutable, packageInfo, executableName, executablePath }; } catch (error) { logger_1.logger.error(`Error checking Python package existence for ${packageName}`, error); throw error; } } /** * Checks if a Python package is executable (has console scripts or entry points) * * @param packageInfo - Package information from PyPI * @returns Boolean indicating if the package is executable */ function checkPythonPackageExecutability(packageInfo) { // Check for console_scripts in entry_points (most common way) if (packageInfo.entry_points) { try { const entryPoints = typeof packageInfo.entry_points === 'string' ? JSON.parse(packageInfo.entry_points) : packageInfo.entry_points; if (entryPoints.console_scripts && Object.keys(entryPoints.console_scripts).length > 0) { return true; } } catch (error) { // If entry_points is malformed, continue checking other indicators } } // Check for scripts in project_urls or other metadata if (packageInfo.project_urls) { // Some packages indicate executability through project URLs const urls = Object.keys(packageInfo.project_urls).map(k => k.toLowerCase()); if (urls.some(url => url.includes('cli') || url.includes('command') || url.includes('tool'))) { return true; } } // Check classifiers for console application indicators if (packageInfo.classifiers) { const hasConsoleClassifier = packageInfo.classifiers.some(classifier => classifier.includes('Environment :: Console') || classifier.includes('Topic :: System :: Systems Administration') || classifier.includes('Topic :: Utilities')); if (hasConsoleClassifier) { return true; } } // Check if package name suggests it's a tool (common patterns) const toolPatterns = ['-cli', 'cli-', 'tool', 'cmd', 'command']; const nameIndicatesTool = toolPatterns.some(pattern => packageInfo.name.toLowerCase().includes(pattern)); return nameIndicatesTool; } /** * Extracts executable information from package metadata * * @param packageInfo - Package information from PyPI * @returns Object with executable name and path information */ function extractPythonExecutableInfo(packageInfo) { // Try to extract from entry_points first if (packageInfo.entry_points) { try { const entryPoints = typeof packageInfo.entry_points === 'string' ? JSON.parse(packageInfo.entry_points) : packageInfo.entry_points; if (entryPoints.console_scripts) { const scripts = Object.entries(entryPoints.console_scripts); if (scripts.length > 0) { const [scriptName, scriptPath] = scripts[0]; return { executableName: scriptName, executablePath: scriptPath }; } } } catch (error) { // If entry_points is malformed, fall back to package name } } // Fallback to package name as executable name return { executableName: packageInfo.name, executablePath: packageInfo.name }; } /** * Verifies if a Python package can be installed and executed via uvx * * @param packageName - The name of the package to verify * @returns Promise resolving to a boolean indicating if the package is uvx-executable */ async function isUvxExecutable(packageName) { try { const result = await checkPythonPackageExistence(packageName); if (!result.exists) { logger_1.logger.warn(`Python package ${packageName} does not exist on PyPI`); return false; } if (!result.isExecutable) { logger_1.logger.warn(`Python package ${packageName} exists but is not executable`); return false; } return true; } catch (error) { logger_1.logger.error(`Error checking if ${packageName} is uvx-executable`, error); return false; } } /** * Gets the recommended uvx command for a Python package * * @param packageName - The name of the package * @returns Promise resolving to the uvx command structure or null if not executable */ async function getUvxCommand(packageName) { try { const result = await checkPythonPackageExistence(packageName); if (!result.exists || !result.isExecutable) { return null; } // Use the executable name if available, otherwise use package name const executableName = result.executableName || packageName; return { command: 'uvx', args: [executableName] }; } catch (error) { logger_1.logger.error(`Error getting uvx command for ${packageName}`, error); return null; } } /** * Reads and parses a pyproject.toml file * * @param filePath - Path to the pyproject.toml file * @returns Parsed pyproject.toml content or null if file doesn't exist or is invalid */ function readPyProjectToml(filePath) { try { if (!fs.existsSync(filePath)) { return null; } const content = fs.readFileSync(filePath, 'utf8'); // Simple TOML parsing for pyproject.toml // This is a basic implementation - for production use, consider using a proper TOML parser const lines = content.split('\n'); const result = {}; let currentSection = []; let currentObject = result; for (const line of lines) { const trimmed = line.trim(); // Skip comments and empty lines if (!trimmed || trimmed.startsWith('#')) { continue; } // Handle section headers if (trimmed.startsWith('[') && trimmed.endsWith(']')) { const sectionPath = trimmed.slice(1, -1).split('.'); currentSection = sectionPath; currentObject = result; // Navigate/create nested structure for (const part of sectionPath) { if (!currentObject[part]) { currentObject[part] = {}; } currentObject = currentObject[part]; } continue; } // Handle key-value pairs const equalIndex = trimmed.indexOf('='); if (equalIndex > 0) { const key = trimmed.slice(0, equalIndex).trim(); let value = trimmed.slice(equalIndex + 1).trim(); // Remove quotes if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1); } // Handle arrays (basic implementation) if (value.startsWith('[') && value.endsWith(']')) { const arrayContent = value.slice(1, -1); if (arrayContent.trim()) { currentObject[key] = arrayContent.split(',').map(item => item.trim().replace(/^["']|["']$/g, '')); } else { currentObject[key] = []; } } else { currentObject[key] = value; } } } return result; } catch (error) { logger_1.logger.error(`Error reading pyproject.toml file: ${filePath}`, error); return null; } } /** * Checks if a directory contains Python package files * * @param directoryPath - Path to the directory to check * @returns Boolean indicating if Python package files are present */ function hasPythonPackageFiles(directoryPath) { const pythonFiles = [ 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile', 'poetry.lock', 'uv.lock' ]; return pythonFiles.some(file => fs.existsSync(path.join(directoryPath, file))); } /** * Checks if uvx is available on the system * * @returns Promise resolving to boolean indicating if uvx is available */ async function checkUvxAvailability() { try { await execPromise('uvx --version'); return true; } catch (error) { return false; } } //# sourceMappingURL=python.js.map