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.

148 lines (147 loc) • 5.64 kB
"use strict"; /** * @moduleName: STDIO Transport Implementation * @version: 2.0.0 * @since: 2025-07-25 * @lastUpdated: 2025-07-25 * @projectSummary: Enhanced MCP Quiz Server - STDIO Transport Implementation * @techStack: TypeScript, Node.js STDIO, JSON-RPC 2.0, MCP Protocol * @dependency: Node.js process module * @interModuleDependency: ./transport-abstraction, ../types/mcp-types * @requirementsTraceability: * {@link Requirements.REQ_MCP_001} (JSON-RPC 2.0 MCP Protocol) * @briefDescription: STDIO transport implementation for VS Code integration and local development * @methods: initialize, send, onMessage, close, processStdin * @contributors: GitHub Copilot, VS Code Integration Team * @examples: * - VS Code MCP server integration * - Local development testing * @vulnerabilitiesAssessment: Process isolation, no network exposure, input validation for JSON-RPC */ Object.defineProperty(exports, "__esModule", { value: true }); exports.StdioTransport = void 0; const transport_abstraction_1 = require("./transport-abstraction"); /** * STDIO transport implementation for VS Code and local development */ class StdioTransport extends transport_abstraction_1.BaseTransport { constructor(config) { super(); this.name = 'stdio'; this.version = '1.0.0'; this.capabilities = transport_abstraction_1.TRANSPORT_CAPABILITIES[transport_abstraction_1.TransportType.STDIO]; this.inputBuffer = ''; this.isInitialized = false; this.encoding = 'utf8'; this.encoding = (config === null || config === void 0 ? void 0 : config.encoding) || 'utf8'; } async initialize() { if (this.isInitialized) { return; } console.error('🔌 Initializing STDIO transport...'); // Set up STDIN processing process.stdin.setEncoding(this.encoding); process.stdin.on('data', this.handleStdinData.bind(this)); process.stdin.on('end', this.handleStdinEnd.bind(this)); // Set up process signal handlers process.on('SIGINT', this.handleShutdown.bind(this)); process.on('SIGTERM', this.handleShutdown.bind(this)); this.isInitialized = true; this.metrics.currentConnections = 1; this.emit('onConnect'); console.error('✅ STDIO transport ready for JSON-RPC 2.0 communication'); } async send(response) { if (!this.isInitialized) { throw new Error('STDIO transport not initialized'); } try { const jsonMessage = JSON.stringify(response); process.stdout.write(jsonMessage + '\n'); this.metrics.messagesSent++; } catch (error) { this.metrics.errors++; throw new Error(`Failed to send STDIO message: ${error}`); } } async close() { if (!this.isInitialized) { return; } console.error('🛑 Closing STDIO transport...'); // Clean up event listeners process.stdin.removeAllListeners('data'); process.stdin.removeAllListeners('end'); this.isInitialized = false; this.metrics.currentConnections = 0; this.emit('onDisconnect', 'Manual close'); } isReady() { return this.isInitialized; } async handleStdinData(chunk) { this.inputBuffer += chunk; // Process complete JSON-RPC messages (delimited by newlines) const lines = this.inputBuffer.split('\n'); this.inputBuffer = lines.pop() || ''; // Keep incomplete line in buffer for (const line of lines) { if (line.trim()) { await this.processJsonRpcMessage(line.trim()); } } } async processJsonRpcMessage(message) { try { const request = JSON.parse(message); // Validate JSON-RPC 2.0 format if (request.jsonrpc !== '2.0') { await this.sendErrorResponse(null, -32600, 'Invalid Request - must be JSON-RPC 2.0'); return; } if (!request.method) { await this.sendErrorResponse(request.id, -32600, 'Invalid Request - missing method'); return; } // Create transport metadata const metadata = { transportType: transport_abstraction_1.TransportType.STDIO, timestamp: new Date().toISOString(), messageId: `stdio-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, encoding: this.encoding, }; // Process message through base transport const response = await this.processMessage(request, metadata); await this.send(response); } catch (error) { this.metrics.errors++; await this.sendErrorResponse(null, -32700, 'Parse error', error instanceof Error ? error.message : String(error)); } } async sendErrorResponse(id, code, message, data) { const errorResponse = { jsonrpc: '2.0', id, error: { code, message, data, }, }; await this.send(errorResponse); } handleStdinEnd() { console.error('📪 STDIN closed, shutting down...'); this.emit('onDisconnect', 'STDIN closed'); process.exit(0); } handleShutdown() { console.error('🔄 Received shutdown signal...'); this.close().then(() => { process.exit(0); }); } } exports.StdioTransport = StdioTransport;