mcpipe
Version:
Decorate stdio MCP servers with debugging and other capabilities.
142 lines (141 loc) • 5.05 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findEnvFilePath = findEnvFilePath;
exports.loadEnvironmentVariablesFromFile = loadEnvironmentVariablesFromFile;
exports.executeCommand = executeCommand;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const child_process_1 = require("child_process");
const ENV_FILENAME = '.env.mcp';
function reportError(message, error) {
console.error(message);
if (error) {
console.error(error);
}
}
/**
* Finds the closest .env.mcp file by traversing up the directory tree
* @param startDir The directory to start searching from
* @returns The path to the found .env.mcp file or undefined if not found
*/
function findEnvFilePath(startDir = process.cwd()) {
let currentDir = startDir;
// Continue checking until we reach the root directory
while (true) {
const envFilePath = path_1.default.join(currentDir, ENV_FILENAME);
try {
if (fs_1.default.existsSync(envFilePath)) {
return envFilePath;
}
}
catch (err) {
reportError(`Error checking file at ${envFilePath}:`, err);
}
// Move up to the parent directory
const parentDir = path_1.default.dirname(currentDir);
// Stop if we've reached the root directory
if (parentDir === currentDir) {
break;
}
currentDir = parentDir;
}
// Special case: Check for ~/.env.mcp
const homeDir = os_1.default.homedir();
const homeEnvFile = path_1.default.join(homeDir, ENV_FILENAME);
try {
if (fs_1.default.existsSync(homeEnvFile)) {
return homeEnvFile;
}
}
catch (err) {
reportError(`Error checking file at ${homeEnvFile}:`, err);
}
return undefined;
}
/**
* Parses an environment file and returns its contents as key-value pairs
* @param filePath Path to the environment file
* @returns Object containing environment variables
*/
function parseEnvFile(filePath) {
const content = fs_1.default.readFileSync(filePath, 'utf8');
const result = {};
// Split by newlines and process each line
const lines = content.split('\n');
for (const line of lines) {
// Skip empty lines and comments
const trimmedLine = line.trim();
if (!trimmedLine || trimmedLine.startsWith('#')) {
continue;
}
// Parse key=value format
const match = trimmedLine.match(/^([^=]+)=(.*)$/);
if (match) {
const key = match[1].trim();
let value = match[2].trim();
// Remove surrounding quotes if present
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length - 1);
}
result[key] = value;
}
}
return result;
}
/**
* Loads environment variables from a file
* @param filePath Path to the env file
* @returns True if environment variables were loaded, false otherwise
*/
function loadEnvironmentVariablesFromFile(filePath) {
try {
const envVars = parseEnvFile(filePath);
// Add variables to process.env
Object.entries(envVars).forEach(([key, value]) => {
process.env[key] = value;
});
return true;
}
catch (error) {
reportError(`Error loading ${ENV_FILENAME} file:`, error);
return false;
}
}
/**
* Executes a command with the given arguments, inheriting stdio.
* @param command The command to execute
* @param args The arguments for the command
* @returns The spawned ChildProcess
*/
function executeCommand(command, args = []) {
var _a, _b;
const childProcess = (0, child_process_1.spawn)(command, args, {
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin, pipe stdout/stderr
shell: true,
env: process.env
});
// Pipe child stdout/stderr to parent streams
(_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.pipe(process.stdout);
(_b = childProcess.stderr) === null || _b === void 0 ? void 0 : _b.pipe(process.stderr);
// Handle errors during spawn for direct execution as well
childProcess.on('error', (err) => {
console.error(`[mcpipe] Failed to start child process: ${err.message}`);
process.exit(1);
});
// Exit mcpipe when the direct child process exits
childProcess.on('close', (code, signal) => {
if (signal) {
console.warn(`[mcpipe] Child process terminated by signal: ${signal}`);
process.kill(process.pid, signal);
}
else {
process.exit(code !== null && code !== void 0 ? code : 1);
}
});
return childProcess;
}