UNPKG

halton-iot-mcp-setup

Version:

One-command setup for Halton IoT MCP Server in VS Code

234 lines (204 loc) • 6.82 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const readline = require('readline'); const os = require('os'); const { execSync, spawn } = require('child_process'); const MCP_IMAGE = 'ghcr.io/haltongroup/haltoniotmcp:latest'; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function prompt(question) { return new Promise((resolve) => { rl.question(question, resolve); }); } function isDockerInstalled() { try { execSync('docker --version', { stdio: 'pipe' }); return true; } catch { return false; } } function isDockerRunning() { try { execSync('docker info', { stdio: 'pipe', timeout: 10000 }); return true; } catch { return false; } } async function startDocker() { const platform = os.platform(); console.log('🐳 Starting Docker Desktop...'); try { if (platform === 'win32') { const paths = [ 'C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe', path.join(os.homedir(), 'AppData\\Local\\Docker\\Docker Desktop.exe') ]; for (const p of paths) { if (fs.existsSync(p)) { spawn(p, [], { detached: true, stdio: 'ignore' }).unref(); break; } } } else if (platform === 'darwin') { execSync('open -a Docker', { stdio: 'pipe' }); } else { execSync('systemctl start docker', { stdio: 'pipe' }); } } catch { // Ignore errors, we'll check if it started below } console.log('ā³ Waiting for Docker to start (this may take a minute)...'); for (let i = 0; i < 30; i++) { await new Promise(r => setTimeout(r, 2000)); if (isDockerRunning()) { console.log('āœ… Docker is running!'); return true; } process.stdout.write('.'); } return false; } async function pullImage() { console.log(`\nšŸ“¦ Pulling Halton IoT MCP image...`); try { execSync(`docker pull ${MCP_IMAGE}`, { stdio: 'inherit' }); console.log('āœ… Image pulled successfully!'); return true; } catch (err) { console.log('āŒ Failed to pull image'); return false; } } function getVSCodeMcpConfigPath() { const platform = os.platform(); const home = os.homedir(); if (platform === 'win32') { return path.join(home, 'AppData', 'Roaming', 'Code', 'User', 'mcp.json'); } else if (platform === 'darwin') { return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'mcp.json'); } else { return path.join(home, '.config', 'Code', 'User', 'mcp.json'); } } function readJsonFile(filePath) { try { if (fs.existsSync(filePath)) { const content = fs.readFileSync(filePath, 'utf8'); const jsonContent = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''); return JSON.parse(jsonContent); } } catch (err) { // Ignore errors } return null; } function getExistingToken(config) { try { if (config?.servers?.['halton-iot']?.env?.HALTON_API_TOKEN) { return config.servers['halton-iot'].env.HALTON_API_TOKEN; } } catch (err) { // Ignore errors } return null; } async function main() { console.log('\nšŸ”§ Halton IoT MCP Server Setup\n'); console.log('This will configure VS Code to use the Halton IoT MCP server.\n'); // Check Docker installation if (!isDockerInstalled()) { console.log('āŒ Docker is not installed.'); console.log(' Please install Docker Desktop from: https://www.docker.com/products/docker-desktop/'); rl.close(); process.exit(1); } // Check if Docker is running, start if not if (!isDockerRunning()) { console.log('āš ļø Docker is not running.'); const started = await startDocker(); if (!started) { console.log('\nāŒ Could not start Docker automatically.'); console.log(' Please start Docker Desktop manually and run this setup again.'); rl.close(); process.exit(1); } } else { console.log('āœ… Docker is running'); } // Pull the image const pulled = await pullImage(); if (!pulled) { console.log('āŒ Could not pull the Docker image. Check your internet connection.'); rl.close(); process.exit(1); } // Check for existing token const mcpConfigPath = getVSCodeMcpConfigPath(); const existingConfig = readJsonFile(mcpConfigPath); const existingToken = getExistingToken(existingConfig); let token; if (existingToken) { const maskedToken = existingToken.substring(0, 8) + '...' + existingToken.substring(existingToken.length - 4); console.log(`\nFound existing API token: ${maskedToken}`); const useExisting = await prompt('Use existing token? (Y/n): '); if (useExisting.toLowerCase() !== 'n') { token = existingToken; console.log('āœ… Using existing token\n'); } else { console.log('\nYou need an API token from https://api.halton.io\n'); token = await prompt('Paste your new Halton API token: '); } } else { console.log('\nYou need an API token from https://api.halton.io\n'); token = await prompt('Paste your Halton API token: '); } if (!token || token.trim().length < 10) { console.log('\nāŒ Invalid token. Please get your token from https://api.halton.io\n'); rl.close(); process.exit(1); } let mcpConfig = existingConfig || { servers: {} }; if (!mcpConfig.servers) { mcpConfig.servers = {}; } // Add or update halton-iot server mcpConfig.servers['halton-iot'] = { type: 'stdio', command: 'docker', args: ['run', '-i', '--rm', '-e', 'HALTON_API_TOKEN', MCP_IMAGE], env: { HALTON_API_TOKEN: token.trim() } }; // Write MCP config try { const dir = path.dirname(mcpConfigPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2)); console.log('\nāœ… VS Code MCP configured successfully!\n'); console.log('Next steps:'); console.log(' 1. Restart VS Code'); console.log(' 2. Open GitHub Copilot chat'); console.log(' 3. Ask: "What Halton systems do I have access to?"\n'); console.log('To update your token later, run this setup again:'); console.log(' npx halton-iot-mcp-setup\n'); } catch (err) { console.log('\nāŒ Could not write MCP config file:', err.message); console.log('\nManual setup: Create/edit mcp.json with:\n'); console.log(JSON.stringify(mcpConfig, null, 2)); } rl.close(); } main().catch(err => { console.error('Error:', err); rl.close(); process.exit(1); });