Skip to main content
Glama
brendon92

Specialized AI Search Tools

by brendon92
base.ts5.27 kB
import { z } from 'zod'; import { logger } from '../utils/logger.js'; import type { ToolResult } from '../types/tools.js'; /** * Base class for all MCP tools * Provides common functionality for validation, execution, and error handling */ export abstract class BaseTool<TSchema extends z.ZodType = z.ZodType> { abstract readonly name: string; abstract readonly description: string; abstract readonly schema: TSchema; /** * Validate input parameters against the tool's schema */ protected validate(params: unknown): z.infer<TSchema> { try { return this.schema.parse(params); } catch (error) { if (error instanceof z.ZodError) { const issues = error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join(', '); throw new Error(`Validation failed: ${issues}`); } throw error; } } /** * Format successful response */ protected formatSuccess(data: unknown): ToolResult { return { content: [ { type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2), }, ], }; } /** * Format error response */ protected formatError(error: Error): ToolResult { logger.error(`Tool ${this.name} error:`, { error: error.message }); return { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; } /** * Execute the tool with given parameters * This method handles validation and error handling automatically */ async run(params: unknown): Promise<ToolResult> { try { logger.debug(`Executing tool: ${this.name}`, { params }); const validatedParams = this.validate(params); const result = await this.execute(validatedParams); logger.info(`Tool ${this.name} executed successfully`); return this.formatSuccess(result); } catch (error) { return this.formatError(error instanceof Error ? error : new Error(String(error))); } } /** * Abstract method to be implemented by concrete tools * This is where the actual tool logic goes */ protected abstract execute(params: z.infer<TSchema>): Promise<unknown>; /** * Get tool definition for MCP server registration */ getDefinition() { return { name: this.name, description: this.description, inputSchema: { type: 'object', properties: this.getSchemaProperties(), required: this.getRequiredFields(), }, }; } /** * Extract properties from Zod schema for MCP tool definition */ private getSchemaProperties(): Record<string, unknown> { // This is a simplified version - in production you'd want more robust schema conversion if (!(this.schema instanceof z.ZodObject)) { return {}; } const shape = this.schema.shape; const properties: Record<string, unknown> = {}; for (const [key, value] of Object.entries(shape)) { if (value instanceof z.ZodString) { properties[key] = { type: 'string', description: value.description || '' }; } else if (value instanceof z.ZodNumber) { properties[key] = { type: 'number', description: value.description || '' }; } else if (value instanceof z.ZodBoolean) { properties[key] = { type: 'boolean', description: value.description || '' }; } else if (value instanceof z.ZodEnum) { properties[key] = { type: 'string', enum: value.options, description: value.description || '', }; } else if (value instanceof z.ZodOptional) { // Handle optional fields recursively const innerType = value.unwrap(); if (innerType instanceof z.ZodString) { properties[key] = { type: 'string', description: innerType.description || '' }; } else if (innerType instanceof z.ZodNumber) { properties[key] = { type: 'number', description: innerType.description || '' }; } else if (innerType instanceof z.ZodBoolean) { properties[key] = { type: 'boolean', description: innerType.description || '' }; } } } return properties; } /** * Get list of required fields from Zod schema */ private getRequiredFields(): string[] { if (!(this.schema instanceof z.ZodObject)) { return []; } const shape = this.schema.shape; const required: string[] = []; for (const [key, value] of Object.entries(shape)) { if (!(value instanceof z.ZodOptional)) { required.push(key); } } return required; } }

Latest Blog Posts

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/brendon92/mcp-server'

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