Skip to main content
Glama
response-formatter.ts14.7 kB
/** * Response Formatter * * Utility for formatting consistent responses across all Git MCP tools. * Provides standardized success/error response formats with metadata. */ export interface ToolResult { success: boolean; data?: any; error?: { code: string; message: string; details?: any; suggestions?: string[]; configurationGuide?: { requiredEnvVars: string[]; exampleConfig: any; }; }; metadata?: { provider?: string; operation: string; timestamp: string; executionTime: number; toolName?: string; projectPath?: string; repositoryName?: string; }; } export interface FormatterOptions { includeMetadata?: boolean; includeTimestamp?: boolean; includeExecutionTime?: boolean; maxDataSize?: number; truncateData?: boolean; } export class ResponseFormatter { private defaultOptions: FormatterOptions = { includeMetadata: true, includeTimestamp: true, includeExecutionTime: true, maxDataSize: 10000, // 10KB truncateData: true }; /** * Format successful response */ success( data: any, metadata: { operation: string; provider?: string; executionTime?: number; toolName?: string; projectPath?: string; repositoryName?: string; }, options: FormatterOptions = {} ): ToolResult { const mergedOptions = { ...this.defaultOptions, ...options }; // Process data size if needed let processedData = data; if (mergedOptions.truncateData && mergedOptions.maxDataSize) { processedData = this.truncateDataIfNeeded(data, mergedOptions.maxDataSize); } const result: ToolResult = { success: true, data: processedData }; if (mergedOptions.includeMetadata) { result.metadata = { operation: metadata.operation, timestamp: mergedOptions.includeTimestamp ? new Date().toISOString() : '', executionTime: mergedOptions.includeExecutionTime ? (metadata.executionTime || 0) : 0 }; if (metadata.provider) result.metadata.provider = metadata.provider; if (metadata.toolName) result.metadata.toolName = metadata.toolName; if (metadata.projectPath) result.metadata.projectPath = metadata.projectPath; if (metadata.repositoryName) result.metadata.repositoryName = metadata.repositoryName; } return result; } /** * Format error response */ error( code: string, message: string, details?: any, metadata?: { operation: string; provider?: string; executionTime?: number; toolName?: string; projectPath?: string; repositoryName?: string; }, options: FormatterOptions = {} ): ToolResult { const mergedOptions = { ...this.defaultOptions, ...options }; const result: ToolResult = { success: false, error: { code, message, details, suggestions: this.generateSuggestions(code, message, details) } }; // Add configuration guide for specific error types const configGuide = this.generateConfigurationGuide(code, details); if (configGuide) { result.error!.configurationGuide = configGuide; } if (mergedOptions.includeMetadata && metadata) { result.metadata = { operation: metadata.operation, timestamp: mergedOptions.includeTimestamp ? new Date().toISOString() : '', executionTime: mergedOptions.includeExecutionTime ? (metadata.executionTime || 0) : 0 }; if (metadata.provider) result.metadata.provider = metadata.provider; if (metadata.toolName) result.metadata.toolName = metadata.toolName; if (metadata.projectPath) result.metadata.projectPath = metadata.projectPath; if (metadata.repositoryName) result.metadata.repositoryName = metadata.repositoryName; } return result; } /** * Format validation error response */ validationError( parameter: string, expectedFormat: string, actualValue?: any, metadata?: { operation: string; toolName?: string; } ): ToolResult { return this.error( 'VALIDATION_ERROR', `Invalid parameter: ${parameter}`, { parameter, expectedFormat, actualValue, example: this.generateParameterExample(parameter, expectedFormat) }, metadata ); } /** * Format configuration error response */ configurationError( missingConfig: string[], provider?: string, metadata?: { operation: string; toolName?: string; } ): ToolResult { const providerText = provider ? ` for ${provider}` : ''; return this.error( 'CONFIGURATION_ERROR', `Missing required configuration${providerText}`, { missingConfig, provider }, metadata ); } /** * Format Git error response */ gitError( gitErrorType: string, gitMessage: string, command?: string, metadata?: { operation: string; toolName?: string; projectPath?: string; } ): ToolResult { return this.error( `GIT_${gitErrorType.toUpperCase()}`, `Git error: ${gitMessage}`, { gitErrorType, command, workingDirectory: metadata?.projectPath }, metadata ); } /** * Format provider error response */ providerError( provider: string, apiError: string, statusCode?: number, metadata?: { operation: string; toolName?: string; } ): ToolResult { return this.error( 'PROVIDER_ERROR', `${provider} API error: ${apiError}`, { provider, statusCode, apiError }, metadata ); } /** * Format operation not supported error */ operationNotSupported( operation: string, supportedOperations: string[], toolName?: string ): ToolResult { return this.error( 'OPERATION_NOT_SUPPORTED', `Operation '${operation}' is not supported`, { operation, supportedOperations, toolName }, { operation: 'validate_operation', toolName } ); } /** * Format partial success response (for multi-provider operations) */ partialSuccess( successResults: any[], failedResults: any[], metadata: { operation: string; providers: string[]; executionTime?: number; toolName?: string; } ): ToolResult { const totalProviders = metadata.providers.length; const successCount = successResults.length; const failureCount = failedResults.length; return { success: successCount > 0, // Partial success if at least one succeeded data: { results: successResults, summary: { total: totalProviders, successful: successCount, failed: failureCount, successRate: Math.round((successCount / totalProviders) * 100) } }, error: failureCount > 0 ? { code: 'PARTIAL_FAILURE', message: `${failureCount} out of ${totalProviders} providers failed`, details: { failedResults, failedProviders: failedResults.map((r: any) => r.provider) }, suggestions: [ 'Check failed provider configurations', 'Verify network connectivity', 'Review provider-specific error messages' ] } : undefined, metadata: { operation: metadata.operation, timestamp: new Date().toISOString(), executionTime: metadata.executionTime || 0, toolName: metadata.toolName, provider: metadata.providers.join(', ') } }; } /** * Format list response with pagination info */ listResponse( items: any[], pagination?: { page?: number; limit?: number; total?: number; hasMore?: boolean; }, metadata?: { operation: string; provider?: string; executionTime?: number; toolName?: string; } ): ToolResult { const data: any = { items, count: items.length }; if (pagination) { data.pagination = pagination; } return this.success(data, metadata || { operation: 'list' }); } /** * Format status response */ statusResponse( status: 'healthy' | 'warning' | 'error', details: any, metadata?: { operation: string; toolName?: string; } ): ToolResult { return this.success( { status, details, timestamp: new Date().toISOString() }, metadata || { operation: 'status' } ); } /** * Generate suggestions based on error code and message */ private generateSuggestions(code: string, message: string, details?: any): string[] { const suggestions: string[] = []; switch (code) { case 'VALIDATION_ERROR': suggestions.push('Check parameter format and try again'); suggestions.push('Refer to tool documentation for parameter examples'); break; case 'CONFIGURATION_ERROR': suggestions.push('Set required environment variables'); suggestions.push('Check configuration guide in error details'); break; case 'GIT_NOT_A_REPOSITORY': suggestions.push('Initialize Git repository with: git init'); suggestions.push('Ensure you are in the correct directory'); break; case 'GIT_COMMAND_NOT_FOUND': suggestions.push('Install Git from https://git-scm.com/'); suggestions.push('Ensure Git is in your system PATH'); break; case 'PROVIDER_ERROR': suggestions.push('Check API credentials and permissions'); suggestions.push('Verify network connectivity'); if (details?.statusCode === 401) { suggestions.push('Verify authentication token is valid'); } if (details?.statusCode === 403) { suggestions.push('Check repository permissions'); } if (details?.statusCode === 404) { suggestions.push('Verify repository exists and is accessible'); } break; case 'OPERATION_NOT_SUPPORTED': suggestions.push('Use one of the supported operations listed in details'); suggestions.push('Check tool documentation for available operations'); break; default: suggestions.push('Check error details for more information'); suggestions.push('Retry the operation after addressing the issue'); } return suggestions; } /** * Generate configuration guide for specific errors */ private generateConfigurationGuide(code: string, details?: any): { requiredEnvVars: string[]; exampleConfig: any; } | undefined { if (code === 'CONFIGURATION_ERROR') { const provider = details?.provider; if (provider === 'github') { return { requiredEnvVars: ['GITHUB_TOKEN', 'GITHUB_USERNAME'], exampleConfig: { GITHUB_TOKEN: 'ghp_xxxxxxxxxxxxxxxxxxxx', GITHUB_USERNAME: 'your-github-username' } }; } if (provider === 'gitea') { return { requiredEnvVars: ['GITEA_URL', 'GITEA_TOKEN', 'GITEA_USERNAME'], exampleConfig: { GITEA_URL: 'https://gitea.example.com', GITEA_TOKEN: 'your-gitea-token', GITEA_USERNAME: 'your-gitea-username' } }; } // Multi-provider configuration return { requiredEnvVars: [ 'GITHUB_TOKEN', 'GITHUB_USERNAME', 'GITEA_URL', 'GITEA_TOKEN', 'GITEA_USERNAME' ], exampleConfig: { github: { GITHUB_TOKEN: 'ghp_xxxxxxxxxxxxxxxxxxxx', GITHUB_USERNAME: 'your-github-username' }, gitea: { GITEA_URL: 'https://gitea.example.com', GITEA_TOKEN: 'your-gitea-token', GITEA_USERNAME: 'your-gitea-username' } } }; } return undefined; } /** * Generate parameter example based on parameter name and expected format */ private generateParameterExample(parameter: string, expectedFormat: string): any { const examples: Record<string, any> = { projectPath: '/path/to/your/project', provider: 'github | gitea | both', action: 'create | list | get | update | delete', repositoryName: 'my-repository', branchName: 'feature/new-feature', commitMessage: 'Add new feature', tagName: 'v1.0.0', issueNumber: 123, pullRequestNumber: 456, fileName: 'src/index.ts', content: 'file content here', title: 'Issue or PR title', description: 'Detailed description', labels: ['bug', 'enhancement'], assignees: ['username1', 'username2'] }; return examples[parameter] || `<${expectedFormat}>`; } /** * Truncate data if it exceeds maximum size */ private truncateDataIfNeeded(data: any, maxSize: number): any { const dataString = JSON.stringify(data); if (dataString.length <= maxSize) { return data; } // If data is too large, truncate and add indicator const truncatedString = dataString.substring(0, maxSize - 100); try { // Try to parse truncated JSON (might be invalid) return JSON.parse(truncatedString); } catch { // If truncated JSON is invalid, return summary return { _truncated: true, _originalSize: dataString.length, _maxSize: maxSize, _summary: this.generateDataSummary(data), _note: 'Data was truncated due to size limits' }; } } /** * Generate summary of data structure */ private generateDataSummary(data: any): any { if (Array.isArray(data)) { return { type: 'array', length: data.length, sampleItems: data.slice(0, 3), hasMore: data.length > 3 }; } if (typeof data === 'object' && data !== null) { const keys = Object.keys(data); const summary: any = { type: 'object', keys: keys.slice(0, 10), hasMoreKeys: keys.length > 10 }; // Include sample values for first few keys for (let i = 0; i < Math.min(3, keys.length); i++) { const key = keys[i]; summary[key] = typeof data[key] === 'object' ? `[${typeof data[key]}]` : data[key]; } return summary; } return { type: typeof data, value: String(data).substring(0, 100) }; } } // Export singleton instance export const responseFormatter = new ResponseFormatter();

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/Andre-Buzeli/git-mcp'

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