hotel-mcp
Version:
Hotel MCP Server - Read-only hotel information with rich media support for Claude Desktop
289 lines (239 loc) ⢠8.6 kB
JavaScript
/**
* Hotel MCP Server - NPX Launcher
*
* Intelligent launcher that detects environment and runs the Hotel MCP server
* with optimal configuration for Claude Desktop integration.
*/
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
// ANSI color codes for beautiful output
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m'
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function findProjectRoot() {
let currentDir = __dirname;
// Go up from bin/ to project root
const projectRoot = path.dirname(currentDir);
// Verify it's the correct project by checking for hotel_mcp.py
const serverFile = path.join(projectRoot, 'hotel_mcp.py');
if (fs.existsSync(serverFile)) {
return projectRoot;
}
// If not found, try current working directory
const cwd = process.cwd();
const cwdServerFile = path.join(cwd, 'hotel_mcp.py');
if (fs.existsSync(cwdServerFile)) {
return cwd;
}
throw new Error('Hotel MCP server file not found. Please run from project directory.');
}
function checkPythonEnvironment(projectRoot) {
const uvLockFile = path.join(projectRoot, 'uv.lock');
const pyprojectFile = path.join(projectRoot, 'pyproject.toml');
if (fs.existsSync(uvLockFile) && fs.existsSync(pyprojectFile)) {
return 'uv';
}
// Check for other Python environments
const venvDirs = ['.venv', 'venv', '.env'];
for (const venvDir of venvDirs) {
const venvPath = path.join(projectRoot, venvDir);
if (fs.existsSync(venvPath)) {
return 'venv';
}
}
return 'system';
}
function installDependencies(projectRoot, pythonEnv) {
return new Promise((resolve, reject) => {
log('š¦ Installing Python dependencies...', 'yellow');
let command, args;
switch (pythonEnv) {
case 'uv':
command = 'uv';
args = ['sync'];
break;
case 'system':
default:
command = 'pip';
args = ['install', '-r', path.join(projectRoot, 'requirements.txt')];
break;
}
const child = spawn(command, args, {
cwd: projectRoot,
stdio: ['inherit', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
log('ā
Dependencies installed successfully', 'green');
resolve();
} else {
log('ā Failed to install dependencies', 'red');
if (stderr) log(stderr, 'red');
reject(new Error(`Dependency installation failed with code ${code}`));
}
});
child.on('error', (error) => {
reject(error);
});
});
}
function buildCommand(projectRoot, pythonEnv) {
const serverFile = path.join(projectRoot, 'hotel_mcp.py');
switch (pythonEnv) {
case 'uv':
return {
command: 'uv',
args: ['run', 'python', serverFile],
cwd: projectRoot
};
case 'venv':
const isWindows = os.platform() === 'win32';
const pythonBin = isWindows ?
path.join(projectRoot, '.venv', 'Scripts', 'python.exe') :
path.join(projectRoot, '.venv', 'bin', 'python');
if (fs.existsSync(pythonBin)) {
return {
command: pythonBin,
args: [serverFile],
cwd: projectRoot
};
}
// Fall through to system python
case 'system':
default:
return {
command: 'python3',
args: [serverFile],
cwd: projectRoot
};
}
}
function loadEnvironment(projectRoot) {
const envFile = path.join(projectRoot, '.env');
const env = { ...process.env };
if (fs.existsSync(envFile)) {
const envContent = fs.readFileSync(envFile, 'utf8');
const envLines = envContent.split('\n');
for (const line of envLines) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
const [key, ...valueParts] = trimmed.split('=');
if (key && valueParts.length > 0) {
env[key.trim()] = valueParts.join('=').trim();
}
}
}
}
return env;
}
function displayHeader() {
log('šØ Hotel MCP Server', 'cyan');
log('ā'.repeat(50), 'blue');
log('š Starting intelligent MCP server for Claude Desktop', 'green');
log('');
}
function displayEnvironmentInfo(projectRoot, pythonEnv) {
log('š Environment Detection:', 'yellow');
log(` š Project Root: ${projectRoot}`, 'reset');
log(` š Python Environment: ${pythonEnv}`, 'reset');
log(` š» Platform: ${os.platform()} ${os.arch()}`, 'reset');
log('');
}
async function main() {
try {
// Handle help flag
if (process.argv.includes('--help') || process.argv.includes('-h')) {
displayHeader();
log('š Usage:', 'yellow');
log(' npx hotel-mcp # Start the MCP server', 'reset');
log(' npx hotel-mcp --help # Show this help', 'reset');
log('', 'reset');
log('š Documentation: https://github.com/hotel-mcp/hotel-mcp#readme', 'reset');
return;
}
displayHeader();
// Find project root and detect environment
const projectRoot = findProjectRoot();
const pythonEnv = checkPythonEnvironment(projectRoot);
displayEnvironmentInfo(projectRoot, pythonEnv);
// Install dependencies if needed
try {
await installDependencies(projectRoot, pythonEnv);
} catch (error) {
log(`ā ļø Warning: Could not install dependencies: ${error.message}`, 'yellow');
log(' Continuing with existing environment...', 'yellow');
}
// Build command
const { command, args, cwd } = buildCommand(projectRoot, pythonEnv);
// Load environment variables
const env = loadEnvironment(projectRoot);
log('š§ Starting server...', 'green');
log(` Command: ${command} ${args.join(' ')}`, 'reset');
log('');
// Spawn the Python process
const child = spawn(command, args, {
cwd,
env,
stdio: 'inherit'
});
// Handle process events
child.on('error', (error) => {
log(`ā Failed to start server: ${error.message}`, 'red');
process.exit(1);
});
child.on('exit', (code, signal) => {
if (signal) {
log(`\nš Server stopped by signal: ${signal}`, 'yellow');
} else if (code !== 0) {
log(`\nā Server exited with code: ${code}`, 'red');
process.exit(code);
} else {
log('\nš Server stopped gracefully', 'green');
}
});
// Handle Ctrl+C gracefully
process.on('SIGINT', () => {
log('\nš Received SIGINT, stopping server...', 'yellow');
child.kill('SIGINT');
});
process.on('SIGTERM', () => {
log('\nš Received SIGTERM, stopping server...', 'yellow');
child.kill('SIGTERM');
});
} catch (error) {
log(`ā Error: ${error.message}`, 'red');
log('', 'reset');
log('š” Troubleshooting:', 'yellow');
log(' 1. Ensure you have Python 3.10+ installed', 'reset');
log(' 2. Run from the hotel-mcp project directory', 'reset');
log(' 3. Install dependencies: uv sync', 'reset');
log(' 4. Configure .env file with Supabase credentials', 'reset');
process.exit(1);
}
}
// Run the launcher
if (require.main === module) {
main();
}
module.exports = { main };