Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
mcp-resources.ts17.3 kB
/** * MCP Resources Implementation * Provides MCP protocol resources for pattern data access */ import { Resource, ReadResourceRequest, ListResourcesRequest, } from '@modelcontextprotocol/sdk/types.js'; interface ResourceResponse { contents: Array<{ uri: string; mimeType: string; text: string; }>; } import { Pattern, PatternCategory } from '../models/pattern.js'; import { parseTags, parseArrayProperty } from '../utils/parse-tags.js'; // Import real service interfaces import { DatabaseManager as RealDatabaseManager } from '../services/database-manager.js'; // Adapter interface to match MCP expectations export interface SupportedLanguage { language: string; count: number; } export interface ServerStats { totalPatterns: number; totalCategories: number; avgResponseTime: number; totalRequests: number; cacheHitRate: number; } export interface DatabaseManager { getAllPatterns(): Promise<Pattern[]>; getPatternById(id: string): Promise<Pattern | null>; getPatternCategories(): Promise<PatternCategory[]>; getSupportedLanguages(): Promise<SupportedLanguage[]>; getServerStats(): Promise<ServerStats>; } // Adapter class to bridge real service with MCP interface export class DatabaseManagerAdapter implements DatabaseManager { constructor(private realDatabaseManager: RealDatabaseManager) {} getAllPatterns(): Promise<Pattern[]> { const rows = this.realDatabaseManager.query<{ id: string; name: string; category: string; description: string; when_to_use: string; benefits: string; drawbacks: string; use_cases: string; complexity: string; tags: string; created_at: string; updated_at: string; }>(` SELECT id, name, category, description, when_to_use, benefits, drawbacks, use_cases, complexity, tags, created_at, updated_at FROM patterns ORDER BY name `); return Promise.resolve( rows.map(row => ({ id: row.id, name: row.name, category: row.category, description: row.description, problem: row.description, // Use description as the problem statement solution: 'See description for solution details', // Default solution text 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<{ id: string; name: string; category: string; description: string; when_to_use: string; benefits: string; drawbacks: string; use_cases: string; complexity: string; tags: string; created_at: string; updated_at: string; }>( ` SELECT id, name, category, description, when_to_use, benefits, drawbacks, use_cases, complexity, tags, created_at, updated_at FROM patterns WHERE id = ? `, [id] ); if (!row) return Promise.resolve(null); return Promise.resolve({ id: row.id, name: row.name, category: row.category, description: row.description, problem: row.description, // Use description as the problem statement solution: 'See description for solution details', // Default solution text 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 getPatternCategories(): Promise<any[]> { const rows = this.realDatabaseManager.query<{ category: string; pattern_count: number; }>(` SELECT category, COUNT(*) as pattern_count FROM patterns GROUP BY category ORDER BY category `); return Promise.resolve( rows.map((row, index) => ({ id: index + 1, name: row.category, description: `${row.category} design patterns`, patternCount: row.pattern_count, typicalUseCases: this.getTypicalUseCases(row.category), createdAt: new Date(), updatedAt: new Date(), })) ); } async getSupportedLanguages(): Promise<SupportedLanguage[]> { // Return static list for now - could be made dynamic return Promise.resolve([ { 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' ); const categoryCount = this.realDatabaseManager.queryOne<{ count: number }>( 'SELECT COUNT(DISTINCT category) as count FROM patterns' ); return Promise.resolve({ totalPatterns: patternCount?.count ?? 0, totalCategories: categoryCount?.count ?? 0, avgResponseTime: 150, totalRequests: 0, // Would need to track this 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'; } } export interface MCPResourcesConfig { databaseManager: DatabaseManager; serverVersion: string; totalPatterns: number; } /** * MCP Resources Handler * Implements all MCP resources for the Design Patterns server */ export class MCPResourcesHandler { private config: MCPResourcesConfig; constructor(config: MCPResourcesConfig) { this.config = config; } /** * Get all available MCP resources */ getResources(): Resource[] { return [ { uri: 'patterns', mimeType: 'application/json', name: 'Design Patterns Catalog', description: 'Complete catalog of 200+ design patterns with metadata', }, { uri: 'categories', mimeType: 'application/json', name: 'Pattern Categories', description: 'Pattern categories with counts and statistics', }, { uri: 'languages', mimeType: 'application/json', name: 'Supported Languages', description: 'Programming languages supported for pattern implementations', }, { uri: 'server_info', mimeType: 'application/json', name: 'Server Information', description: 'Server status, configuration, and performance statistics', }, ]; } /** * Handle resource read requests */ async handleResourceRead(request: ReadResourceRequest): Promise<ResourceResponse> { const { uri } = request.params; if (uri === 'patterns') { return await this.handlePatternsResource(); } else if (uri === 'categories') { return await this.handleCategoriesResource(); } else if (uri === 'languages') { return await this.handleLanguagesResource(); } else if (uri === 'server_info') { return await this.handleServerInfoResource(); } else if (uri.startsWith('pattern/')) { const patternId = uri.split('/')[1]; return await this.handlePatternResource(patternId); } else { throw new Error(`Unknown resource: ${uri}`); } } /** * Handle patterns resource */ private async handlePatternsResource(): Promise<ResourceResponse> { try { const patterns = await this.config.databaseManager.getAllPatterns(); return { contents: [ { uri: 'patterns', mimeType: 'application/json', text: JSON.stringify( patterns.map(pattern => ({ id: pattern.id, name: pattern.name, category: pattern.category, description: pattern.description, complexity: pattern.complexity, popularity: pattern.popularity ?? null, tags: pattern.tags, createdAt: pattern.createdAt.toISOString(), updatedAt: pattern.updatedAt.toISOString(), })), null, 2 ), }, ], }; } catch (error) { throw new Error( `Failed to retrieve patterns: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle specific pattern resource */ private async handlePatternResource(patternId: string): Promise<ResourceResponse> { try { const pattern = await this.config.databaseManager.getPatternById(patternId); if (!pattern) { return { contents: [ { uri: `pattern/${patternId}`, mimeType: 'application/json', text: '[]', }, ], }; } return { contents: [ { uri: `pattern/${patternId}`, mimeType: 'application/json', text: JSON.stringify( { id: pattern.id, name: pattern.name, category: pattern.category, description: pattern.description, problem: pattern.problem, solution: pattern.solution, when_to_use: pattern.when_to_use, benefits: pattern.benefits, drawbacks: pattern.drawbacks, use_cases: pattern.use_cases, implementations: pattern.implementations, relatedPatterns: pattern.relatedPatterns, complexity: pattern.complexity, tags: pattern.tags, createdAt: pattern.createdAt.toISOString(), updatedAt: pattern.updatedAt.toISOString(), }, null, 2 ), }, ], }; } catch (error) { throw new Error( `Failed to retrieve pattern ${patternId}: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle categories resource */ private async handleCategoriesResource(): Promise<ResourceResponse> { try { const categories = await this.config.databaseManager.getPatternCategories(); const categoriesWithStats = categories.map((category: any) => ({ category: category.name, pattern_count: category.patternCount, description: category.description, typical_use_cases: category.typicalUseCases, complexity_distribution: { Low: Math.floor(category.patternCount * 0.3), // Placeholder distribution Medium: Math.floor(category.patternCount * 0.5), High: Math.floor(category.patternCount * 0.2), }, })); return { contents: [ { uri: 'categories', mimeType: 'application/json', text: JSON.stringify(categoriesWithStats, null, 2), }, ], }; } catch (error) { throw new Error( `Failed to retrieve categories: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle languages resource */ private async handleLanguagesResource(): Promise<ResourceResponse> { try { const languages = await this.config.databaseManager.getSupportedLanguages(); // If no languages from database, provide default list const defaultLanguages = [ { language: 'typescript', display_name: 'TypeScript', implementation_count: 150, supported_features: ['classes', 'interfaces', 'generics', 'decorators'], }, { language: 'javascript', display_name: 'JavaScript', implementation_count: 120, supported_features: ['classes', 'prototypes', 'closures'], }, { language: 'python', display_name: 'Python', implementation_count: 100, supported_features: ['classes', 'decorators', 'metaclasses'], }, { language: 'java', display_name: 'Java', implementation_count: 90, supported_features: ['classes', 'interfaces', 'generics'], }, { language: 'csharp', display_name: 'C#', implementation_count: 80, supported_features: ['classes', 'interfaces', 'generics', 'delegates'], }, { language: 'go', display_name: 'Go', implementation_count: 60, supported_features: ['structs', 'interfaces', 'channels'], }, { language: 'rust', display_name: 'Rust', implementation_count: 40, supported_features: ['traits', 'ownership', 'lifetimes'], }, ]; const result = languages.length > 0 ? languages : defaultLanguages; return { contents: [ { uri: 'languages', mimeType: 'application/json', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new Error( `Failed to retrieve languages: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle server info resource */ private async handleServerInfoResource(): Promise<ResourceResponse> { try { const stats = await this.config.databaseManager.getServerStats(); const serverInfo = { server_version: this.config.serverVersion, mcp_protocol_version: '1.0', total_patterns: this.config.totalPatterns, database_type: 'sqlite', semantic_search_enabled: true, supported_languages: ['typescript', 'javascript', 'python', 'java', 'csharp', 'go', 'rust'], llm_providers: [ { name: 'openai', available: true, configured: false, }, { name: 'ollama', available: true, configured: false, }, { name: 'anthropic', available: true, configured: false, }, ], performance_stats: { avg_response_time_ms: stats?.avgResponseTime || 150, total_requests: stats?.totalRequests || 0, cache_hit_rate: stats?.cacheHitRate || 0.85, }, uptime_seconds: Math.floor(process.uptime()), memory_usage_mb: Math.round(process.memoryUsage().heapUsed / 1024 / 1024), }; return { contents: [ { uri: 'server_info', mimeType: 'application/json', text: JSON.stringify(serverInfo, null, 2), }, ], }; } catch (error) { throw new Error( `Failed to retrieve server info: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Handle resource list requests */ async handleResourceList(_request: ListResourcesRequest): Promise<{ resources: any[] }> { try { const resources = this.getResources(); // Add dynamic pattern resources if needed const patterns = await this.config.databaseManager.getAllPatterns(); const patternResources = patterns.slice(0, 10).map(pattern => ({ // Limit to first 10 for performance uri: `pattern/${pattern.id}`, mimeType: 'application/json', name: `${pattern.name} Pattern`, description: `Detailed information about the ${pattern.name} design pattern`, })); return { resources: [...resources, ...patternResources], }; } catch (error) { throw new Error( `Failed to list resources: ${error instanceof Error ? error.message : String(error)}` ); } } }

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