Skip to main content
Glama
response-parser.ts8.76 kB
import { MCPResponse, ErrorCodes } from '../types/index.js'; /** * Response parser for FiveM RCON commands */ export class ResponseParser { /** * Parse RCON response and determine if it was successful */ static parseRCONResponse(response: string, command: string): MCPResponse { const trimmedResponse = response.trim(); console.log(`[DEBUG] Parsing response for command: ${command}`); console.log(`[DEBUG] Response: ${trimmedResponse}`); // Check for common error patterns if (this.isErrorResponse(trimmedResponse)) { console.log(`[DEBUG] Detected error response`); return this.createErrorResponse(trimmedResponse, command); } // Check for plugin-specific responses if (this.isPluginResponse(trimmedResponse)) { console.log(`[DEBUG] Detected plugin response`); return this.parsePluginResponse(trimmedResponse, command); } console.log(`[DEBUG] Using default success case`); // Default success case return { success: true, message: 'Command executed successfully', data: { response: trimmedResponse, command } }; } /** * Check if response indicates an error */ private static isErrorResponse(response: string): boolean { const errorPatterns = [ /^No such command/i, /^Unknown command/i, /^Command not found/i, /^Error:/i, /^script error/i, /^Failed to/i, /^Cannot/i, /^Invalid/i, /^Permission denied/i, /^Access denied/i, /^Timeout/i, /^Connection failed/i, /^Resource .* not found/i, /^Plugin .* not found/i, /argument.*null/i, /^nil$/i, /^false$/i ]; return errorPatterns.some(pattern => pattern.test(response)); } /** * Check if response is from a plugin (mcp-bridge) */ private static isPluginResponse(response: string): boolean { // Check for JSON response format or plugin-specific patterns return response.includes('{"data":') || response.includes('[MCP-Bridge]') || response.includes('"success":'); } /** * Parse plugin-specific responses */ private static parsePluginResponse(response: string, command: string): MCPResponse { try { console.log(`[DEBUG] Parsing plugin response`); // Remove "print" prefix from response for cleaner parsing const cleanResponse = response.replace(/^print\s+/i, '').trim(); // Check for command execution failures before JSON parsing if (cleanResponse.includes('No such command') || cleanResponse.includes('Unknown command') || cleanResponse.includes('Command not found')) { console.log(`[DEBUG] Found command failure pattern in plugin response`); return this.createErrorResponse(response, command); } // Try to extract JSON from response const jsonMatch = cleanResponse.match(/\{.*\}/); if (jsonMatch) { console.log(`[DEBUG] Found JSON in plugin response: ${jsonMatch[0]}`); try { const jsonData = JSON.parse(jsonMatch[0]); // For plugin responses with JSON, trust the plugin's success field // Only check for explicit command failure messages const hasCommandFailure = cleanResponse.includes('No such command') || cleanResponse.includes('Unknown command') || cleanResponse.includes('Command not found'); console.log(`[DEBUG] Plugin JSON success: ${jsonData.success}, Has command failure: ${hasCommandFailure}`); // Use plugin's success field unless there's an explicit command failure const actualSuccess = !hasCommandFailure && (jsonData.success !== false); console.log(`[DEBUG] Actual success determined: ${actualSuccess}`); return { success: actualSuccess, message: actualSuccess ? (jsonData.message || 'Plugin command executed successfully') : 'Command execution failed', data: jsonData.data || { response, command }, error: !actualSuccess ? { code: this.determineErrorCode(response), message: this.extractErrorMessage(response), details: { response, command } } : undefined }; } catch (parseError) { console.log(`[DEBUG] JSON parse error: ${parseError}`); // If JSON parsing fails but we found JSON-like content, assume success return { success: true, message: 'Plugin command executed (JSON parse error)', data: { response, command } }; } } // Check for plugin error indicators if (response.includes('[ERROR]') || response.includes('ERROR:')) { console.log(`[DEBUG] Found ERROR indicator in plugin response`); return this.createErrorResponse(response, command); } // Check for command failures in the output if (this.isErrorResponse(response)) { console.log(`[DEBUG] Error response detected in plugin output`); return this.createErrorResponse(response, command); } console.log(`[DEBUG] Default plugin success`); // Default plugin success return { success: true, message: 'Plugin command executed successfully', data: { response, command } }; } catch (error) { console.log(`[DEBUG] Exception in plugin parsing: ${error}`); // If JSON parsing fails, check for errors in raw response if (this.isErrorResponse(response)) { return this.createErrorResponse(response, command); } return { success: true, message: 'Plugin command executed (non-JSON response)', data: { response, command } }; } } /** * Create standardized error response */ private static createErrorResponse(response: string, command: string): MCPResponse { const errorCode = this.determineErrorCode(response); const errorMessage = this.extractErrorMessage(response); return { success: false, message: errorMessage, error: { code: errorCode, message: errorMessage, details: { response, command } } }; } /** * Determine error code based on response */ private static determineErrorCode(response: string): string { if (/No such command|Unknown command|Command not found/i.test(response)) { return ErrorCodes.INVALID_COMMAND; } if (/Permission denied|Access denied/i.test(response)) { return ErrorCodes.PERMISSION_DENIED; } if (/Timeout/i.test(response)) { return ErrorCodes.TIMEOUT; } if (/Connection failed/i.test(response)) { return ErrorCodes.CONNECTION_FAILED; } if (/Resource.*not found|Plugin.*not found/i.test(response)) { return ErrorCodes.RESOURCE_NOT_FOUND; } if (/Invalid|argument.*null/i.test(response)) { return ErrorCodes.INVALID_ARGUMENTS; } if (/script error|Failed to/i.test(response)) { return ErrorCodes.COMMAND_FAILED; } return ErrorCodes.UNKNOWN_ERROR; } /** * Extract meaningful error message from response */ private static extractErrorMessage(response: string): string { // Clean up common FiveM error prefixes let message = response .replace(/^print\s+/i, '') .replace(/^script error in native [0-9a-f]+:\s*/i, '') .trim(); // Capitalize first letter if (message.length > 0) { message = message.charAt(0).toUpperCase() + message.slice(1); } return message || 'Unknown error occurred'; } /** * Validate command before execution */ static validateCommand(command: string): MCPResponse | null { if (!command || command.trim().length === 0) { return { success: false, message: 'Command cannot be empty', error: { code: ErrorCodes.INVALID_ARGUMENTS, message: 'Command cannot be empty' } }; } // Check for potentially dangerous commands (basic security) const dangerousPatterns = [ /rm\s+-rf/i, /del\s+\/[sq]/i, /format\s+/i, /shutdown/i, /reboot/i ]; if (dangerousPatterns.some(pattern => pattern.test(command))) { return { success: false, message: 'Command contains potentially dangerous operations', error: { code: ErrorCodes.PERMISSION_DENIED, message: 'Command contains potentially dangerous operations' } }; } return null; // Valid command } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eeharumt/fivem-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server