UNPKG

node-server-orchestrator

Version:

CLI tool for orchestrating Node.js development servers (backend, frontend, databases, etc.)

251 lines • 10.5 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ProjectServerManager } from './ProjectServerManager.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; class ProjectServerMCPServer { server; manager; constructor() { this.server = new Server({ name: 'project-server-manager', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.manager = new ProjectServerManager(); this.setupToolHandlers(); this.setupErrorHandlers(); } setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = [ { name: 'start_server', description: 'Start a project development server', inputSchema: { type: 'object', properties: { server_id: { type: 'string', description: 'Server identifier', }, }, required: ['server_id'], }, }, { name: 'stop_server', description: 'Stop a running server', inputSchema: { type: 'object', properties: { server_id: { type: 'string', description: 'Server identifier', }, }, required: ['server_id'], }, }, { name: 'get_server_status', description: 'Get status of a server', inputSchema: { type: 'object', properties: { server_id: { type: 'string', description: 'Server identifier', }, }, required: ['server_id'], }, }, { name: 'list_servers', description: 'List all available servers', inputSchema: { type: 'object', properties: {}, }, }, { name: 'start_all_servers', description: 'Start all project servers', inputSchema: { type: 'object', properties: {}, }, }, { name: 'stop_all_servers', description: 'Stop all running servers', inputSchema: { type: 'object', properties: {}, }, }, ]; return { tools }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { const serverId = args?.server_id; switch (name) { case 'start_server': const startResult = await this.manager.startServer(serverId); return { content: [ { type: 'text', text: startResult.success ? `āœ… ${startResult.message}\nšŸ“Š PID: ${startResult.pid}\n🌐 Port: ${startResult.port}` : `āŒ ${startResult.message}` }, ], }; case 'stop_server': const stopResult = await this.manager.stopServer(serverId); return { content: [ { type: 'text', text: stopResult.success ? `āœ… ${stopResult.message}` : `āŒ ${stopResult.message}` }, ], }; case 'get_server_status': const status = await this.manager.getServerStatus(serverId); const config = this.manager.getServerConfig(serverId); if (!config) { return { content: [ { type: 'text', text: `āŒ Unknown server: ${serverId}` }, ], }; } return { content: [ { type: 'text', text: this.manager.formatStatusMessage(status, config) }, ], }; case 'list_servers': const servers = this.manager.listServers(); const serverList = servers.map(id => { const config = this.manager.getServerConfig(id); return `${config?.name || id} (${id}) - ${config?.description || 'No description'}`; }).join('\n'); return { content: [ { type: 'text', text: `šŸ“‹ Available Servers:\n${serverList}` }, ], }; case 'start_all_servers': const allResults = await this.manager.startAllServers(); const resultsText = Object.entries(allResults).map(([id, result]) => { const config = this.manager.getServerConfig(id); const status = result.success ? 'āœ…' : 'āŒ'; return `${status} ${config?.name || id}: ${result.message}`; }).join('\n'); return { content: [ { type: 'text', text: `šŸš€ Starting All Servers:\n${resultsText}` }, ], }; case 'stop_all_servers': const stopAllResults = await this.manager.stopAllServers(); const stopAllText = Object.entries(stopAllResults).map(([id, result]) => { const config = this.manager.getServerConfig(id); const status = result.success ? 'āœ…' : 'āŒ'; return `${status} ${config?.name || id}: ${result.message}`; }).join('\n'); return { content: [ { type: 'text', text: `šŸ›‘ Stopping All Servers:\n${stopAllText}` }, ], }; default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { // Sanitize error messages to prevent information leakage const sanitizedMessage = this.sanitizeErrorMessage(error.message); return { content: [ { type: 'text', text: `āŒ Error: ${sanitizedMessage}` }, ], isError: true, }; } }); } sanitizeErrorMessage(errorMessage) { // Remove sensitive information from error messages const sensitivePatterns = [ /\/home\/[^\s]+/g, // Home directory paths /\/usr\/[^\s]+/g, // System paths /\/etc\/[^\s]+/g, // Configuration paths /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/g, // IP addresses ]; let sanitized = errorMessage; for (const pattern of sensitivePatterns) { sanitized = sanitized.replace(pattern, '[REDACTED]'); } return sanitized; } setupErrorHandlers() { this.server.onerror = (error) => { console.error('[MCP Server Error]', error); }; process.on('SIGINT', async () => { await this.manager.stopAllServers(); process.exit(0); }); process.on('SIGTERM', async () => { await this.manager.stopAllServers(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Project Server MCP server running on stdio'); } } // Start the server if this file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const server = new ProjectServerMCPServer(); server.run().catch((error) => { console.error('Failed to start MCP server:', error); process.exit(1); }); } export { ProjectServerMCPServer }; //# sourceMappingURL=index.js.map