Skip to main content
Glama
code-suggestions.ts12 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import fs from 'fs-extra'; import path from 'path'; interface CodeSuggestion { file: string; line: number; type: 'performance' | 'security' | 'maintainability' | 'best_practice'; severity: 'low' | 'medium' | 'high'; message: string; suggestion: string; } export default function registerCodeSuggestionsTool(server: McpServer) { server.registerTool( 'code-suggestions', { title: 'Code Suggestions', description: 'Analyzes code files and provides suggestions for improvements.', inputSchema: { directory: z.string().describe('Directory path to analyze'), include_patterns: z.array(z.string()).optional().default(['**/*.{js,ts,jsx,tsx}']).describe('File patterns to include'), suggestion_types: z.array(z.enum(['performance', 'security', 'maintainability', 'best_practice'])).optional().default(['performance', 'security']).describe('Types of suggestions to generate'), max_suggestions: z.number().optional().default(50).describe('Maximum number of suggestions to return'), }, }, async (params: { directory: string; include_patterns?: string[]; suggestion_types?: ('performance' | 'security' | 'maintainability' | 'best_practice')[]; max_suggestions?: number; }) => { try { const { directory, include_patterns = ['**/*.{js,ts,jsx,tsx}'], suggestion_types = ['performance', 'security'], max_suggestions = 50 } = params; if (!(await fs.pathExists(directory))) { return { content: [{ type: 'text', text: `Error: Directory does not exist: ${directory}` }], isError: true, }; } const suggestions: CodeSuggestion[] = []; // Analyze files recursively const analyzeFile = async (filePath: string): Promise<void> => { try { const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); const ext = path.extname(filePath).toLowerCase(); for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; // Performance suggestions if (suggestion_types.includes('performance')) { // Check for console.log in production code if (line.includes('console.log') && !line.trim().startsWith('//')) { suggestions.push({ file: filePath, line: lineNumber, type: 'performance', severity: 'low', message: 'Console.log statements in production code', suggestion: 'Remove or replace console.log with proper logging' }); } // Check for synchronous file operations if (line.includes('fs.readFileSync') || line.includes('fs.writeFileSync')) { suggestions.push({ file: filePath, line: lineNumber, type: 'performance', severity: 'medium', message: 'Synchronous file operations', suggestion: 'Consider using asynchronous file operations for better performance' }); } // Check for large loops without optimization if (line.includes('for (let i = 0; i < array.length; i++)') && lines[i + 1]?.includes('array[i]')) { suggestions.push({ file: filePath, line: lineNumber, type: 'performance', severity: 'low', message: 'Traditional for loop', suggestion: 'Consider using array methods like forEach, map, or filter for better readability' }); } } // Security suggestions if (suggestion_types.includes('security')) { // Check for potential path traversal if (line.includes('path.join') && line.includes('..')) { suggestions.push({ file: filePath, line: lineNumber, type: 'security', severity: 'high', message: 'Potential path traversal vulnerability', suggestion: 'Validate and sanitize file paths to prevent directory traversal attacks' }); } // Check for eval usage if (line.includes('eval(')) { suggestions.push({ file: filePath, line: lineNumber, type: 'security', severity: 'high', message: 'Use of eval() function', suggestion: 'Avoid using eval() as it can execute arbitrary code. Consider safer alternatives.' }); } // Check for innerHTML usage if (line.includes('.innerHTML =')) { suggestions.push({ file: filePath, line: lineNumber, type: 'security', severity: 'medium', message: 'Direct innerHTML assignment', suggestion: 'Consider using textContent or createElement to prevent XSS attacks' }); } } // Maintainability suggestions if (suggestion_types.includes('maintainability')) { // Check for long functions if (line.includes('function ') || line.includes('const ') && line.includes('= (')) { // Count lines until next function or end of file let functionLines = 1; for (let j = i + 1; j < lines.length; j++) { if (lines[j].includes('function ') || (lines[j].includes('const ') && lines[j].includes('= ('))) { break; } functionLines++; } if (functionLines > 30) { suggestions.push({ file: filePath, line: lineNumber, type: 'maintainability', severity: 'medium', message: 'Long function detected', suggestion: `Consider breaking this function into smaller, more focused functions (currently ${functionLines} lines)` }); } } // Check for long lines if (line.length > 120) { suggestions.push({ file: filePath, line: lineNumber, type: 'maintainability', severity: 'low', message: 'Line too long', suggestion: 'Consider breaking long lines for better readability (current length: ' + line.length + ')' }); } } // Best practice suggestions if (suggestion_types.includes('best_practice')) { // Check for missing error handling if ((line.includes('async ') || line.includes('Promise')) && !line.includes('catch') && !line.includes('try')) { suggestions.push({ file: filePath, line: lineNumber, type: 'best_practice', severity: 'medium', message: 'Missing error handling', suggestion: 'Consider adding try-catch blocks or .catch() handlers for async operations' }); } // Check for missing TypeScript types if (ext === '.ts' && line.includes('function') && !line.includes(': ')) { suggestions.push({ file: filePath, line: lineNumber, type: 'best_practice', severity: 'low', message: 'Missing return type annotation', suggestion: 'Consider adding explicit return type annotations for better type safety' }); } } // Limit suggestions to prevent overwhelming output if (suggestions.length >= max_suggestions) { return; } } } catch (error) { // Ignore file reading errors } }; const analyzeDirectory = async (dir: string): Promise<void> => { try { const items = await fs.readdir(dir, { withFileTypes: true }); for (const item of items) { const itemPath = path.join(dir, item.name); if (item.isDirectory() && !item.name.startsWith('.') && item.name !== 'node_modules' && item.name !== 'dist') { await analyzeDirectory(itemPath); } else if (item.isFile()) { const isIncluded = include_patterns.some(pattern => { const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*').replace(/\./g, '\\.')); return regex.test(itemPath); }); if (isIncluded) { await analyzeFile(itemPath); } } if (suggestions.length >= max_suggestions) { break; } } } catch (error) { // Ignore directory reading errors } }; await analyzeDirectory(directory); // Sort suggestions by severity and type suggestions.sort((a, b) => { const severityOrder = { high: 3, medium: 2, low: 1 }; const typeOrder = { security: 4, performance: 3, maintainability: 2, best_practice: 1 }; if (severityOrder[a.severity] !== severityOrder[b.severity]) { return severityOrder[b.severity] - severityOrder[a.severity]; } return typeOrder[b.type] - typeOrder[a.type]; }); // Generate output let output = `# Code Analysis & Suggestions **Analyzed directory**: ${directory} **Total suggestions**: ${suggestions.length} **Suggestion types**: ${suggestion_types.join(', ')} `; if (suggestions.length === 0) { output += `## ✅ No suggestions found! Your code follows best practices! Great job maintaining clean, secure, and efficient code. `; } else { // Group suggestions by type const suggestionsByType: { [key: string]: CodeSuggestion[] } = {}; for (const suggestion of suggestions) { if (!suggestionsByType[suggestion.type]) { suggestionsByType[suggestion.type] = []; } suggestionsByType[suggestion.type].push(suggestion); } for (const [type, typeSuggestions] of Object.entries(suggestionsByType)) { output += `## ${type.charAt(0).toUpperCase() + type.slice(1)} Suggestions (${typeSuggestions.length})\n\n`; for (const suggestion of typeSuggestions) { output += `### ${suggestion.severity.toUpperCase()} - ${suggestion.message} - **File**: ${suggestion.file} - **Line**: ${suggestion.line} - **Suggestion**: ${suggestion.suggestion} `; } } } return { content: [{ type: 'text', text: output }], }; } catch (error: any) { return { content: [{ type: 'text', text: `Error analyzing code for suggestions: ${error.message}` }], isError: true, }; } } ); }

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/Yussefgafer/MyMCP'

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