fahad-mcp-package
Version:
NPM wrapper to run the My Contextual Demo MCP Server (Python)
125 lines (106 loc) • 6.05 kB
JavaScript
const spawn = require('cross-spawn');
const path = require('path');
const fs = require('fs-extra');
const os = require('os');
const { execSync } = require('child_process');
// --- Configuration for your Python package ---
const PYTHON_PACKAGE_NAME = 'fahad-mcp-server'; // Matches name in your Python setup.py
const PYTHON_PACKAGE_VERSION = '0.1.5'; // Matches version in your Python setup.py (adjust if you've incremented)
const CONSOLE_SCRIPT_NAME = 'fahad_mcp_server'; // Matches name in console_scripts of setup.py
// Determine virtual environment path in user's home directory
const venvDirName = `.${PYTHON_PACKAGE_NAME}-venv`;
const venvPath = path.join(os.homedir(), venvDirName);
// Determine Python and Pip executable names based on OS
const pythonExecutable = process.platform === 'win32' ? 'python.exe' : 'python3';
const pipExecutable = process.platform === 'win32' ? 'pip.exe' : 'pip3';
/**
* Helper function to get the detected Python version.
* @param {string} pythonExec - The name of the Python executable (e.g., 'python', 'python3').
* @returns {object|null} An object {major, minor} or null if version cannot be determined.
*/
function getPythonVersion(pythonExec) {
try {
const versionOutput = execSync(`${pythonExec} --version`, { encoding: 'utf8', stdio: 'pipe' });
const match = versionOutput.match(/Python (\d+)\.(\d+)/);
if (match) {
return { major: parseInt(match[1]), minor: parseInt(match[2]) };
}
} catch (e) {
// Silently fail if python --version command itself fails
}
return null;
}
async function runPythonMCP() {
console.error(`\nSetting up ${PYTHON_PACKAGE_NAME} Python environment...`);
try {
// 1. Check for Python 3 availability on the system
execSync(`${pythonExecutable} --version`, { stdio: 'pipe' });
console.error('Python found on system.');
// NEW FIX: Check the Python version and enforce 3.10 or higher
const pythonVersion = getPythonVersion(pythonExecutable);
if (!pythonVersion || pythonVersion.major < 3 || (pythonVersion.major === 3 && pythonVersion.minor < 10)) {
console.error(`Error: Detected Python version is ${pythonVersion ? `${pythonVersion.major}.${pythonVersion.minor}` : 'unknown'}.`);
console.error(`This MCP server requires Python 3.10 or higher.`);
console.error(`Please ensure 'python' (or 'python.exe' on Windows) in your system's PATH points to Python 3.10 or a newer version.`);
console.error(`You might need to reinstall Python, ensuring "Add Python to PATH" is selected, or manually adjust your PATH environment variable.`);
process.exit(1);
}
console.error(`Detected Python version: ${pythonVersion.major}.${pythonVersion.minor} (compatible).`);
} catch (error) {
console.error('Error: Python not found or not in PATH.');
console.error('Please install Python 3 (Python 3.10 or higher recommended) to run this MCP server.');
process.exit(1);
}
// Determine paths to Python and Pip within the virtual environment
const venvBinPath = process.platform === 'win32' ? path.join(venvPath, 'Scripts') : path.join(venvPath, 'bin');
const venvPython = path.join(venvBinPath, pythonExecutable);
const venvPip = path.join(venvBinPath, pipExecutable);
// 2. Create or ensure virtual environment exists
if (!fs.existsSync(venvPath)) {
console.error(`Creating virtual environment at ${venvPath}...`);
try {
execSync(`${pythonExecutable} -m venv ${venvPath}`, { stdio: 'inherit' });
console.error('Virtual environment created successfully.');
} catch (error) {
console.error(`Error creating virtual environment: ${error.message}`);
process.exit(1);
}
} else {
console.error(`Virtual environment already exists at ${venvPath}.`);
}
// IMPORTANT FIX: Upgrade pip within the virtual environment BEFORE installing other packages
console.error(`Upgrading pip in the virtual environment...`);
try {
execSync(`${venvPython} -m pip install --upgrade pip`, { stdio: 'inherit' });
console.error('pip upgraded successfully.');
} catch (error) {
console.error(`Error upgrading pip: ${error.message}`);
// Do not exit here, but log the error as the installation might still proceed
}
// 3. Install/Upgrade the Python package in the virtual environment
console.error(`Installing/Upgrading ${PYTHON_PACKAGE_NAME}==${PYTHON_PACKAGE_VERSION} in venv...`);
try {
execSync(`${venvPip} install --upgrade ${PYTHON_PACKAGE_NAME}==${PYTHON_PACKAGE_VERSION}`, { stdio: 'inherit' });
console.error(`${PYTHON_PACKAGE_NAME} installed successfully.`);
} catch (error) {
console.error(`Error installing ${PYTHON_PACKAGE_NAME}:`, error.message);
console.error("This might happen if the package isn't yet available on PyPI, there's a network issue, or a core dependency like 'mcp' is still incompatible.");
process.exit(1);
}
// 4. Run the Python MCP server using its console script
console.error(`\nStarting ${PYTHON_PACKAGE_NAME} (Python process)...`);
// The 'CONSOLE_SCRIPT_NAME' executable is located in the venv's bin/Scripts directory
const pythonProcess = spawn(path.join(venvBinPath, CONSOLE_SCRIPT_NAME), process.argv.slice(2), {
stdio: 'inherit', // Connects stdin/stdout/stderr to the parent npx process
});
pythonProcess.on('close', (code) => {
console.error(`\n${PYTHON_PACKAGE_NAME} exited with code ${code}`);
process.exit(code);
});
pythonProcess.on('error', (err) => {
console.error(`\nFailed to start ${PYTHON_PACKAGE_NAME} (Python process): ${err.message}`);
process.exit(1);
});
}
runPythonMCP();