mcp-repl
Version:
MCP REPL with code execution, semantic code search, and comprehensive ast-grep integration
130 lines (112 loc) • 5.12 kB
JavaScript
// Core MCP server implementation
// Main server setup and request handling logic
import * as path from 'node:path';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { getAllTools } from './tool-definitions.js';
import { createErrorResponse } from './common-errors.js';
import { getSequentialThinkingToolDefinition } from './thinking-handler.js';
/**
* Initialize MCP server with lazy-loaded dependencies
*/
export async function createMCPServer(workingDir) {
// Load dependencies synchronously to prevent ARM64 memory corruption during lazy loading
console.log('[DEBUG] Loading dependencies synchronously...');
// Load all modules upfront to avoid memory corruption during lazy loading
const isARM64 = process.arch === 'arm64' || process.platform === 'linux' && process.arch === 'arm64';
// Use ARM64-compatible vector indexer
let vectorIndexer = await import('./arm64-vector-indexer.js');
const [astgrepUtils, astgrepAdvanced, astgrepHandlers, astgrepHandlersAdvanced] = await Promise.all([
import('./astgrep-utils.js'),
import('./astgrep-advanced.js'),
import('./astgrep-handlers.js'),
import('./astgrep-handlers-advanced.js')
]);
const [astgrepJsonFormats, astgrepProjectConfig, astgrepAdvancedSearch, astgrepTestValidation, astgrepEnhancedHandlers] = await Promise.all([
import('./astgrep-json-formats.js'),
import('./astgrep-project-config.js'),
import('./astgrep-advanced-search.js'),
import('./astgrep-test-validation.js'),
import('./astgrep-enhanced-handlers.js')
]);
const batchHandler = await import('./batch-handler.js');
const bashHandler = await import('./bash-handler.js');
// Create synchronous getter functions
const getVectorIndexer = async () => vectorIndexer;
const getAstGrepUtils = async () => ({
astgrepUtils, astgrepAdvanced, astgrepHandlers, astgrepHandlersAdvanced
});
const getEnhancedAstGrepUtils = async () => ({
astgrepJsonFormats, astgrepProjectConfig, astgrepAdvancedSearch, astgrepTestValidation, astgrepEnhancedHandlers
});
const getBatchHandler = async () => batchHandler;
const getBashHandler = async () => bashHandler;
// Initialize the MCP server
const server = new McpServer(
{ name: "direct-node-executor", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Initialize code search lazily (don't sync on startup to prevent hanging)
// Use ARM64-compatible semantic search
try {
console.log('[DEBUG] Code search will initialize on first use');
const { initialize } = await getVectorIndexer();
await initialize();
console.log('[DEBUG] Code search base initialization complete');
} catch (error) {
console.error(`[DEBUG] Code search base initialization failed: ${error.message}`);
}
// Create tool handlers
const { createToolHandlers } = await import('./tool-handlers.js');
const toolHandlers = createToolHandlers(
workingDir,
getVectorIndexer,
getAstGrepUtils,
getEnhancedAstGrepUtils,
getBatchHandler,
getBashHandler
);
// Load tool descriptions JSON (metadata)
const __dirname = path.dirname(fileURLToPath(import.meta.url));
let descriptions = {};
try {
const raw = readFileSync(path.join(__dirname, 'tool-descriptions.json'), 'utf8');
descriptions = JSON.parse(raw).tools || {};
} catch (err) {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) console.error('[DEBUG] Failed to load tool-descriptions.json', err.message);
descriptions = {};
}
// Register ListTools handler: return the static tool definitions from code only.
// This ensures the server advertises only implemented tools (no placeholders).
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: getAllTools() }));
// Register CallTool handler to dispatch to the tool handlers map
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const startTime = Date.now();
const toolName = request.params?.name;
const args = request.params?.arguments || {};
const handler = toolHandlers[toolName];
if (!handler) {
return createErrorResponse(`Unknown tool: ${toolName}`, startTime);
}
try {
return await handler(args);
} catch (error) {
return createErrorResponse(error, startTime, { tool: toolName });
}
});
return server;
}
/**
* Start the MCP server
*/
export async function startServer() {
const workingDir = process.argv[2] && !process.argv[2].startsWith('-')
? path.resolve(process.argv[2])
: process.cwd();
const server = await createMCPServer(workingDir);
const transport = new StdioServerTransport();
await server.connect(transport);
}