UNPKG

@genxis/whmrockstar

Version:

🎸 GenXis WHMRockStar - AI-powered multi-server WHM/cPanel management via Model Context Protocol (MCP). Enhanced with proper MCP stdio protocol support and multi-server management.

383 lines (346 loc) • 11.6 kB
#!/usr/bin/env node /** * GenXis WHMRockStar - AI-powered WHM/cPanel management via MCP * Clean version for NPM publication - no hardcoded credentials */ const express = require('express'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); const WHMService = require('./lib/whm-service'); const logger = require('./lib/logger'); class GenXisWHMRockStar { constructor(options = {}) { this.config = this.loadConfig(options); this.app = express(); this.setupMiddleware(); this.setupRoutes(); this.whmService = new WHMService(this.config); this.clients = new Map(); this.clientIdCounter = 0; } loadConfig(options) { // Load from options, environment, or config file const config = { server: options.server || process.env.WHM_SERVER, apiToken: options.apiToken || process.env.WHM_API_TOKEN, port: options.port || process.env.WHM_PORT || '2087', mcpPort: options.mcpPort || process.env.MCP_PORT || 3000, timeout: options.timeout || 30000 }; // Try to load from config file if no options provided if (!config.server || !config.apiToken) { const configPath = path.join(process.env.HOME || process.env.USERPROFILE, '.genxis-whmrockstar', 'config.json'); if (fs.existsSync(configPath)) { const fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')); Object.assign(config, fileConfig); } } if (!config.server || !config.apiToken) { throw new Error('WHM server and API token are required. Run "genxis-whm setup" first.'); } return config; } setupMiddleware() { this.app.use(cors()); this.app.use(express.json()); // Request logging this.app.use((req, res, next) => { logger.info(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); } setupRoutes() { // Health check this.app.get('/', (req, res) => { res.json({ name: 'GenXis WHMRockStar', version: require('./package.json').version, status: 'active', mcp: 'enabled' }); }); // SSE endpoint for MCP this.app.get('/sse', this.handleSSE.bind(this)); // MCP messages endpoint this.app.post('/messages', this.handleMCPMessage.bind(this)); } handleSSE(req, res) { const clientId = (++this.clientIdCounter).toString(); logger.info(`SSE connection request from client ${clientId}`); // Set SSE headers res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // Send initial connection message const initialMessage = { jsonrpc: "2.0", id: clientId, method: "connection/ready", params: { status: "connected", clientId: clientId, timestamp: new Date().toISOString() } }; res.write(`data: ${JSON.stringify(initialMessage)}\n\n`); // Send tools ready notification setTimeout(() => { const toolsNotification = { jsonrpc: "2.0", method: "tools/ready", params: {} }; res.write(`data: ${JSON.stringify(toolsNotification)}\n\n`); }, 100); // Store client connection this.clients.set(clientId, res); // Handle client disconnect req.on('close', () => { logger.info(`SSE connection closed for client ${clientId}`); this.clients.delete(clientId); }); } async handleMCPMessage(req, res) { try { const { id, method, params } = req.body; logger.info(`MCP Message: ${method}`); if (!id || !method) { return res.status(400).json({ jsonrpc: "2.0", id: null, error: { code: -32600, message: "Invalid Request", data: "Missing required fields (id, method)" } }); } if (method === "tools/list") { return res.json({ jsonrpc: "2.0", id, result: { tools: this.getAvailableTools() } }); } if (method === "tools/call") { const result = await this.handleToolCall(params); return res.json({ jsonrpc: "2.0", id, result }); } // Unknown method return res.status(404).json({ jsonrpc: "2.0", id, error: { code: -32601, message: "Method not found", data: `Method '${method}' is not supported` } }); } catch (error) { logger.error(`MCP Error: ${error.message}`); return res.status(500).json({ jsonrpc: "2.0", id: req.body.id || null, error: { code: -32603, message: "Internal error", data: error.message } }); } } getAvailableTools() { return [ { name: "whm_list_accounts", description: "List all WHM accounts on the server", parameters: {} }, { name: "whm_create_account", description: "Create a new cPanel account", parameters: { username: { type: "string", description: "cPanel username (8 chars max)" }, domain: { type: "string", description: "Primary domain name" }, password: { type: "string", description: "Account password" }, email: { type: "string", description: "Contact email" }, package: { type: "string", description: "Hosting package name" } } }, { name: "whm_account_summary", description: "Get account summary information", parameters: { username: { type: "string", description: "cPanel username" } } }, { name: "whm_suspend_account", description: "Suspend a cPanel account", parameters: { username: { type: "string", description: "cPanel username" }, reason: { type: "string", description: "Suspension reason" } } }, { name: "whm_unsuspend_account", description: "Unsuspend a cPanel account", parameters: { username: { type: "string", description: "cPanel username" } } }, { name: "whm_terminate_account", description: "Permanently remove a cPanel account", parameters: { username: { type: "string", description: "cPanel username" } } }, { name: "whm_server_status", description: "Get server status and system statistics", parameters: {} }, { name: "whm_server_load", description: "Get server load statistics", parameters: {} }, { name: "whm_service_status", description: "Get status of server services", parameters: {} }, { name: "whm_restart_service", description: "Restart a specific server service", parameters: { service: { type: "string", description: "Service name (httpd, mysql, nginx)" } } }, { name: "whm_check_updates", description: "Check for available server updates", parameters: {} }, { name: "whm_start_update", description: "Start server update process", parameters: {} }, { name: "whm_list_domains", description: "List domains for a cPanel account", parameters: { username: { type: "string", description: "cPanel username" } } }, { name: "whm_add_domain", description: "Add domain to a cPanel account", parameters: { username: { type: "string", description: "cPanel username" }, domain: { type: "string", description: "Domain name to add" } } }, { name: "whm_delete_domain", description: "Remove domain from a cPanel account", parameters: { username: { type: "string", description: "cPanel username" }, domain: { type: "string", description: "Domain name to remove" } } } ]; } async handleToolCall(params) { const { name, arguments: args } = params; if (!name.startsWith("whm_")) { throw new Error(`Unknown tool: ${name}`); } try { let result; switch (name) { case "whm_list_accounts": result = await this.whmService.listAccounts(); break; case "whm_create_account": result = await this.whmService.createAccount(args); break; case "whm_account_summary": result = await this.whmService.getAccountSummary(args.username); break; case "whm_suspend_account": result = await this.whmService.suspendAccount(args.username, args.reason); break; case "whm_unsuspend_account": result = await this.whmService.unsuspendAccount(args.username); break; case "whm_terminate_account": result = await this.whmService.terminateAccount(args.username); break; case "whm_server_status": result = await this.whmService.getServerStatus(); break; case "whm_server_load": result = await this.whmService.getServerLoad(); break; case "whm_service_status": result = await this.whmService.getServiceStatus(); break; case "whm_restart_service": result = await this.whmService.restartService(args.service); break; case "whm_check_updates": result = await this.whmService.checkForUpdates(); break; case "whm_start_update": result = await this.whmService.startUpdate(); break; case "whm_list_domains": result = await this.whmService.listDomains(args.username); break; case "whm_add_domain": result = await this.whmService.addDomain(args.username, args.domain); break; case "whm_delete_domain": result = await this.whmService.deleteDomain(args.username, args.domain); break; default: throw new Error(`Tool '${name}' is not implemented`); } return result; } catch (error) { logger.error(`Tool execution error: ${error.message}`); throw error; } } start() { const port = this.config.mcpPort; this.app.listen(port, () => { logger.info(`GenXis WHMRockStar MCP Server running on http://localhost:${port}/`); logger.info(`SSE endpoint: http://localhost:${port}/sse`); logger.info(`Connected to WHM server: ${this.config.server}`); }); } } // Export for programmatic use module.exports = GenXisWHMRockStar; // CLI usage if (require.main === module) { try { const server = new GenXisWHMRockStar(); server.start(); } catch (error) { console.error('Failed to start GenXis WHMRockStar:', error.message); console.error('Run "genxis-whm setup" to configure your server.'); process.exit(1); } }