UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

475 lines 20 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Logger = exports.Document = exports.IL2CPPVectorStore = void 0; exports.initializeServer = initializeServer; exports.initializeVectorStore = initializeVectorStore; exports.startMcpStdioServer = startMcpStdioServer; exports.startMcpServerWithTransport = startMcpServerWithTransport; exports.startMcpServer = startMcpServer; exports.startMcpServerStdio = startMcpServerStdio; exports.getServerStatus = getServerStatus; exports.shutdown = shutdown; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const path_1 = __importDefault(require("path")); // Import transport infrastructure const transport_1 = require("../transport"); // Import IL2CPP components const indexer_1 = require("../indexer/indexer"); const vector_store_1 = require("../embeddings/vector-store"); Object.defineProperty(exports, "IL2CPPVectorStore", { enumerable: true, get: function () { return vector_store_1.IL2CPPVectorStore; } }); const documents_1 = require("@langchain/core/documents"); Object.defineProperty(exports, "Document", { enumerable: true, get: function () { return documents_1.Document; } }); // Import error types const error_types_1 = require("./error-types"); // ============================================================================ // GLOBAL STATE MANAGEMENT // ============================================================================ // Global vector store instance let vectorStore = null; // Server configuration let serverConfig = {}; // Initialization state let isInitialized = false; // Logging configuration let logLevel = process.env.LOG_LEVEL || 'info'; // Add global error handlers to prevent crashes process.on('uncaughtException', (error) => { console.error('[FATAL] Uncaught Exception:', error); console.error('Stack:', error.stack); }); process.on('unhandledRejection', (reason, promise) => { console.error('[FATAL] Unhandled Rejection at:', promise, 'reason:', reason); }); // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ /** * Safely stringify JSON with error handling */ function safeJsonStringify(obj, space) { try { return JSON.stringify(obj, null, space); } catch (error) { Logger.error('JSON stringify error:', error); return JSON.stringify({ error: 'Failed to serialize response', message: error instanceof Error ? error.message : 'Unknown serialization error' }, null, space); } } // ============================================================================ // LOGGING UTILITIES // ============================================================================ /** * Enhanced logging utility */ class Logger { static shouldLog(level) { const levels = ['debug', 'info', 'warn', 'error']; const currentLevelIndex = levels.indexOf(logLevel); const messageLevelIndex = levels.indexOf(level); return messageLevelIndex >= currentLevelIndex; } static debug(message, ...args) { if (this.shouldLog('debug')) { console.debug(`[DEBUG] ${new Date().toISOString()} - ${message}`, ...args); } } static info(message, ...args) { if (this.shouldLog('info')) { console.info(`[INFO] ${new Date().toISOString()} - ${message}`, ...args); } } static warn(message, ...args) { if (this.shouldLog('warn')) { console.warn(`[WARN] ${new Date().toISOString()} - ${message}`, ...args); } } static error(message, error) { if (this.shouldLog('error')) { console.error(`[ERROR] ${new Date().toISOString()} - ${message}`, error); } } } exports.Logger = Logger; // ============================================================================ // INITIALIZATION FUNCTIONS // ============================================================================ /** * Initialize the MCP server with comprehensive setup * @param options Configuration options */ async function initializeServer(options = {}) { try { Logger.info('Starting MCP server initialization...'); // Store configuration const nodeEnv = process.env.NODE_ENV; const environment = nodeEnv === 'development' || nodeEnv === 'test' ? nodeEnv : 'production'; serverConfig = { environment, enableCaching: true, logLevel: 'info', chunkSize: 1000, chunkOverlap: 200, ...options }; // Set log level if (serverConfig.logLevel) { logLevel = serverConfig.logLevel; } Logger.debug('Server configuration:', serverConfig); // Determine dump file path let dumpFilePath = serverConfig.dumpFilePath; if (!dumpFilePath) { const currentDir = __dirname; const projectRoot = path_1.default.resolve(currentDir, '..', '..'); dumpFilePath = path_1.default.join(projectRoot, 'dump.cs'); } Logger.info(`Loading dump file from: ${dumpFilePath}`); // Progress callback wrapper const progressCallback = serverConfig.progressCallback || ((progress, message) => { Logger.info(`Progress: ${progress}% - ${message}`); }); progressCallback(10, 'Initializing IL2CPP indexer...'); // Create and initialize the indexer with enhanced parser const indexer = new indexer_1.IL2CPPIndexer(serverConfig.chunkSize, serverConfig.chunkOverlap, serverConfig.model, serverConfig.hashFilePath); progressCallback(20, 'Loading and parsing dump file...'); // Index the dump file Logger.info('Indexing dump file...'); vectorStore = await indexer.indexFile(dumpFilePath, progressCallback, serverConfig.forceReprocess); progressCallback(100, 'Initialization complete'); Logger.info('Indexing complete'); // Mark as initialized isInitialized = true; Logger.info('MCP server initialization completed successfully'); } catch (error) { const errorMessage = `Failed to initialize MCP server: ${error instanceof Error ? error.message : 'Unknown error'}`; Logger.error(errorMessage, error); throw new error_types_1.MCPServerError(errorMessage, error_types_1.ErrorType.INITIALIZATION_ERROR); } } /** * Initialize the MCP server with a pre-existing vector store (backward compatibility) * @param store Vector store instance */ function initializeVectorStore(store) { Logger.info('Initializing MCP server with existing vector store'); vectorStore = store; isInitialized = true; Logger.info('Vector store initialization completed'); } /** * Check if the server is properly initialized */ function ensureInitialized() { if (!isInitialized || !vectorStore) { throw new error_types_1.MCPServerError('MCP server not initialized. Call initializeServer() first.', error_types_1.ErrorType.INITIALIZATION_ERROR); } } // ============================================================================ // MCP SERVER FACTORY // ============================================================================ /** * Create and configure the MCP server with all tools and resources * @returns Configured MCP server */ function createMcpServer() { Logger.debug('Creating MCP server instance'); // Create an MCP server with enhanced metadata const server = new mcp_js_1.McpServer({ name: "IL2CPP Dump Analyzer", version: "1.0.0", description: "A specialized RAG system for analyzing IL2CPP dump.cs files from Unity games", metadata: { capabilities: [ "Unity IL2CPP dump analysis", "MonoBehaviour discovery", "Class hierarchy analysis", "Method signature lookup", "Enum value retrieval", "Advanced filtering and search", "Dependency mapping and analysis", "Cross-reference analysis", "Design pattern detection and analysis", "C# class wrapper generation", "Method stub generation", "Unity MonoBehaviour template generation", "IL2CPP metadata extraction and analysis" ], supportedGameEngines: ["Unity"], supportedLanguages: ["C#"], author: "IL2CPP Dump Analyzer Team", version: "1.0.0", protocol: "MCP", features: { stdio_transport: true, http_transport: true, sse_transport: true, network_transport: true, resource_templates: true, advanced_tools: true, remote_access: true, authentication: true, rate_limiting: true } } }); // Register resources registerResources(server); // Register tools registerTools(server); Logger.debug('MCP server created successfully'); return server; } // ============================================================================ // RESOURCE HANDLERS // ============================================================================ /** * Register all resource handlers */ function registerResources(server) { Logger.debug('Registering resource handlers'); // Enhanced IL2CPP code search resource server.resource("il2cpp-code", new mcp_js_1.ResourceTemplate("il2cpp://{query}", { list: async () => { // Return some example resources that clients can discover return { resources: [ { uri: "il2cpp://MonoBehaviour", name: "MonoBehaviour Classes", description: "Search for MonoBehaviour classes" }, { uri: "il2cpp://Player", name: "Player Classes", description: "Search for Player-related classes" }, { uri: "il2cpp://GameObject", name: "GameObject Classes", description: "Search for GameObject-related classes" }, { uri: "il2cpp://WildMapPokemon", name: "WildMapPokemon Classes", description: "Search for WildMapPokemon-related classes" } ] }; } }), async (uri, { query }) => { try { ensureInitialized(); Logger.debug(`Resource request for il2cpp-code: ${uri.href}`); // Extract options from query parameters const url = new URL(uri.href); const topK = parseInt(url.searchParams.get('top_k') || '5'); const filterType = url.searchParams.get('filter_type'); const filterNamespace = url.searchParams.get('filter_namespace'); const filterMonoBehaviour = url.searchParams.get('filter_monobehaviour') === 'true'; // Build filter const filter = {}; if (filterType) filter.type = filterType; if (filterNamespace) filter.namespace = filterNamespace; if (filterMonoBehaviour) filter.isMonoBehaviour = true; // Ensure query is a string (not an array) const queryString = Array.isArray(query) ? query[0] : query; Logger.debug(`Searching with query: "${queryString}", filter:`, filter, `top_k: ${topK}`); // Perform search let results; if (Object.keys(filter).length > 0) { results = await vectorStore.searchWithFilter(queryString, filter, topK); } else { results = await vectorStore.similaritySearch(queryString, topK); } Logger.debug(`Found ${results.length} results for resource request`); // Format response return { contents: results.map(doc => ({ uri: `il2cpp://${encodeURIComponent(doc.metadata.fullName || doc.metadata.name)}`, text: doc.pageContent, metadata: { ...doc.metadata, searchQuery: queryString, appliedFilters: filter, resultCount: results.length } })) }; } catch (error) { Logger.error('Error in il2cpp-code resource handler:', error); throw new error_types_1.MCPServerError(`Resource error: ${error instanceof Error ? error.message : 'Unknown error'}`, error_types_1.ErrorType.RESOURCE_ERROR); } }); Logger.debug('Resource handlers registered successfully'); } // ============================================================================ // TOOL IMPLEMENTATIONS // ============================================================================ // Import the new tool registry const tool_registry_1 = require("./tools/tool-registry"); /** * Register all tool handlers using the new registry system */ function registerTools(server) { Logger.debug('Registering tool handlers using new registry system'); // Create tool execution context const context = { vectorStore: vectorStore, logger: Logger, isInitialized: () => isInitialized }; // Register all tools using the new registry (0, tool_registry_1.registerAllTools)(server, context); Logger.debug('All tools registered successfully via registry'); } // ============================================================================ // TRANSPORT FUNCTIONS // ============================================================================ /** * Start the MCP server with stdio transport (for command-line tools) */ async function startMcpStdioServer() { try { Logger.info('Starting MCP server with stdio transport...'); ensureInitialized(); const server = createMcpServer(); const transport = new stdio_js_1.StdioServerTransport(); await server.connect(transport); Logger.info("MCP server started successfully with stdio transport"); } catch (error) { Logger.error('Failed to start stdio server:', error); throw new error_types_1.MCPServerError(`Failed to start stdio server: ${error instanceof Error ? error.message : 'Unknown error'}`, error_types_1.ErrorType.TRANSPORT_ERROR); } } /** * Start the MCP server with configurable transport (stdio, HTTP, WebSocket, SSE) */ async function startMcpServerWithTransport(transportConfig) { try { Logger.info('Starting MCP server with configurable transport...'); ensureInitialized(); // Load transport configuration const config = transportConfig || (0, transport_1.loadTransportConfig)(); // Validate configuration const validation = (0, transport_1.validateTransportConfig)(config); if (!validation.valid) { throw new error_types_1.MCPServerError(`Invalid transport configuration: ${validation.errors.join(', ')}`, error_types_1.ErrorType.VALIDATION_ERROR); } // Print configuration summary (0, transport_1.printTransportConfigSummary)(config); const server = createMcpServer(); // Create transport based on configuration let transport; if (config.type === transport_1.TransportType.STDIO) { // Use stdio transport directly for backward compatibility transport = new stdio_js_1.StdioServerTransport(); } else { // Use transport factory for network transports transport = await (0, transport_1.createValidatedTransport)(config); } await server.connect(transport); Logger.info(`MCP server started successfully with ${config.type} transport`); // Log connection information for network transports if (config.type !== transport_1.TransportType.STDIO) { const protocol = config.enableSsl ? 'https' : 'http'; const url = `${protocol}://${config.host}:${config.port}`; Logger.info(`Server accessible at: ${url}`); if (config.apiKey) { Logger.info('Authentication: API key required'); } else { Logger.warn('Authentication: No API key configured (open access)'); } } } catch (error) { Logger.error('Failed to start MCP server with transport:', error); throw new error_types_1.MCPServerError(`Failed to start MCP server: ${error instanceof Error ? error.message : 'Unknown error'}`, error_types_1.ErrorType.TRANSPORT_ERROR); } } // ============================================================================ // STARTUP FUNCTIONS (from stdio-server.ts) // ============================================================================ /** * Complete server startup with initialization and configurable transport * This function combines the initialization and startup logic with transport selection */ async function startMcpServer(options = {}) { try { // Set up the environment for the MCP server process.env.NODE_ENV = process.env.NODE_ENV || 'production'; Logger.info('Starting complete MCP server initialization and startup...'); // Initialize the server with the provided options await initializeServer(options); // Start the MCP server with configurable transport await startMcpServerWithTransport(options.transportConfig); } catch (error) { Logger.error('Failed to start MCP server:', error); process.exit(1); } } /** * Legacy function for backward compatibility - starts with stdio transport only */ async function startMcpServerStdio(options = {}) { try { // Set up the environment for the MCP server process.env.NODE_ENV = process.env.NODE_ENV || 'production'; Logger.info('Starting MCP server with stdio transport (legacy mode)...'); // Initialize the server with the provided options await initializeServer(options); // Start the MCP stdio server await startMcpStdioServer(); } catch (error) { Logger.error('Failed to start MCP server:', error); process.exit(1); } } // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ /** * Get server status and statistics */ function getServerStatus() { return { initialized: isInitialized, vectorStoreReady: vectorStore !== null, configuration: serverConfig }; } /** * Gracefully shutdown the server */ async function shutdown() { Logger.info('Shutting down MCP server...'); // Reset state vectorStore = null; isInitialized = false; serverConfig = {}; Logger.info('MCP server shutdown complete'); } // ============================================================================ // EXPORTS FOR BACKWARD COMPATIBILITY // ============================================================================ // Export the main startup function as default for stdio-server.ts compatibility exports.default = startMcpServer; //# sourceMappingURL=mcp-sdk-server.js.map