@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
JavaScript
/**
* 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);
}
}