Skip to main content
Glama

Context Pods

by conorluddy
base-tool.ts5.28 kB
/** * Base tool class with error handling */ import { logger } from '@context-pods/core'; /** * Tool execution result */ export interface ToolResult { success: boolean; data?: unknown; error?: string; warnings?: string[]; } import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; /** * MCP tool response content - matches CallToolResult from MCP SDK */ export type MCPToolResponse = CallToolResult; /** * Base tool class with common functionality */ export abstract class BaseTool { protected toolName: string; constructor(toolName: string) { this.toolName = toolName; } /** * Execute the tool with error boundary */ async safeExecute(args: unknown): Promise<MCPToolResponse> { const startTime = Date.now(); try { logger.info(`Executing tool: ${this.toolName}`, { args }); // Validate arguments const validationError = await this.validateArguments(args); if (validationError) { return this.createErrorResponse(`Invalid arguments: ${validationError}`); } // Execute the tool const result = await this.execute(args); // Format successful response const response = this.formatResponse(result); const duration = Date.now() - startTime; logger.info(`Tool executed successfully: ${this.toolName}`, { duration }); return response; } catch (error) { const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Tool execution failed: ${this.toolName}`, { error: errorMessage, duration, args, }); return this.createErrorResponse(errorMessage); } } /** * Abstract method for tool implementation */ protected abstract execute(args: unknown): Promise<ToolResult>; /** * Validate tool arguments */ protected async validateArguments(_args: unknown): Promise<string | null> { // Default implementation - no validation // Override in subclasses for specific validation return null; } /** * Format successful tool response */ protected formatResponse(result: ToolResult): MCPToolResponse { if (!result.success) { return this.createErrorResponse(result.error || 'Tool execution failed'); } let text = ''; // Add success message if (result.data) { if (typeof result.data === 'string') { text = result.data; } else { text = JSON.stringify(result.data, null, 2); } } else { text = `✅ ${this.toolName} completed successfully`; } // Add warnings if any if (result.warnings && result.warnings.length > 0) { text += '\n\n⚠️ Warnings:\n' + result.warnings.map((w) => `- ${w}`).join('\n'); } return { content: [ { type: 'text', text, }, ], }; } /** * Create error response */ protected createErrorResponse(error: string): MCPToolResponse { return { content: [ { type: 'text', text: `❌ Error in ${this.toolName}: ${error}`, }, ], }; } /** * Create success response with custom text */ protected createSuccessResponse(text: string): MCPToolResponse { return { content: [ { type: 'text', text: `✅ ${text}`, }, ], }; } /** * Create info response */ protected createInfoResponse(text: string): MCPToolResponse { return { content: [ { type: 'text', text, }, ], }; } /** * Helper to check if argument exists and is of correct type */ protected validateArgument( args: Record<string, unknown>, name: string, type: 'string' | 'number' | 'boolean' | 'object', required = true, ): string | null { if (required && (args[name] === undefined || args[name] === null)) { return `Missing required argument: ${name}`; } if (args[name] !== undefined && args[name] !== null) { const actualType = Array.isArray(args[name]) ? 'array' : typeof args[name]; if (type === 'object' && actualType !== 'object') { return `Argument '${name}' must be an object, got ${actualType}`; } else if (type !== 'object' && actualType !== type) { return `Argument '${name}' must be a ${type}, got ${actualType}`; } } return null; } /** * Helper to validate string argument with additional checks */ protected validateStringArgument( args: Record<string, unknown>, name: string, required = true, minLength = 0, maxLength = Infinity, ): string | null { const typeError = this.validateArgument(args, name, 'string', required); if (typeError) return typeError; if (args[name] !== undefined && args[name] !== null) { const value = typeof args[name] === 'string' ? args[name] : JSON.stringify(args[name]); if (value.length < minLength) { return `Argument '${name}' must be at least ${minLength} characters long`; } if (value.length > maxLength) { return `Argument '${name}' must be at most ${maxLength} characters long`; } } return null; } }

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/conorluddy/ContextPods'

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