UNPKG

mcp-product-manager

Version:

MCP Orchestrator for task and project management with web interface

182 lines 6.17 kB
/** * Tool Registry System * Dynamically imports tools from route files that export tool definitions */ import fs from 'fs/promises'; import { readdirSync, statSync } from 'fs'; import path from 'path'; import { join } from 'path'; import { fileURLToPath } from 'url'; import { enhanceTool } from './toolAdapter.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Cache for loaded tools const toolCache = new Map(); const toolMapping = new Map(); /** * Recursively find all route files in a directory */ function findRouteFiles(dir, files = []) { try { const items = readdirSync(dir); for (const item of items) { const fullPath = join(dir, item); const stat = statSync(fullPath); if (stat.isDirectory()) { findRouteFiles(fullPath, files); } else if (item.endsWith('.js') && !item.includes('.test.')) { files.push(fullPath); } } } catch (error) { console.error(`Error scanning directory ${dir}:`, error.message); } return files; } /** * Import tools from route files that export tool definitions */ export async function importTools() { const routesDir = path.join(__dirname, '../../api', 'routes'); console.error('[Tool Registry] Scanning for route files with tool exports...'); try { const routeFiles = findRouteFiles(routesDir); console.error(`[Tool Registry] Found ${routeFiles.length} route files`); for (const filePath of routeFiles) { try { // Dynamic import const module = await import(`file://${filePath}`); // Check if module exports a tool if (module.tool) { const tool = module.tool; const toolName = tool.name; // Store in cache with enhanced metadata and auto-generated execute method const enhancedTool = enhanceTool(toolName, tool); toolCache.set(toolName, enhancedTool); console.error(`[Tool Registry] ✓ Loaded tool: ${toolName} from ${filePath.replace(routesDir, '...')}`); } } catch (error) { console.error(`[Tool Registry] ✗ Error loading ${filePath}: ${error.message}`); } } // Also load tools from api/tools directory if it exists const toolsDir = path.join(__dirname, '../../api', 'tools'); try { const files = await fs.readdir(toolsDir); const toolFiles = files.filter(f => f.endsWith('.js')); for (const file of toolFiles) { const toolName = path.basename(file, '.js'); const toolPath = path.join(toolsDir, file); try { const toolModule = await import(`file://${toolPath}`); const tool = (toolModule.default || toolModule); // Ensure inputSchema is set from parameters if available if (tool.parameters && !tool.inputSchema) { tool.inputSchema = tool.parameters; } // Override route-based tools with api/tools versions that have execute methods toolCache.set(toolName, tool); console.error(`[Tool Registry] ✓ Loaded tool: ${toolName} from api/tools/ (with execute method)`); } catch (error) { console.error(`[Tool Registry] ✗ Failed to load tool ${toolName}:`, error.message); } } } catch (error) { console.error('[Tool Registry] No api/tools directory or error reading it:', error.message); } console.error(`[Tool Registry] Successfully loaded ${toolCache.size} tools total`); return toolCache; } catch (error) { console.error(`[Tool Registry] Fatal error: ${error.message}`); throw error; } } /** * Generate MCP manifest for available tools */ export function generateManifest() { const manifest = { name: "orchestrator-mcp", version: "1.0.0", description: "MCP server for orchestrator tools", tools: {} }; for (const [name, tool] of toolCache.entries()) { manifest.tools[name] = { description: tool.description || `Execute ${name} tool`, parameters: tool.inputSchema || tool.parameters || { type: "object", properties: {}, required: [] } }; } return manifest; } /** * Get tool by name */ export function getTool(name) { return toolCache.get(name); } /** * Get all tool mappings */ export function getToolMapping() { return toolMapping; } /** * Register a new tool dynamically */ export function registerTool(name, tool) { if (typeof tool === 'function') { const wrappedTool = { name, description: `Execute ${name} tool`, inputSchema: { type: 'object', properties: {} }, execute: tool }; toolCache.set(name, wrappedTool); toolMapping.set(name, tool); } else { toolCache.set(name, tool); if (tool.execute) { toolMapping.set(name, tool.execute.bind(tool)); } } console.error(`[Tool Registry] Registered tool: ${name}`); } /** * Execute a tool by name with parameters */ export async function executeTool(name, params) { const tool = toolCache.get(name); if (!tool) { throw new Error(`Tool ${name} not found`); } if (tool.execute) { return await tool.execute(params); } throw new Error(`Tool ${name} does not have an execute method`); } /** * List all available tools */ export function listTools() { return Array.from(toolCache.keys()); } /** * Clear tool cache (useful for testing) */ export function clearToolCache() { toolCache.clear(); toolMapping.clear(); } //# sourceMappingURL=toolRegistry.js.map