Skip to main content
Glama
tool-registry.js8.77 kB
/** * Tool Registry for MCP Server * Manages tool registration, discovery, and execution */ import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); class ToolRegistry { constructor() { this.tools = new Map(); this.config = null; } /** * Initialize the tool registry */ async initialize(config) { this.config = config; // Load built-in tools await this.loadBuiltInTools(); // Load custom tools if directory exists if (config.tools?.customToolsDir) { await this.loadCustomTools(config.tools.customToolsDir); } } /** * Load built-in tools */ async loadBuiltInTools() { // Example built-in tools this.registerTool({ name: 'list_items', description: 'List all items in the system', inputSchema: { type: 'object', properties: { filter: { type: 'string', description: 'Optional filter pattern' }, limit: { type: 'number', description: 'Maximum number of items to return' } } }, handler: async (args) => { const dataFile = path.join(this.config.dataDir, 'items.json'); if (!fs.existsSync(dataFile)) { return { items: [], count: 0 }; } const data = await fs.readJson(dataFile); let items = data.items || []; // Apply filter if provided if (args.filter) { const filterRegex = new RegExp(args.filter, 'i'); items = items.filter(item => filterRegex.test(item.name) || filterRegex.test(item.description) ); } // Apply limit if provided if (args.limit) { items = items.slice(0, args.limit); } return { items, count: items.length }; } }); this.registerTool({ name: 'add_item', description: 'Add a new item to the system', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Item name' }, description: { type: 'string', description: 'Item description' }, metadata: { type: 'object', description: 'Additional metadata' } }, required: ['name', 'description'] }, handler: async (args) => { const dataFile = path.join(this.config.dataDir, 'items.json'); // Load existing data let data = { items: [] }; if (fs.existsSync(dataFile)) { data = await fs.readJson(dataFile); } // Create new item const newItem = { id: Date.now().toString(), name: args.name, description: args.description, metadata: args.metadata || {}, createdAt: new Date().toISOString() }; // Add to array data.items.push(newItem); // Save await fs.writeJson(dataFile, data, { spaces: 2 }); return { success: true, item: newItem, message: `Item '${args.name}' added successfully` }; } }); this.registerTool({ name: 'remove_item', description: 'Remove an item from the system', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Item ID to remove' } }, required: ['id'] }, handler: async (args) => { const dataFile = path.join(this.config.dataDir, 'items.json'); if (!fs.existsSync(dataFile)) { throw new Error('No items found'); } const data = await fs.readJson(dataFile); const initialLength = data.items.length; // Filter out the item data.items = data.items.filter(item => item.id !== args.id); if (data.items.length === initialLength) { throw new Error(`Item with ID '${args.id}' not found`); } // Save await fs.writeJson(dataFile, data, { spaces: 2 }); return { success: true, message: `Item '${args.id}' removed successfully` }; } }); this.registerTool({ name: 'search_items', description: 'Search items with advanced query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, fields: { type: 'array', items: { type: 'string' }, description: 'Fields to search in' } }, required: ['query'] }, handler: async (args) => { const dataFile = path.join(this.config.dataDir, 'items.json'); if (!fs.existsSync(dataFile)) { return { results: [], count: 0 }; } const data = await fs.readJson(dataFile); const query = args.query.toLowerCase(); const fields = args.fields || ['name', 'description']; const results = data.items.filter(item => { return fields.some(field => { const value = item[field]; if (typeof value === 'string') { return value.toLowerCase().includes(query); } if (typeof value === 'object') { return JSON.stringify(value).toLowerCase().includes(query); } return false; }); }); return { results, count: results.length }; } }); this.registerTool({ name: 'get_status', description: 'Get system status and statistics', inputSchema: { type: 'object', properties: {} }, handler: async () => { const dataFile = path.join(this.config.dataDir, 'items.json'); let itemCount = 0; if (fs.existsSync(dataFile)) { const data = await fs.readJson(dataFile); itemCount = data.items?.length || 0; } return { serverName: this.config.serverName, version: this.config.version, uptime: process.uptime(), statistics: { totalItems: itemCount, registeredTools: this.tools.size }, configuration: { dataDir: this.config.dataDir, outputDir: this.config.outputDir, debug: this.config.debug } }; } }); } /** * Load custom tools from directory */ async loadCustomTools(toolsDir) { const absolutePath = path.isAbsolute(toolsDir) ? toolsDir : path.join(process.cwd(), toolsDir); if (!fs.existsSync(absolutePath)) { console.error(`Custom tools directory not found: ${absolutePath}`); return; } const files = await fs.readdir(absolutePath); const toolFiles = files.filter(f => f.endsWith('.js')); for (const file of toolFiles) { try { const toolPath = path.join(absolutePath, file); const tool = await import(toolPath); if (tool.default) { this.registerTool(tool.default); console.error(`Loaded custom tool from: ${file}`); } } catch (error) { console.error(`Failed to load custom tool ${file}: ${error.message}`); } } } /** * Register a tool */ registerTool(tool) { if (!tool.name || !tool.handler) { throw new Error('Tool must have a name and handler'); } this.tools.set(tool.name, { name: tool.name, description: tool.description || '', inputSchema: tool.inputSchema || { type: 'object', properties: {} }, handler: tool.handler }); } /** * Get all registered tools */ getTools() { return Array.from(this.tools.values()).map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })); } /** * Execute a tool */ async executeTool(name, args) { const tool = this.tools.get(name); if (!tool) { throw new Error(`Tool '${name}' not found`); } try { return await tool.handler(args); } catch (error) { throw new Error(`Tool '${name}' execution failed: ${error.message}`); } } /** * Check if tool exists */ hasTool(name) { return this.tools.has(name); } } export default new ToolRegistry();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iptracej-education/mcp-server-template'

If you have feedback or need assistance with the MCP directory API, please join our Discord server