Skip to main content
Glama
index.js6.62 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { readFileSync, readdirSync } from 'fs'; import { join, dirname, basename } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const CRITICS_DIR = join(__dirname, '..', 'ai-review', 'critic'); const FIXERS_DIR = join(__dirname, '..', 'ai-review', 'fixer'); // Load available critics from the critic directory function loadCritics() { try { const criticFiles = readdirSync(CRITICS_DIR).filter(file => file.endsWith('.md')); const critics = {}; for (const file of criticFiles) { const criticName = basename(file, '.md'); const criticPath = join(CRITICS_DIR, file); const criticContent = readFileSync(criticPath, 'utf-8'); critics[criticName] = { name: criticName, description: `Code review using the ${criticName} critic framework`, content: criticContent }; } return critics; } catch (error) { console.error('Error loading critics:', error); return {}; } } // Load available fixers from the fixer directory function loadFixers() { try { const fixerFiles = readdirSync(FIXERS_DIR).filter(file => file.endsWith('.md')); const fixers = {}; for (const file of fixerFiles) { const fixerName = basename(file, '.md'); const fixerPath = join(FIXERS_DIR, file); const fixerContent = readFileSync(fixerPath, 'utf-8'); fixers[fixerName] = { name: fixerName, description: `Apply ${fixerName} fixing strategy to address critic issues`, content: fixerContent }; } return fixers; } catch (error) { console.error('Error loading fixers:', error); return {}; } } const critics = loadCritics(); const fixers = loadFixers(); // Create MCP server const server = new Server( { name: 'mcp-review-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Create user-friendly tool names dynamically function createDynamicTools() { const criticTools = Object.keys(critics).map(criticName => ({ name: `review-${criticName}`, description: `Review code using the ${criticName} framework from critic/${criticName}.md`, inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'The code or project content to review', }, language: { type: 'string', description: 'Programming language or context of the code', default: 'unknown', }, filePath: { type: 'string', description: 'Optional: Path to the file being reviewed', } }, required: ['code'], } })); const fixerTools = Object.keys(fixers).map(fixerName => ({ name: `fix-${fixerName}`, description: `Apply ${fixerName} fixing strategy from fixer/${fixerName}.md`, inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'The original code that was reviewed', }, critiqueResults: { type: 'string', description: 'The results from the critic analysis', }, language: { type: 'string', description: 'Programming language or context of the code', default: 'unknown', }, filePath: { type: 'string', description: 'Optional: Path to the file being fixed', } }, required: ['code', 'critiqueResults'], } })); return { criticTools, fixerTools }; } const { criticTools, fixerTools } = createDynamicTools(); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [...criticTools, ...fixerTools] }; }); // Handle tool calls with dynamic parsing server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; // Parse review-name syntax if (name.startsWith('review-')) { const criticName = name.replace('review-', ''); const critic = critics[criticName]; if (!critic) { throw new Error(`Unknown critic: ${criticName}. Available critics: ${Object.keys(critics).join(', ')}`); } const { code, language = 'unknown', filePath = 'unknown' } = args; const critique = performCritique(critic, code, language, filePath); return { content: [ { type: 'text', text: critique, }, ], }; } // Parse fix-name syntax if (name.startsWith('fix-')) { const fixerName = name.replace('fix-', ''); const fixer = fixers[fixerName]; if (!fixer) { throw new Error(`Unknown fixer: ${fixerName}. Available fixers: ${Object.keys(fixers).join(', ')}`); } const { code, critiqueResults, language = 'unknown', filePath = 'unknown' } = args; const fixedCode = performFix(fixer, code, critiqueResults, language, filePath); return { content: [ { type: 'text', text: fixedCode, }, ], }; } throw new Error(`Unknown tool: ${name}. Use review-<name> or fix-<name> syntax.`); }); // Perform critique using the specified critic framework function performCritique(critic, code, language, filePath) { // Include the critic framework content directly since the agent // won't have access to files outside the user's project workspace const prompt = `## ${critic.name} Critic Framework: ${critic.content} ## Code to Review: **File:** ${filePath} **Language:** ${language} \`\`\`${language} ${code} \`\`\``; return prompt; } // Perform fix using the specified fixer strategy function performFix(fixer, code, critiqueResults, language, filePath) { // Include the fixer strategy content directly const prompt = `## ${fixer.name} Fixer Strategy: ${fixer.content} ## Original Code: **File:** ${filePath} **Language:** ${language} \`\`\`${language} ${code} \`\`\` ## Critic Analysis Results: ${critiqueResults}`; return prompt; } // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCP Review Server running on stdio'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); });

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/jonels-msft/mcp-review'

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