halton-iot-mcp-setup
Version:
One-command setup for Halton IoT MCP Server in VS Code
234 lines (204 loc) ⢠6.82 kB
JavaScript
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);
});