UNPKG

@debugmcp/mcp-debugger

Version:

Step-through debugging MCP server for LLMs

136 lines 5.84 kB
/** * Message parsing and validation utilities for DAP Proxy * Pure functions with no side effects for easy testing */ export class MessageParser { /** * Parse and validate a command from the parent process * @throws {Error} if the message is invalid */ static parseCommand(message) { // Handle string messages (from IPC) if (typeof message === 'string') { try { const parsed = JSON.parse(message); return this.parseCommand(parsed); // Recursive call with parsed object } catch (e) { throw new Error(`Failed to parse JSON message: ${e instanceof Error ? e.message : String(e)}`); } } // Validate object structure if (!message || typeof message !== 'object') { throw new Error(`Invalid message type: expected object, got ${typeof message}`); } const obj = message; // Check for required 'cmd' field if (!obj.cmd || typeof obj.cmd !== 'string') { throw new Error(`Missing or invalid 'cmd' field: ${obj.cmd}`); } // Route to specific validators based on command type // Note: sessionId validation is done per command type since terminate can work without it switch (obj.cmd) { case 'init': return this.validateInitPayload(obj); case 'dap': return this.validateDapPayload(obj); case 'terminate': return this.validateTerminatePayload(obj); default: throw new Error(`Unknown command type: ${obj.cmd}`); } } /** * Validate and type-guard an init payload * @throws {Error} if validation fails */ static validateInitPayload(payload) { const obj = payload; // Required string fields const requiredStrings = [ 'sessionId', 'executablePath', 'adapterHost', 'logDir', 'scriptPath' ]; for (const field of requiredStrings) { if (!obj[field] || typeof obj[field] !== 'string') { throw new Error(`Init payload missing or invalid '${field}': ${obj[field]}`); } } // Required number field if (typeof obj.adapterPort !== 'number' || obj.adapterPort <= 0 || obj.adapterPort > 65535) { throw new Error(`Init payload invalid 'adapterPort': ${obj.adapterPort}`); } // Optional fields validation if (obj.scriptArgs !== undefined && !Array.isArray(obj.scriptArgs)) { throw new Error(`Init payload 'scriptArgs' must be an array if provided`); } if (obj.stopOnEntry !== undefined && typeof obj.stopOnEntry !== 'boolean') { throw new Error(`Init payload 'stopOnEntry' must be a boolean if provided`); } if (obj.justMyCode !== undefined && typeof obj.justMyCode !== 'boolean') { throw new Error(`Init payload 'justMyCode' must be a boolean if provided`); } if (obj.dryRunSpawn !== undefined && typeof obj.dryRunSpawn !== 'boolean') { throw new Error(`Init payload 'dryRunSpawn' must be a boolean if provided`); } // Validate initialBreakpoints if provided if (obj.initialBreakpoints !== undefined) { if (!Array.isArray(obj.initialBreakpoints)) { throw new Error(`Init payload 'initialBreakpoints' must be an array if provided`); } for (const bp of obj.initialBreakpoints) { if (!bp || typeof bp !== 'object') { throw new Error(`Invalid breakpoint in initialBreakpoints`); } const bpObj = bp; if (typeof bpObj.file !== 'string' || typeof bpObj.line !== 'number') { throw new Error(`Breakpoint must have 'file' (string) and 'line' (number)`); } if (bpObj.condition !== undefined && typeof bpObj.condition !== 'string') { throw new Error(`Breakpoint 'condition' must be a string if provided`); } } } // Type assertion via unknown to satisfy TypeScript return obj; } /** * Validate and type-guard a DAP command payload * @throws {Error} if validation fails */ static validateDapPayload(payload) { const obj = payload; // Required string fields const requiredStrings = ['sessionId', 'requestId', 'dapCommand']; for (const field of requiredStrings) { if (!obj[field] || typeof obj[field] !== 'string') { throw new Error(`DAP payload missing or invalid '${field}': ${obj[field]}`); } } // dapArgs is optional but commonly used if (obj.dapArgs !== undefined && obj.dapArgs === null) { throw new Error(`DAP payload 'dapArgs' should not be null`); } // Type assertion via unknown to satisfy TypeScript return obj; } /** * Validate and type-guard a terminate payload * @throws {Error} if validation fails */ static validateTerminatePayload(payload) { const obj = payload; // sessionId is preferred but not strictly required for emergency shutdown if (obj.sessionId !== undefined && typeof obj.sessionId !== 'string') { throw new Error(`Terminate payload has invalid 'sessionId' type: ${typeof obj.sessionId}`); } // Type assertion via unknown to satisfy TypeScript return obj; } /** * Helper to check if a message is a string that needs parsing */ static isStringMessage(message) { return typeof message === 'string'; } } //# sourceMappingURL=dap-proxy-message-parser.js.map