import pino from 'pino';
const logger = pino(
{ level: process.env.LOG_LEVEL || 'info' },
pino.destination(2)
);
export class ToolRegistry {
constructor() {
this.tools = new Map();
}
registerTool(name, toolDefinition) {
if (this.tools.has(name)) {
logger.warn({ name }, 'Tool already registered, overwriting');
}
if (!toolDefinition.handler || typeof toolDefinition.handler !== 'function') {
throw new Error(`Tool ${name} must have a handler function`);
}
if (!toolDefinition.inputSchema) {
throw new Error(`Tool ${name} must have an inputSchema`);
}
if (!toolDefinition.description) {
throw new Error(`Tool ${name} must have a description`);
}
this.tools.set(name, toolDefinition);
logger.debug({ name }, 'Tool registered');
}
registerTools(toolsObject) {
Object.entries(toolsObject).forEach(([name, definition]) => {
this.registerTool(name, definition);
});
logger.info({ count: this.tools.size }, 'All tools registered');
}
getTool(name) {
return this.tools.get(name);
}
getAllTools() {
return Array.from(this.tools.entries()).map(([name, definition]) => ({
name,
description: definition.description,
inputSchema: definition.inputSchema
}));
}
getToolNames() {
return Array.from(this.tools.keys());
}
hasTool(name) {
return this.tools.has(name);
}
async executeTool(name, input) {
const tool = this.tools.get(name);
if (!tool) {
throw new Error(`Tool ${name} not found`);
}
try {
const validatedInput = tool.inputSchema.parse(input);
logger.debug({ tool: name }, 'Executing tool');
const result = await tool.handler(validatedInput);
logger.debug({ tool: name, success: result.success }, 'Tool execution completed');
return result;
} catch (error) {
logger.error({ err: error, tool: name }, 'Tool execution failed');
// Handle Zod validation errors
if (error.name === 'ZodError') {
return {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Input validation failed',
details: error.errors
}
};
}
throw error;
}
}
getToolCount() {
return this.tools.size;
}
clearTools() {
this.tools.clear();
logger.info('All tools cleared');
}
}