Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
mcp-server.ts21.7 kB
#!/usr/bin/env node /** * MCP Server for Design Patterns * Main server implementation following MCP protocol * Simplified and clean implementation focusing on core functionality */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ReadResourceRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js'; import path from 'path'; import { fileURLToPath } from 'url'; import { DatabaseManager } from './services/database-manager.js'; import { VectorOperationsService } from './services/vector-operations.js'; import { PatternMatcher } from './services/pattern-matcher.js'; import { SemanticSearchService } from './services/semantic-search.js'; import { LLMBridgeService } from './services/llm-bridge.js'; import { MigrationManager } from './services/migrations.js'; import { PatternSeeder } from './services/pattern-seeder.js'; import { CacheService } from './services/cache.js'; import { logger } from './services/logger.js'; import { parseTags, parseArrayProperty } from './utils/parse-tags.js'; import { MCPRateLimiter } from './utils/rate-limiter.js'; export interface MCPServerConfig { databasePath: string; logLevel: 'debug' | 'info' | 'warn' | 'error'; enableLLM: boolean; maxConcurrentRequests: number; } class DesignPatternsMCPServer { private server: Server; private db: DatabaseManager; private vectorOps: VectorOperationsService; private patternMatcher: PatternMatcher; private semanticSearch!: SemanticSearchService; private llmBridge: LLMBridgeService | null = null; private migrationManager: MigrationManager; private patternSeeder: PatternSeeder; private config: MCPServerConfig; private rateLimiter: MCPRateLimiter; constructor(config: MCPServerConfig) { this.config = config; // Initialize database this.db = new DatabaseManager({ filename: config.databasePath, options: { verbose: config.logLevel === 'debug' ? (message: string) => logger.debug('database', message) : undefined, }, }); // Initialize services this.vectorOps = new VectorOperationsService(this.db, { model: 'all-MiniLM-L6-v2', dimensions: 384, similarityThreshold: 0.3, maxResults: 10, cacheEnabled: true, }); // Initialize semantic search service this.semanticSearch = new SemanticSearchService(this.db, this.vectorOps, { modelName: 'all-MiniLM-L6-v2', maxResults: 10, similarityThreshold: 0.3, contextWindow: 512, useQueryExpansion: false, useReRanking: true, }); this.patternMatcher = new PatternMatcher(this.db, this.vectorOps, { maxResults: 5, minConfidence: 0.05, // Lower threshold for more results useSemanticSearch: true, useKeywordSearch: true, useHybridSearch: true, semanticWeight: 0.7, keywordWeight: 0.3, }); if (config.enableLLM) { this.llmBridge = new LLMBridgeService(this.db, { provider: 'ollama', model: 'llama3.2', maxTokens: 2000, temperature: 0.3, timeout: 30000, // 30 seconds }); } // Get the directory of the current module const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Resolve path relative to project root // When running from src/, no need to go up // When running from dist/src/, go up two levels const isCompiled = __dirname.includes('dist'); const projectRoot = isCompiled ? path.resolve(__dirname, '..', '..') : path.resolve(__dirname, '..'); const patternsPath = path.join(projectRoot, 'data', 'patterns'); this.migrationManager = new MigrationManager(this.db); this.patternSeeder = new PatternSeeder(this.db, { patternsPath, batchSize: 100, skipExisting: true, }); // Initialize rate limiter this.rateLimiter = new MCPRateLimiter({ maxRequestsPerMinute: 60, maxRequestsPerHour: 1000, maxConcurrentRequests: this.config.maxConcurrentRequests, burstLimit: 20, }); // Initialize MCP server this.server = new Server( { name: 'design-patterns-mcp', version: '0.1.0', }, { capabilities: { resources: {}, tools: {}, prompts: {}, }, } ); this.setupHandlers(); } private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'find_patterns', description: 'Find design patterns matching a problem description using semantic search', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language description of the problem or requirements', }, categories: { type: 'array', items: { type: 'string' }, description: 'Optional: Pattern categories to search in', }, maxResults: { type: 'number', description: 'Maximum number of recommendations to return', default: 5, }, programmingLanguage: { type: 'string', description: 'Target programming language for implementation examples', }, }, required: ['query'], }, }, { name: 'search_patterns', description: 'Search patterns by keyword or semantic similarity', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query', }, searchType: { type: 'string', enum: ['keyword', 'semantic', 'hybrid'], default: 'hybrid', }, limit: { type: 'number', default: 10, }, }, required: ['query'], }, }, { name: 'get_pattern_details', description: 'Get detailed information about a specific pattern', inputSchema: { type: 'object', properties: { patternId: { type: 'string', description: 'Pattern ID to get details for', }, }, required: ['patternId'], }, }, { 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, }, }, }, }, ], }; }); // Handle tool calls with rate limiting this.server.setRequestHandler(CallToolRequestSchema, async request => { const { name, arguments: args } = request.params; // Apply rate limiting to tool calls const rateLimitedHandler = this.rateLimiter.wrapToolHandler( async (toolName: string, toolArgs: any) => { switch (toolName) { case 'find_patterns': return await this.handleFindPatterns(toolArgs); case 'search_patterns': return await this.handleSearchPatterns(toolArgs); case 'get_pattern_details': return await this.handleGetPatternDetails(toolArgs); case 'count_patterns': return await this.handleCountPatterns(toolArgs); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`); } }, name ); return await rateLimitedHandler(name, args); }); // List available resources this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'patterns', name: 'Design Patterns', description: 'Complete catalog of design patterns', mimeType: 'application/json', }, { uri: 'categories', name: 'Pattern Categories', description: 'All available pattern categories', mimeType: 'application/json', }, { uri: 'server_info', name: 'Server Information', description: 'Server status and configuration', mimeType: 'application/json', }, ], }; }); // Handle resource reads this.server.setRequestHandler(ReadResourceRequestSchema, async request => { const { uri } = request.params; switch (uri) { case 'patterns': return await this.handleReadPatterns(); case 'categories': return await this.handleReadCategories(); case 'server_info': return await this.handleReadServerInfo(); default: throw new McpError(ErrorCode.InvalidRequest, `Unknown resource: ${uri}`); } }); // Error handling this.server.onerror = error => { logger.error( 'mcp-server', 'Server error', error instanceof Error ? error : new Error(String(error)) ); }; } // Tool handlers private async handleFindPatterns(args: any): Promise<any> { const request = { id: crypto.randomUUID(), query: args.query, categories: args.categories || [], maxResults: args.maxResults || 5, programmingLanguage: args.programmingLanguage, }; const recommendations = await this.patternMatcher.findMatchingPatterns(request); return { content: [ { type: 'text', text: `Found ${recommendations.length} pattern recommendations:\n\n` + recommendations .map( (rec, index) => `${index + 1}. **${rec.pattern.name}** (${rec.pattern.category})\n` + ` Confidence: ${(rec.confidence * 100).toFixed(1)}%\n` + ` Rationale: ${rec.justification.primaryReason}\n` + ` Benefits: ${rec.justification.benefits.join(', ')}\n` ) .join('\n'), }, ], }; } private async handleSearchPatterns(args: any): Promise<any> { const query = { text: args.query, filters: args.filters || {}, options: { limit: args.limit || 10, includeMetadata: true, }, }; const results = await this.semanticSearch.search(query); return { content: [ { type: 'text', text: `Search results for "${args.query}":\n\n` + results .map( (result, index) => `${index + 1}. **${result.pattern.name}** (${result.pattern.category})\n` + ` Score: ${(result.score * 100).toFixed(1)}%\n` + ` Description: ${result.pattern.description}\n` ) .join('\n'), }, ], }; } private async handleGetPatternDetails(args: any): Promise<any> { const pattern = this.db.queryOne( ` SELECT id, name, category, description, when_to_use, benefits, drawbacks, use_cases, complexity, tags, examples, created_at FROM patterns WHERE id = ? `, [args.patternId] ); if (!pattern) { // Try to find similar patterns using semantic search const similarPatterns = await this.semanticSearch.search({ text: args.patternId, options: { limit: 3, includeMetadata: true, }, }); if (similarPatterns.length > 0) { return { content: [ { type: 'text', text: `Pattern "${args.patternId}" not found. Here are similar patterns:\n\n${similarPatterns .map( (p, i) => `${i + 1}. **${p.pattern.name}** (${p.pattern.category})\n ${p.pattern.description}\n Score: ${(p.score * 100).toFixed(1)}%` ) .join('\n\n')}`, }, ], }; } else { return { content: [ { type: 'text', text: `Pattern "${args.patternId}" not found and no similar patterns were found.`, }, ], }; } } const implementations = this.db.query( ` SELECT language, code, explanation FROM pattern_implementations WHERE pattern_id = ? LIMIT 3 `, [args.patternId] ); // Parse code examples if available let examplesText = ''; if (pattern.examples) { try { const examples = JSON.parse(pattern.examples); const exampleKeys = Object.keys(examples); if (exampleKeys.length > 0) { examplesText = '\n\n**Code Examples:**\n'; exampleKeys.forEach(lang => { const example = examples[lang]; examplesText += `\n### ${lang.charAt(0).toUpperCase() + lang.slice(1)}\n`; if (example.description) { examplesText += `${example.description}\n\n`; } examplesText += `\`\`\`${lang}\n${example.code}\n\`\`\`\n`; }); } } catch (e) { // If parsing fails, skip examples } } return { content: [ { type: 'text', text: `# ${pattern.name} (${pattern.category})\n\n` + `**Description:** ${pattern.description}\n\n` + `**When to Use:** ${parseArrayProperty(pattern.when_to_use).join(', ')}\n\n` + `**Benefits:** ${parseArrayProperty(pattern.benefits).join(', ')}\n\n` + `**Drawbacks:** ${parseArrayProperty(pattern.drawbacks).join(', ')}\n\n` + `**Use Cases:** ${parseArrayProperty(pattern.use_cases).join(', ')}\n\n` + `**Complexity:** ${pattern.complexity}\n\n` + `**Tags:** ${parseTags(pattern.tags).join(', ')}\n` + examplesText + (implementations.length > 0 ? `\n\n**Implementations:**\n` + implementations .map( impl => `\n### ${impl.language}\n\`\`\`${impl.language.toLowerCase()}\n${impl.code}\n\`\`\`\n${impl.explanation}` ) .join('\n') : ''), }, ], }; } private async handleCountPatterns(args: any): Promise<any> { try { // OPTIMIZATION: Use COUNT instead of loading all rows const totalResult = this.db.queryOne<{ total: number }>( 'SELECT COUNT(*) as total FROM patterns' ); const total = totalResult?.total || 0; if (args.includeDetails) { // Get category breakdown efficiently const breakdown = this.db.query<{ category: string; count: number }>( 'SELECT category, COUNT(*) as count FROM patterns GROUP BY category ORDER BY count DESC' ); 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 McpError( ErrorCode.InternalError, `Pattern count failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } // Resource handlers private async handleReadPatterns(): Promise<any> { // OPTIMIZATION: Add pagination with LIMIT to prevent loading all 574+ patterns const patterns = this.db.query( 'SELECT id, name, category, description, complexity, tags FROM patterns ORDER BY name LIMIT 100' ); return { contents: [ { uri: 'patterns', mimeType: 'application/json', text: JSON.stringify(patterns, null, 2), }, ], }; } private async handleReadCategories(): Promise<any> { const categories = this.db.query(` SELECT category, COUNT(*) as count FROM patterns GROUP BY category ORDER BY category `); return { contents: [ { uri: 'categories', mimeType: 'application/json', text: JSON.stringify(categories, null, 2), }, ], }; } private async handleReadServerInfo(): Promise<any> { const info = { name: 'Design Patterns MCP Server', version: '0.1.0', status: 'running', database: { path: this.config.databasePath, patternCount: this.db.queryOne('SELECT COUNT(*) as count FROM patterns')?.count || 0, }, features: { semanticSearch: true, llmBridge: this.config.enableLLM, caching: true, }, config: { logLevel: this.config.logLevel, maxConcurrentRequests: this.config.maxConcurrentRequests, }, }; return { contents: [ { uri: 'server_info', mimeType: 'application/json', text: JSON.stringify(info, null, 2), }, ], }; } async initialize(): Promise<void> { try { logger.info('mcp-server', 'Initializing Design Patterns MCP Server', { databasePath: this.config.databasePath, logLevel: this.config.logLevel, }); await this.db.initialize(); await this.migrationManager.initialize(); await this.migrationManager.migrate(); await this.patternSeeder.seedAll(); // LLMBridge doesn't require initialization if (this.llmBridge) { logger.info('mcp-server', 'LLM Bridge configured'); } logger.info('mcp-server', 'Design Patterns MCP Server initialized successfully'); } catch (error) { logger.error( 'mcp-server', 'Failed to initialize server', error instanceof Error ? error : new Error(String(error)) ); throw error; } } async start(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); logger.info('mcp-server', 'Server started and listening on stdio'); } async stop(): Promise<void> { try { await this.db.close(); await this.server.close(); logger.info('mcp-server', 'Server stopped'); } catch (error) { logger.error( 'mcp-server', 'Error stopping server', error instanceof Error ? error : new Error(String(error)) ); throw error; } } } // Export server creation function export function createDesignPatternsServer(config: MCPServerConfig): DesignPatternsMCPServer { return new DesignPatternsMCPServer(config); } // Main execution when run directly async function main(): Promise<void> { // Get the directory of the current module const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Resolve path relative to project root const isCompiled = __dirname.includes('dist'); const projectRoot = isCompiled ? path.resolve(__dirname, '..', '..') : path.resolve(__dirname, '..'); const defaultDbPath = path.join(projectRoot, 'data', 'design-patterns.db'); const config: MCPServerConfig = { databasePath: process.env.DATABASE_PATH || defaultDbPath, logLevel: (process.env.LOG_LEVEL as any) || 'info', enableLLM: process.env.ENABLE_LLM === 'true', maxConcurrentRequests: parseInt(process.env.MAX_CONCURRENT_REQUESTS || '10'), }; const server = createDesignPatternsServer(config); try { await server.initialize(); await server.start(); } catch (error) { logger.error( 'main', 'Failed to start server', error instanceof Error ? error : new Error(String(error)) ); process.exit(1); } // Graceful shutdown const shutdown = async (signal: string) => { logger.info('main', `Received ${signal}, shutting down gracefully`); try { await server.stop(); process.exit(0); } catch (error) { logger.error( 'main', 'Error during shutdown', error instanceof Error ? error : new Error(String(error)) ); process.exit(1); } }; process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); } // Run if this is the main module if (import.meta.url === `file://${process.argv[1]}`) { main().catch(error => { console.error('Fatal error:', error); process.exit(1); }); }

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