Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
mcp-tools.ts36.9 kB
/** * MCP Tools Implementation * Provides MCP protocol tools for pattern recommendations and analysis */ import { CallToolRequest, Tool } from '@modelcontextprotocol/sdk/types.js'; import { PatternRequest } from '../models/request.js'; import { Pattern } from '../models/pattern.js'; import { UserPreference } from '../models/preference.js'; import { parseTags, parseArrayProperty } from '../utils/parse-tags.js'; // Import real service interfaces import { PatternMatcher as RealPatternMatcher } from '../services/pattern-matcher.js'; import { SemanticSearchService } from '../services/semantic-search.js'; import { DatabaseManager as RealDatabaseManager } from '../services/database-manager.js'; // Common filter types export interface PatternFilters { categories?: string[]; complexity?: string[]; tags?: string[]; } // Adapter interfaces to match MCP expectations export interface PatternMatcher { findSimilarPatterns(request: PatternRequest): Promise<PatternRecommendation[]>; analyzeCode(code: string, language: string): Promise<CodeAnalysisResult>; } export interface SemanticSearch { search( query: string, options?: { limit?: number; filters?: PatternFilters } ): Promise<SearchResult[]>; } export interface DatabaseManager { searchPatterns( query: string, options?: { limit?: number; filters?: PatternFilters } ): Promise<SearchResult[]>; updatePattern(id: string, updates: Partial<Pattern>): Promise<void>; savePattern(pattern: Pattern): Promise<void>; getAllPatterns(): Promise<Pattern[]>; getPatternById(id: string): Promise<Pattern | null>; getPatternCategories(): Promise<CategoryInfo[]>; getSupportedLanguages(): Promise<LanguageInfo[]>; getServerStats(): Promise<ServerStats>; } // Type definitions for MCP tool responses export interface PatternRecommendation { pattern: Pattern; score: number; rank: number; justification: string; implementation?: string; alternatives?: Pattern[]; context?: string; } export interface CodeAnalysisResult { patterns: Array<{ name: string; confidence: number; location?: string; description?: string; }>; suggestions: Array<{ type: string; message: string; severity: 'info' | 'warning' | 'error'; }>; summary: string; identifiedPatterns?: Array<{ name: string; confidence: number; location?: string; description?: string; }>; suggestedPatterns?: Pattern[]; improvements?: Array<{ type: string; message: string; severity: 'info' | 'warning' | 'error'; }>; } export interface SearchResult { pattern: Pattern; score: number; highlights?: string[]; } export interface CategoryInfo { name: string; count: number; description?: string; } export interface LanguageInfo { language: string; count: number; } export interface ServerStats { totalPatterns: number; totalCategories: number; avgResponseTime: number; totalRequests: number; cacheHitRate: number; } export interface CodeAnalysisResult { patterns: Array<{ name: string; confidence: number; location?: string; description?: string; }>; suggestions: Array<{ type: string; message: string; severity: 'info' | 'warning' | 'error'; }>; summary: string; } export interface SearchResult { pattern: Pattern; score: number; highlights?: string[]; } export interface CategoryInfo { name: string; count: number; description?: string; } export interface LanguageInfo { language: string; count: number; } export interface ServerStats { totalPatterns: number; totalCategories: number; avgResponseTime: number; totalRequests: number; cacheHitRate: number; } // Adapter interfaces to match MCP expectations export interface PatternMatcher { findSimilarPatterns(request: PatternRequest): Promise<PatternRecommendation[]>; analyzeCode(code: string, language: string): Promise<CodeAnalysisResult>; } export interface SemanticSearch { search( query: string, options?: { limit?: number; filters?: PatternFilters } ): Promise<SearchResult[]>; } export interface DatabaseManager { searchPatterns( query: string, options?: { limit?: number; filters?: PatternFilters } ): Promise<SearchResult[]>; updatePattern(id: string, updates: Partial<Pattern>): Promise<void>; savePattern(pattern: Pattern): Promise<void>; getAllPatterns(): Promise<Pattern[]>; getPatternById(id: string): Promise<Pattern | null>; getPatternCategories(): Promise<CategoryInfo[]>; getSupportedLanguages(): Promise<LanguageInfo[]>; getServerStats(): Promise<ServerStats>; } export interface MCPToolsConfig { patternMatcher: PatternMatcher; semanticSearch: SemanticSearch; databaseManager: DatabaseManager; patternService: unknown; // TODO: Define PatternService interface preferences: Map<string, UserPreference>; } // Adapter classes to bridge real services with MCP interfaces export class PatternMatcherAdapter implements PatternMatcher { constructor(private realPatternMatcher: RealPatternMatcher) {} async findSimilarPatterns(request: PatternRequest): Promise<PatternRecommendation[]> { // Convert MCP PatternRequest to real service PatternRequest const realRequest = { id: request.id, query: request.query, categories: request.categoryFilter, maxResults: request.maxResults, programmingLanguage: request.programmingLanguage, }; const recommendations = await this.realPatternMatcher.findMatchingPatterns(realRequest); // Convert back to MCP format return recommendations.map((rec, index) => ({ pattern: rec.pattern as Pattern, score: rec.confidence, rank: index + 1, justification: 'Pattern matches query criteria', implementation: 'Implementation details available in pattern description', alternatives: [], context: 'Pattern context information', })); } async analyzeCode(_code: string, _language: string): Promise<CodeAnalysisResult> { // For now, return a simple analysis - would need to implement in real service return Promise.resolve({ patterns: [], suggestions: [], summary: 'Analysis not yet implemented in real service', }); } } export class SemanticSearchAdapter implements SemanticSearch { constructor(private realSemanticSearch: SemanticSearchService) {} async search(query: string, options?: { categoryFilter?: string[]; limit?: number }): Promise<any[]> { const searchQuery = { text: query, filters: options?.categoryFilter ? { categories: options.categoryFilter } : undefined, options: { limit: options?.limit || 10, includeMetadata: true, }, }; const results = await this.realSemanticSearch.search(searchQuery); return results.map(result => ({ id: result.patternId, name: result.pattern.name, category: result.pattern.category, description: result.pattern.description, score: result.score, matchType: 'semantic', })); } } export class DatabaseManagerAdapter implements DatabaseManager { constructor(private realDatabaseManager: RealDatabaseManager) {} async searchPatterns(query: string, options?: { limit?: number }): Promise<any[]> { // Simple keyword search implementation const patterns = this.realDatabaseManager.query( ` SELECT id, name, category, description, complexity, tags FROM patterns WHERE name LIKE ? OR description LIKE ? OR category LIKE ? LIMIT ? `, [`%${query}%`, `%${query}%`, `%${query}%`, options?.limit || 10] ); return patterns.map(pattern => ({ id: pattern.id, name: pattern.name, category: pattern.category, description: pattern.description, score: 0.8, // Default score for keyword matches matchType: 'keyword', })); } async updatePattern(id: string, updates: Record<string, unknown>): Promise<void> { // Build dynamic update query const fields = Object.keys(updates); const values = Object.values(updates); const setClause = fields.map(field => `${field} = ?`).join(', '); this.realDatabaseManager.execute( `UPDATE patterns SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, [...values, id] ); } async savePattern(pattern: Pattern): Promise<void> { const sql = ` INSERT INTO patterns (id, name, category, description, when_to_use, benefits, drawbacks, use_cases, complexity, tags, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `; const params = [ pattern.id, pattern.name, pattern.category, pattern.description, pattern.when_to_use ? JSON.stringify(pattern.when_to_use) : null, pattern.benefits ? JSON.stringify(pattern.benefits) : null, pattern.drawbacks ? JSON.stringify(pattern.drawbacks) : null, pattern.use_cases ? JSON.stringify(pattern.use_cases) : null, pattern.complexity, pattern.tags ? JSON.stringify(pattern.tags) : null, ]; this.realDatabaseManager.execute(sql, params); } async getAllPatterns(): Promise<Pattern[]> { const rows = this.realDatabaseManager.query(` SELECT id, name, category, description, when_to_use, benefits, drawbacks, use_cases, complexity, tags, created_at, updated_at FROM patterns ORDER BY name `); return rows.map(row => ({ id: row.id, name: row.name, category: row.category, description: row.description, problem: row.description || '', // Use description as problem fallback solution: row.description || '', // Use description as solution fallback when_to_use: parseArrayProperty(row.when_to_use, 'when_to_use'), benefits: parseArrayProperty(row.benefits, 'benefits'), drawbacks: parseArrayProperty(row.drawbacks, 'drawbacks'), use_cases: parseArrayProperty(row.use_cases, 'use_cases'), implementations: [], // Would need to fetch from pattern_implementations table relatedPatterns: [], // Would need to fetch from pattern_relationships table complexity: row.complexity, tags: parseTags(row.tags), createdAt: new Date(row.created_at), updatedAt: new Date(row.updated_at), })); } async getPatternById(id: string): Promise<Pattern | null> { const row = this.realDatabaseManager.queryOne( ` SELECT id, name, category, description, problem, solution, when_to_use, benefits, drawbacks, use_cases, complexity, tags, created_at, updated_at FROM patterns WHERE id = ? `, [id] ); if (!row) return null; return { id: row.id, name: row.name, category: row.category, description: row.description, problem: row.problem || '', solution: row.solution || '', when_to_use: parseArrayProperty(row.when_to_use, 'when_to_use'), benefits: parseArrayProperty(row.benefits, 'benefits'), drawbacks: parseArrayProperty(row.drawbacks, 'drawbacks'), use_cases: parseArrayProperty(row.use_cases, 'use_cases'), implementations: [], // Would need to fetch from pattern_implementations table relatedPatterns: undefined, // Would need to fetch from pattern_relationships table complexity: row.complexity, tags: parseTags(row.tags), createdAt: new Date(row.created_at), updatedAt: new Date(row.updated_at), }; } async getPatternCategories(): Promise<CategoryInfo[]> { const rows = this.realDatabaseManager.query(` SELECT category, COUNT(*) as pattern_count FROM patterns GROUP BY category ORDER BY category `); return rows.map(row => ({ name: row.category, count: row.pattern_count, })); } async getSupportedLanguages(): Promise<LanguageInfo[]> { // Return static list for now - could be made dynamic return [ { language: 'typescript', count: 150, }, { language: 'javascript', count: 120, }, { language: 'python', count: 100, }, ]; } async getServerStats(): Promise<ServerStats> { const patternCount = this.realDatabaseManager.queryOne<{ count: number }>( 'SELECT COUNT(*) as count FROM patterns' ); return { totalPatterns: patternCount?.count || 0, totalCategories: 20, // Would need to calculate this avgResponseTime: 150, totalRequests: 0, cacheHitRate: 0.85, }; } private getTypicalUseCases(category: string): string { const useCases: Record<string, string> = { Creational: 'Object instantiation, resource management', Structural: 'Class relationships, interface adaptation', Behavioral: 'Communication, responsibility assignment', Architectural: 'System organization, component interaction', 'Cloud-Native': 'Scalability, resilience, distributed systems', Microservices: 'Service decomposition, communication patterns', 'AI/ML': 'Model training, inference, data processing', Functional: 'Data transformation, immutability, composition', Reactive: 'Asynchronous processing, event handling', 'Anti-Pattern': 'Common mistakes to avoid', }; return useCases[category] || 'General software design'; } } /** * MCP Tools Handler * Implements all MCP tools for the Design Patterns server */ export class MCPToolsHandler { private config: MCPToolsConfig; constructor(config: MCPToolsConfig) { this.config = config; } /** * Get all available MCP tools */ getTools(): Tool[] { return [ { name: 'suggest_pattern', description: 'Suggest design patterns based on natural language description and optional code context', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language description of the problem or requirement', minLength: 10, maxLength: 2000, }, code_context: { type: 'string', description: 'Optional code snippet for additional context', maxLength: 50000, }, programming_language: { type: 'string', description: 'Target programming language preference', enum: [ 'typescript', 'javascript', 'python', 'java', 'csharp', 'go', 'rust', 'cpp', 'php', 'ruby', 'swift', 'kotlin', ], }, max_results: { type: 'number', description: 'Maximum number of recommendations to return', minimum: 1, maximum: 20, default: 5, }, include_examples: { type: 'boolean', description: 'Include code examples in recommendations', default: true, }, category_filter: { type: 'array', items: { type: 'string', enum: [ 'Creational', 'Structural', 'Behavioral', 'Architectural', 'Cloud-Native', 'Microservices', 'AI-ML', 'Functional', 'Reactive', 'Anti-Pattern', ], }, description: 'Filter results by pattern categories', }, }, required: ['query'], }, }, { name: 'search_patterns', description: 'Search patterns using keyword or semantic search', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query', minLength: 2, maxLength: 500, }, search_type: { type: 'string', description: 'Type of search to perform', enum: ['keyword', 'semantic', 'hybrid'], default: 'hybrid', }, category_filter: { type: 'array', items: { type: 'string', }, description: 'Filter by pattern categories', }, limit: { type: 'number', description: 'Maximum number of results', minimum: 1, maximum: 50, default: 10, }, }, required: ['query'], }, }, { name: 'analyze_code', description: 'Analyze code to identify patterns and suggest improvements', inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'Code to analyze', minLength: 10, maxLength: 50000, }, language: { type: 'string', description: 'Programming language of the code', enum: [ 'typescript', 'javascript', 'python', 'java', 'csharp', 'go', 'rust', 'cpp', 'php', 'ruby', 'swift', 'kotlin', ], }, analysis_type: { type: 'string', description: 'Type of analysis to perform', enum: ['identify_patterns', 'suggest_improvements', 'both'], default: 'both', }, }, required: ['code', 'language'], }, }, { name: 'update_pattern', description: 'Create or update a design pattern in the catalog', inputSchema: { type: 'object', properties: { pattern_id: { type: 'string', description: 'Pattern ID for updates (omit for new patterns)', }, name: { type: 'string', description: 'Pattern name', minLength: 2, maxLength: 100, }, category: { type: 'string', description: 'Pattern category', enum: [ 'Creational', 'Structural', 'Behavioral', 'Architectural', 'Cloud-Native', 'Microservices', 'AI-ML', 'Functional', 'Reactive', 'Anti-Pattern', ], }, description: { type: 'string', description: 'Pattern description', minLength: 50, maxLength: 2000, }, problem: { type: 'string', description: 'Problem the pattern solves', minLength: 20, maxLength: 1000, }, solution: { type: 'string', description: 'How the pattern solves the problem', minLength: 20, maxLength: 1000, }, use_cases: { type: 'string', description: 'Common use cases', minLength: 20, maxLength: 1000, }, complexity: { type: 'string', description: 'Implementation complexity', enum: ['Beginner', 'Intermediate', 'Advanced'], }, }, required: [ 'name', 'category', 'description', 'problem', 'solution', 'use_cases', 'complexity', ], }, }, { name: 'get_config', description: 'Retrieve server configuration settings', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Configuration category to retrieve', enum: ['all', 'search', 'display', 'llm', 'performance'], default: 'all', }, }, }, }, { name: 'set_config', description: 'Update server configuration settings', inputSchema: { type: 'object', properties: { settings: { type: 'object', description: 'Settings to update', additionalProperties: true, }, category: { type: 'string', description: 'Configuration category', enum: ['search', 'display', 'llm', 'performance'], }, }, required: ['settings', 'category'], }, }, { name: 'count_patterns', description: 'Get the total number of design patterns in the database', inputSchema: { type: 'object', properties: { includeDetails: { type: 'boolean', description: 'Include breakdown by category', default: false, }, }, }, }, { name: 'create_relationship', description: 'Create a new relationship between two design patterns', inputSchema: { type: 'object', properties: { source_pattern_id: { type: 'string', description: 'ID of the source pattern', }, target_pattern_id: { type: 'string', description: 'ID of the target pattern', }, type: { type: 'string', description: 'Type of relationship', enum: [ 'related', 'extends', 'implements', 'uses', 'similar', 'alternative', 'complements', 'conflicts', 'prerequisite', 'successor', ], }, strength: { type: 'number', description: 'Strength of the relationship (0.0 to 1.0)', minimum: 0.0, maximum: 1.0, default: 1.0, }, description: { type: 'string', description: 'Human-readable description of the relationship', minLength: 10, maxLength: 500, }, }, required: ['source_pattern_id', 'target_pattern_id', 'type', 'description'], }, }, { name: 'get_relationships', description: 'Get relationships for patterns with optional filtering', inputSchema: { type: 'object', properties: { pattern_id: { type: 'string', description: 'Pattern ID to get relationships for (optional - returns all if not specified)', }, type: { type: 'string', description: 'Filter by relationship type', enum: [ 'related', 'extends', 'implements', 'uses', 'similar', 'alternative', 'complements', 'conflicts', 'prerequisite', 'successor', ], }, min_strength: { type: 'number', description: 'Minimum relationship strength', minimum: 0.0, maximum: 1.0, }, include_pattern_details: { type: 'boolean', description: 'Include full pattern details in response', default: true, }, limit: { type: 'number', description: 'Maximum number of relationships to return', minimum: 1, maximum: 100, default: 50, }, }, }, }, { name: 'update_relationship', description: 'Update an existing relationship between patterns', inputSchema: { type: 'object', properties: { relationship_id: { type: 'string', description: 'ID of the relationship to update', }, type: { type: 'string', description: 'New relationship type', enum: [ 'related', 'extends', 'implements', 'uses', 'similar', 'alternative', 'complements', 'conflicts', 'prerequisite', 'successor', ], }, strength: { type: 'number', description: 'New relationship strength (0.0 to 1.0)', minimum: 0.0, maximum: 1.0, }, description: { type: 'string', description: 'New relationship description', minLength: 10, maxLength: 500, }, }, required: ['relationship_id'], }, }, { name: 'delete_relationship', description: 'Delete a relationship between two patterns', inputSchema: { type: 'object', properties: { source_pattern_id: { type: 'string', description: 'ID of the source pattern', }, target_pattern_id: { type: 'string', description: 'ID of the target pattern', }, }, required: ['source_pattern_id', 'target_pattern_id'], }, }, ]; } /** * Handle list tools request */ async handleListTools(): Promise<{ tools: Tool[] }> { return { tools: this.getTools(), }; } /** * Handle tool calls */ async handleToolCall(request: CallToolRequest): Promise<any> { const { name, arguments: args } = request.params; switch (name) { case 'suggest_pattern': return await this.handleSuggestPattern(args); case 'search_patterns': return await this.handleSearchPatterns(args); case 'analyze_code': return await this.handleAnalyzeCode(args); case 'get_config': return await this.handleGetConfig(args); case 'set_config': return await this.handleSetConfig(args); case 'count_patterns': return await this.handleCountPatterns(args); default: throw new Error(`Unknown tool: ${name}`); } } /** * Handle suggest_pattern tool */ private async handleSuggestPattern(args: any): Promise<any> { // Validate input if (!args.query || typeof args.query !== 'string' || args.query.length < 10) { throw new Error('Query must be at least 10 characters long'); } if (args.code_context && args.code_context.length > 50000) { throw new Error('Code context exceeds maximum length of 50,000 characters'); } // Create pattern request const request: PatternRequest = { id: crypto.randomUUID(), query: args.query, codeContext: args.code_context, programmingLanguage: args.programming_language, maxResults: args.max_results || 5, includeExamples: args.include_examples !== false, categoryFilter: args.category_filter, timestamp: new Date(), processingTime: 0, }; const startTime = Date.now(); try { // Get pattern recommendations const recommendations = await this.config.patternMatcher.findSimilarPatterns(request); const processingTime = Date.now() - startTime; request.processingTime = processingTime; // Format response according to contract return { request_id: request.id, recommendations: recommendations.map(rec => ({ pattern: { id: rec.pattern.id, name: rec.pattern.name, category: rec.pattern.category, description: rec.pattern.description, complexity: rec.pattern.complexity, tags: rec.pattern.tags, }, score: rec.score || 0, rank: rec.rank, justification: rec.justification, implementation: rec.implementation, alternatives: rec.alternatives, context: rec.context, })), metadata: { total_results: recommendations.length, processing_time_ms: processingTime, search_strategy: 'hybrid', filters_applied: request.categoryFilter ? ['category'] : [], }, }; } catch (error) { throw new Error( `Pattern suggestion failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle analyze_code tool */ private async handleAnalyzeCode(args: any): Promise<any> { // Validate input if (!args.code || typeof args.code !== 'string' || args.code.length < 10) { throw new Error('Code must be at least 10 characters long'); } if (!args.language || typeof args.language !== 'string') { throw new Error('Language is required'); } try { // Use pattern matcher to analyze code const analysis = await this.config.patternMatcher.analyzeCode(args.code, args.language); return { language: args.language, identified_patterns: analysis.identifiedPatterns || analysis.patterns || [], suggested_patterns: analysis.suggestedPatterns || [], improvements: analysis.improvements || analysis.suggestions || [], summary: analysis.summary || 'Analysis completed', analysis_type: args.analysis_type || 'both', timestamp: new Date().toISOString(), }; } catch (error) { throw new Error( `Code analysis failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle search_patterns tool */ private async handleSearchPatterns(args: any): Promise<any> { // Validate input if (!args.query || typeof args.query !== 'string' || args.query.length < 2) { throw new Error('Query must be at least 2 characters long'); } const searchType = args.search_type || 'hybrid'; const limit = Math.min(args.limit || 10, 50); try { let results: any[]; if (searchType === 'semantic') { // Use semantic search results = await this.config.semanticSearch.search(args.query, { limit, filters: args.category_filter ? { categories: [args.category_filter] } : undefined, }); } else if (searchType === 'keyword') { // Use keyword search results = await this.config.databaseManager.searchPatterns(args.query, { limit, filters: args.category_filter ? { categories: [args.category_filter] } : undefined, }); } else { // Hybrid search const semanticResults = await this.config.semanticSearch.search(args.query, { limit: Math.ceil(limit / 2), filters: args.category_filter ? { categories: [args.category_filter] } : undefined, }); const keywordResults = await this.config.databaseManager.searchPatterns(args.query, { limit: Math.ceil(limit / 2), filters: args.category_filter ? { categories: [args.category_filter] } : undefined, }); // Merge and deduplicate results results = this.mergeSearchResults(semanticResults, keywordResults, limit); } return { patterns: results, total_results: results.length, search_type: searchType, limit, timestamp: new Date().toISOString(), }; } catch (error) { throw new Error( `Pattern search failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle get_config tool */ private async handleGetConfig(args: any): Promise<any> { const category = args.category || 'all'; try { const config: any = {}; if (category === 'all') { // Return all configuration for (const [key, pref] of this.config.preferences) { if (!config[pref.category]) { config[pref.category] = {}; } config[pref.category][key] = pref.settingValue; } } else { // Return specific category for (const [key, pref] of this.config.preferences) { if (pref.category === category) { config[key] = pref.settingValue; } } } return { configuration: config, category, timestamp: new Date().toISOString(), }; } catch (error) { throw new Error( `Configuration retrieval failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle set_config tool */ private async handleSetConfig(args: any): Promise<any> { if (!args.settings || typeof args.settings !== 'object') { throw new Error('Settings must be a valid object'); } if (!args.category) { throw new Error('Category is required'); } try { const updatedSettings: string[] = []; for (const [key, value] of Object.entries(args.settings)) { const preference: UserPreference = { id: Date.now(), // Simple ID generation settingKey: key, settingValue: value, category: args.category, description: `User-configured ${key}`, createdAt: new Date(), updatedAt: new Date(), }; this.config.preferences.set(key, preference); updatedSettings.push(key); } return { success: true, updated_settings: updatedSettings, category: args.category, timestamp: new Date().toISOString(), }; } catch (error) { throw new Error( `Configuration update failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle count_patterns tool */ private async handleCountPatterns(args: any): Promise<any> { try { // Get all patterns to count them const patterns = await this.config.databaseManager.getAllPatterns(); const total = patterns.length; if (args.includeDetails) { // Create category breakdown from patterns const categoryBreakdown: { [key: string]: number } = {}; patterns.forEach(pattern => { categoryBreakdown[pattern.category] = (categoryBreakdown[pattern.category] || 0) + 1; }); const breakdown = Object.entries(categoryBreakdown) .map(([category, count]) => ({ category, count })) .sort((a, b) => b.count - a.count); return { content: [ { type: 'text', text: `## Total Design Patterns: ${total}\n\n` + `### Breakdown by Category:\n` + breakdown.map(item => `- **${item.category}**: ${item.count} patterns`).join('\n') + '\n\n' + `*Total patterns from all sources: ${total}*`, }, ], }; } else { return { content: [ { type: 'text', text: `Total design patterns in database: **${total}**`, }, ], }; } } catch (error) { throw new Error( `Pattern count failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } /** * Merge search results from different sources */ private mergeSearchResults(semanticResults: any[], keywordResults: any[], limit: number): any[] { const seen = new Set<string>(); const merged: any[] = []; // Add semantic results first for (const result of semanticResults) { if (!seen.has(result.id) && merged.length < limit) { seen.add(result.id); merged.push({ ...result, matchType: 'semantic' }); } } // Add keyword results for (const result of keywordResults) { if (!seen.has(result.id) && merged.length < limit) { seen.add(result.id); merged.push({ ...result, matchType: 'keyword' }); } } return merged; } }

Implementation Reference

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/apolosan/design_patterns_mcp'

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