UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

288 lines (287 loc) • 10.2 kB
#!/usr/bin/env node "use strict"; /** * @moduleName: MCP Quiz Server - NPX CLI Tool * @version: 2.0.0 * @since: 2025-07-23 * @lastUpdated: 2025-07-27 * @projectSummary: NPX-installable CLI tool for MCP Quiz Server with web server startup capability and MCP integration * @techStack: TypeScript, Node.js CLI, Commander.js, Express, MCP Protocol * @dependency: commander, express, child_process * @interModuleDependency: ./server, ./mcp-server, ./mcp-handler-enhanced * @requirementsTraceability: * {@link Requirements.REQ_OPS_005} (NPX Tool Integration) * {@link Requirements.REQ_OPS_006} (Command Line Interface) * {@link Requirements.REQ_OPS_001} (Structured Application Logging) * {@link Requirements.REQ_OPS_002} (Health Check Endpoints) * {@link Requirements.REQ_OPS_003} (Deployment Automation) * {@link Requirements.REQ_OPS_007} (Runtime Configuration Management) * @briefDescription: CLI entry point for NPX tool supporting server start/stop, MCP mode, and tool integration. Enables `npx mcp-quiz-server` usage with VS Code integration * @methods: startWebServer, startMCPServer, handleCLICommands, serverControl * @contributors: Jorge Sequeira * @examples: * - npx mcp-quiz-server start * - npx mcp-quiz-server mcp * - npx mcp-quiz-server --help * @vulnerabilitiesAssessment: Process isolation, port binding validation, graceful shutdown handling */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.listServers = listServers; exports.startMCPServer = startMCPServer; exports.startWebServer = startWebServer; exports.stopServers = stopServers; const child_process_1 = require("child_process"); const commander_1 = require("commander"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const program = new commander_1.Command(); let activeServers = []; /** * Start the web server */ async function startWebServer(options) { const port = options.port || 3000; console.log(`šŸš€ Starting MCP Quiz Server on port ${port}...`); try { const serverPath = path.join(__dirname, 'server'); if (!fs.existsSync(serverPath)) { console.error('āŒ Server build not found. Run: npm run build'); process.exit(1); } const serverProcess = (0, child_process_1.spawn)('node', [serverPath], { env: { ...process.env, PORT: port.toString() }, stdio: options.background ? 'pipe' : 'inherit', }); if (options.background) { console.log(`āœ… Web server started in background (PID: ${serverProcess.pid})`); console.log(`🌐 Access at: http://localhost:${port}`); console.log(`šŸ“± API docs: http://localhost:${port}/health`); activeServers.push({ process: serverProcess, port, type: 'web', }); return { pid: serverProcess.pid, port, url: `http://localhost:${port}`, }; } else { console.log(`🌐 Web server starting at: http://localhost:${port}`); console.log('Press Ctrl+C to stop...'); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\nšŸ›‘ Shutting down web server...'); serverProcess.kill('SIGTERM'); process.exit(0); }); serverProcess.on('close', code => { console.log(`\nšŸ Web server exited with code ${code}`); process.exit(code || 0); }); } } catch (error) { console.error('āŒ Failed to start web server:', error); process.exit(1); } } /** * Start the MCP server (STDIO mode) */ async function startMCPServer() { console.log('šŸ¤– Starting MCP Server (STDIO mode)...'); try { const mcpServerPath = path.join(__dirname, 'mcp-server'); if (!fs.existsSync(mcpServerPath)) { console.error('āŒ MCP server build not found. Run: npm run build'); process.exit(1); } const mcpProcess = (0, child_process_1.spawn)('node', [mcpServerPath], { stdio: 'inherit', }); console.log('šŸ“” MCP Server ready for JSON-RPC 2.0 communication'); console.log('šŸ”Œ Use in VS Code: Add to .vscode/mcp.json'); console.log('Press Ctrl+C to stop...'); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\nšŸ›‘ Shutting down MCP server...'); mcpProcess.kill('SIGTERM'); process.exit(0); }); mcpProcess.on('close', code => { console.log(`\nšŸ MCP server exited with code ${code}`); process.exit(code || 0); }); } catch (error) { console.error('āŒ Failed to start MCP server:', error); process.exit(1); } } /** * Stop all running servers */ function stopServers() { console.log('šŸ›‘ Stopping all active servers...'); activeServers.forEach((server, index) => { console.log(` Stopping ${server.type} server on port ${server.port}...`); server.process.kill('SIGTERM'); }); activeServers = []; console.log('āœ… All servers stopped'); } /** * List running servers */ function listServers() { if (activeServers.length === 0) { console.log('šŸ“­ No active servers'); return; } console.log('šŸ“‹ Active Servers:'); activeServers.forEach((server, index) => { console.log(` ${index + 1}. ${server.type} server - Port ${server.port} (PID: ${server.process.pid})`); }); } /** * Show VS Code integration instructions */ function showVSCodeIntegration() { const currentDir = process.cwd(); const mcpPath = path.join(currentDir, 'node_modules', 'mcp-quiz-server', 'dist', 'mcp-server'); console.log('šŸ”§ VS Code MCP Integration Setup:'); console.log(''); console.log('1. Add to your .vscode/mcp.json:'); console.log(''); console.log(JSON.stringify({ servers: { 'quiz-server': { type: 'stdio', command: 'npx', args: ['mcp-quiz-server', 'mcp'], env: { NODE_ENV: 'production', }, }, }, }, null, 2)); console.log(''); console.log('2. Restart VS Code'); console.log('3. The MCP Quiz Server will be available in VS Code chat'); console.log(''); console.log('šŸ› ļø Alternative (if NPX fails):'); console.log(` "command": "node"`); console.log(` "args": ["${mcpPath}"]`); } // CLI Command Definitions program .name('mcp-quiz-server') .description('MCP Quiz Server - NPX CLI Tool for AI-powered quiz management') .version('2.0.0'); program .command('start') .description('Start the web server') .option('-p, --port <number>', 'port number', '3000') .option('-b, --background', 'run in background') .action(async (options) => { await startWebServer({ port: parseInt(options.port), background: options.background, }); }); program .command('mcp') .description('Start the MCP server (STDIO mode for VS Code)') .action(async () => { await startMCPServer(); }); program .command('stop') .description('Stop all running servers') .action(() => { stopServers(); }); program .command('list') .description('List active servers') .action(() => { listServers(); }); program .command('vscode') .description('Show VS Code MCP integration instructions') .action(() => { showVSCodeIntegration(); }); program .command('dev') .description('Start development mode (web server + auto-reload)') .option('-p, --port <number>', 'port number', '3000') .action(async (options) => { console.log('šŸ”„ Starting development mode...'); const devProcess = (0, child_process_1.spawn)('npm', ['run', 'dev'], { env: { ...process.env, PORT: options.port }, stdio: 'inherit', }); console.log('šŸ”§ Development server with auto-reload enabled'); console.log('Press Ctrl+C to stop...'); process.on('SIGINT', () => { console.log('\nšŸ›‘ Stopping development server...'); devProcess.kill('SIGTERM'); process.exit(0); }); devProcess.on('close', code => { console.log(`\nšŸ Development server exited with code ${code}`); process.exit(code || 0); }); }); // Handle no command (show help) if (process.argv.length <= 2) { program.help(); } // Parse CLI arguments program.parse(); // Handle graceful shutdown process.on('SIGINT', () => { stopServers(); process.exit(0); }); process.on('SIGTERM', () => { stopServers(); process.exit(0); });