UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

178 lines (177 loc) 7.07 kB
import { z } from 'zod'; import logger from '../../logger.js'; import { AppError, ValidationError } from '../../utils/errors.js'; let instance = null; export class ToolRegistry { tools = new Map(); config; constructor(config) { this.config = config; logger.info('ToolRegistry instance created.'); } static getInstance(config) { if (!instance) { if (!config) { throw new Error("ToolRegistry requires configuration on first initialization."); } instance = new ToolRegistry(config); logger.info('ToolRegistry initialized with configuration', { hasLlmMapping: Boolean(config.llm_mapping), mappingKeys: config.llm_mapping ? Object.keys(config.llm_mapping) : [] }); processPendingToolRegistrations(); } else if (config) { instance.config = { ...config }; logger.info('ToolRegistry configuration updated', { hasLlmMapping: Boolean(config.llm_mapping), mappingKeys: config.llm_mapping ? Object.keys(config.llm_mapping) : [] }); } return instance; } registerTool(definition) { if (this.tools.has(definition.name)) { logger.warn(`Tool "${definition.name}" is already registered. Skipping duplicate registration.`); return; } this.tools.set(definition.name, definition); logger.info(`Registered tool: ${definition.name}`); } getTool(toolName) { return this.tools.get(toolName); } getAllTools() { return Array.from(this.tools.values()); } getRegistrationStats() { return { totalRegistered: this.tools.size, toolNames: Array.from(this.tools.keys()) }; } isToolRegistered(toolName) { return this.tools.has(toolName); } clearRegistryForTesting() { if (process.env.NODE_ENV !== 'test') { logger.warn('Attempted to clear tool registry outside of a test environment. Operation aborted.'); return; } this.tools.clear(); instance = null; logger.debug('Tool registry cleared for testing.'); } } export function registerTool(definition) { if (!instance) { logger.info(`Tool "${definition.name}" registration deferred until ToolRegistry is initialized with proper config.`); pendingToolRegistrations.push(definition); return; } ToolRegistry.getInstance().registerTool(definition); } const pendingToolRegistrations = []; export function processPendingToolRegistrations() { if (!instance) { logger.error("Tried to process pending tool registrations but ToolRegistry is still not initialized"); return; } const pendingCount = pendingToolRegistrations.length; if (pendingCount > 0) { logger.info(`Processing ${pendingCount} pending tool registrations`); for (const toolDef of pendingToolRegistrations) { ToolRegistry.getInstance().registerTool(toolDef); } pendingToolRegistrations.length = 0; logger.info(`Successfully registered ${pendingCount} pending tools`); } } export function getTool(toolName) { if (!instance) { logger.warn("Attempted to get tool before ToolRegistry initialization."); return undefined; } return ToolRegistry.getInstance().getTool(toolName); } export function getAllTools() { if (!instance) { logger.warn("Attempted to get all tools before ToolRegistry initialization."); return []; } return ToolRegistry.getInstance().getAllTools(); } export async function executeTool(toolName, params, config, context) { logger.debug({ toolName, receivedConfig: config }, 'executeTool received config object.'); logger.info(`Attempting to execute tool: ${toolName}`); const toolDefinition = getTool(toolName); if (!toolDefinition) { logger.error(`Tool "${toolName}" not found in registry.`); return { content: [{ type: 'text', text: `Error: Tool "${toolName}" not found.` }], isError: true, }; } const schemaObject = z.object(toolDefinition.inputSchema); const validationResult = schemaObject.safeParse(params); if (!validationResult.success) { logger.error({ tool: toolName, errors: validationResult.error.issues, paramsReceived: params }, 'Tool parameter validation failed.'); const validationError = new ValidationError(`Input validation failed for tool '${toolName}'`, validationResult.error.issues, { toolName, paramsReceived: params }); return { content: [{ type: 'text', text: validationError.message }], isError: true, errorDetails: { type: validationError.name, message: validationError.message, issues: validationError.validationIssues, } }; } logger.debug(`Executing tool "${toolName}" with validated parameters.`); try { logger.debug({ toolName: toolName, sessionId: context?.sessionId }, `Executing tool "${toolName}" executor with context.`); const result = await toolDefinition.executor(validationResult.data, config, context); logger.info(`Tool "${toolName}" executed successfully.`); return result; } catch (error) { logger.error({ err: error, tool: toolName, params: validationResult.data }, `Error during execution of tool "${toolName}".`); let errorMessage = `Execution error in tool '${toolName}'.`; let errorType = 'ToolExecutionError'; let errorContext = { toolName, params: validationResult.data }; if (error instanceof AppError) { errorMessage = `Error in tool '${toolName}': ${error.message}`; errorType = error.name; errorContext = { ...errorContext, ...error.context }; } else if (error instanceof Error) { errorMessage = `Unexpected error in tool '${toolName}': ${error.message}`; errorType = error.name; } else { errorMessage = `Unknown execution error in tool '${toolName}'.`; errorType = 'UnknownExecutionError'; errorContext.originalValue = String(error); } return { content: [{ type: 'text', text: errorMessage }], isError: true, errorDetails: { type: errorType, message: (error instanceof Error) ? error.message : String(error), context: errorContext, } }; } } export function isToolRegistered(toolName) { const registry = ToolRegistry.getInstance(); return registry.isToolRegistered(toolName); } export function clearRegistryForTesting() { if (!instance) { logger.debug('Tool registry not initialized, nothing to clear for testing.'); return; } instance.clearRegistryForTesting(); }