mcp-product-manager
Version:
MCP Orchestrator for task and project management with web interface
182 lines • 6.17 kB
JavaScript
/**
* 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