Skip to main content
Glama
server.ts14.2 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { CallToolRequestSchema, CreateMessageRequest, CreateMessageResultSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, Tool, ToolSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; import { readFileSync } from "fs"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; import { ReviewCodeInputSchema, DetectModelInputSchema, FetchCommitInputSchema, FetchPRCommitsInputSchema, ReviewType, ReviewStrategy, } from "./types.js"; import { createModelPreferences, createFallbackHints, detectModelFromCoAuthors, fetchCommit, fetchPRCommits, } from "./utils.js"; const ToolInputSchema = ToolSchema.shape.inputSchema; type ToolInput = z.infer<typeof ToolInputSchema>; // Tool names enum ToolName { REVIEW_CODE = "review_code", DETECT_MODEL_FROM_AUTHORS = "detect_model_from_authors", FETCH_COMMIT = "fetch_commit", FETCH_PR_COMMITS = "fetch_pr_commits", } // Prompt names enum PromptName { CODE_REVIEW = "code_review", SERVER_INSTRUCTIONS = "server_instructions", } // Get the directory path for the current module const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Cached prompts loaded from markdown files let cachedPrompts: { adversarial: string; biasAware: string; instructions: string; } | null = null; // Function to load prompts from markdown files function loadPrompts() { if (cachedPrompts) { return cachedPrompts; } const promptsDir = join(__dirname, 'prompts'); const adversarial = readFileSync(join(promptsDir, 'adversarial-reviewer.md'), 'utf-8').trim(); const biasAware = readFileSync(join(promptsDir, 'bias-aware-reviewer.md'), 'utf-8').trim(); const instructions = readFileSync(join(promptsDir, 'server-instructions.md'), 'utf-8').trim(); cachedPrompts = { adversarial, biasAware, instructions, }; return cachedPrompts; } // Simple switch function to get the appropriate prompt function getReviewerPrompt(strategy: ReviewStrategy): string { const prompts = loadPrompts(); switch (strategy) { case "adversarial": return prompts.adversarial; case "bias_aware": return prompts.biasAware; } } // Input schema for manual review prompts const ManualReviewSchema = z.object({ code: z.string().optional().describe("Code snippet to review (optional if using file context)"), }); export const createServer = () => { // Load prompts at server creation time const prompts = loadPrompts(); const server = new Server( { name: "mcp-code-crosscheck", version: "0.0.1", }, { capabilities: { tools: {}, prompts: {}, }, instructions: prompts.instructions, } ); // Helper method to request sampling from client const requestSampling = async ( code: string, generationModel: string, reviewStrategy: ReviewStrategy, language?: string, context?: string, reviewType: ReviewType = "general" ) => { const preferences = createModelPreferences(generationModel); const fallbackHints = createFallbackHints(generationModel); // Combine metadata-based preferences with fallback hints const modelPreferences = { ...preferences, hints: fallbackHints, // Provide fallback hints for clients that don't support metadata metadata: { ...preferences.metadata, reviewStrategy: reviewStrategy } }; const codeBlock = language ? `\`\`\`${language}\n${code}\n\`\`\`` : `\`\`\`\n${code}\n\`\`\``; const contextText = context ? `\n\nContext: ${context}` : ""; const promptText = `Review this ${language || 'code'} and identify potential issues:${contextText}\n\n${codeBlock}`; // Get strategy-specific system prompt const systemPrompt = getReviewerPrompt(reviewStrategy); const request: CreateMessageRequest = { method: "sampling/createMessage", params: { messages: [ { role: "user", content: { type: "text", text: promptText, }, }, ], systemPrompt, modelPreferences, maxTokens: 2000, temperature: 0.2, }, }; return await server.request(request, CreateMessageResultSchema); }; // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { const tools: Tool[] = [ { name: ToolName.REVIEW_CODE, description: "Review code with bias mitigation using cross-model evaluation via client sampling. Default 'bias_aware' mode focuses on correctness with low false positives. Optional 'adversarial' mode provides thorough review but expect some false positives. Use adversarial for security-critical code or when you want maximum scrutiny.", inputSchema: zodToJsonSchema(ReviewCodeInputSchema) as ToolInput, }, { name: ToolName.DETECT_MODEL_FROM_AUTHORS, description: "Detect AI model from commit author information. Use this with data from GitHub sources.", inputSchema: zodToJsonSchema(DetectModelInputSchema) as ToolInput, }, { name: ToolName.FETCH_COMMIT, description: "Fetch commit details using GitHub CLI. Fallback tool - prefer GitHub MCP server if available.", inputSchema: zodToJsonSchema(FetchCommitInputSchema) as ToolInput, }, { name: ToolName.FETCH_PR_COMMITS, description: "Fetch PR commits using GitHub CLI. Fallback tool - prefer GitHub MCP server if available.", inputSchema: zodToJsonSchema(FetchPRCommitsInputSchema) as ToolInput, }, ]; return { tools }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (name === ToolName.REVIEW_CODE) { const validatedArgs = ReviewCodeInputSchema.parse(args); const { code, generationModel, language, context, reviewType = "general", reviewStrategy = "bias_aware" } = validatedArgs; try { // Request review from client using sampling const result = await requestSampling(code, generationModel, reviewStrategy, language, context, reviewType); // Extract the response text const responseText = typeof result.content === 'string' ? result.content : Array.isArray(result.content) ? result.content.find(c => c.type === 'text')?.text || '' : result.content.text || ''; // Add strategy indicator and warning for adversarial mode const strategyIndicator = reviewStrategy === "adversarial" ? "⚠️ Adversarial review completed - Some findings may be overly critical" : "✓ Bias-aware review completed"; // Return the markdown response with appended metadata return { content: [ { type: "text", text: responseText, }, { type: "text", text: `\n\n---\n**${strategyIndicator}**\n\n**Metadata:**\n- Review Model: ${result.model || "unknown"}\n- Generation Model: ${generationModel}\n- Strategy: ${reviewStrategy}`, }, ], }; } catch (error) { // Generate manual cross-model instructions when sampling fails const fallbackHints = createFallbackHints(generationModel); const recommendedModels = fallbackHints.map(h => h.name).join(', '); const codeBlock = language ? `\`\`\`${language}\n${code}\n\`\`\`` : `\`\`\`\n${code}\n\`\`\``; const contextText = context ? `\n\nContext: ${context}` : ""; const systemPrompt = getReviewerPrompt(reviewStrategy || "bias_aware"); const reviewPrompt = `${systemPrompt}\n\nReview this ${language || 'code'} and identify potential issues:${contextText}\n\n${codeBlock}`; return { content: [ { type: "text", text: `## Sampling Failed - Manual Cross-Model Review Required **Error:** ${error instanceof Error ? error.message : String(error)} **To ensure bias-resistant review, please run this prompt with ${recommendedModels} instead of ${generationModel}:** --- ${reviewPrompt} --- **Instructions:** 1. Copy the prompt above 2. Switch to a different model (recommended: ${recommendedModels}) 3. Run the prompt with the different model 4. This ensures bias-resistant evaluation by avoiding the model that generated the code (${generationModel}) **Why this matters:** Using the same model for both generation and review can introduce self-preference bias. Cross-model evaluation helps identify issues the original model might miss.`, }, ], structuredContent: { samplingFailed: true, generationModel, recommendedModels: fallbackHints.map(h => h.name), manualPrompt: reviewPrompt, error: error instanceof Error ? error.message : String(error) }, }; } } if (name === ToolName.DETECT_MODEL_FROM_AUTHORS) { const validatedArgs = DetectModelInputSchema.parse(args); const { authors } = validatedArgs; try { const detectedModel = detectModelFromCoAuthors(authors); return { content: [ { type: "text", text: detectedModel ? `Detected AI model: ${detectedModel}` : "No AI model detected from the provided authors", }, ], structuredContent: { detectedModel: detectedModel || null, authors: authors.length, }, }; } catch (error) { return { content: [ { type: "text", text: `Error detecting model: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } if (name === ToolName.FETCH_COMMIT) { const validatedArgs = FetchCommitInputSchema.parse(args); const { commitHash, repo } = validatedArgs; try { const commit = await fetchCommit(commitHash, repo); return { content: [ { type: "text", text: `## Commit Details\n\n**Hash:** ${commit.oid}\n**Author:** ${commit.authors[0]?.name}\n**Date:** ${commit.authoredDate}\n**Message:** ${commit.messageHeadline}\n\n${commit.messageBody}`, }, ], structuredContent: commit, }; } catch (error) { return { content: [ { type: "text", text: `Error fetching commit: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } if (name === ToolName.FETCH_PR_COMMITS) { const validatedArgs = FetchPRCommitsInputSchema.parse(args); const { prNumber, repo } = validatedArgs; try { const commits = await fetchPRCommits(prNumber, repo); return { content: [ { type: "text", text: `## PR #${prNumber} Commits\n\nFound ${commits.length} commits:\n\n${commits.map(c => `- ${c.oid.substring(0, 7)}: ${c.messageHeadline} (${c.authors[0]?.name})`).join('\n')}`, }, ], structuredContent: { commits, count: commits.length }, }; } catch (error) { return { content: [ { type: "text", text: `Error fetching PR commits: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } throw new Error(`Unknown tool: ${name}`); }); // List available prompts server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [ { name: PromptName.CODE_REVIEW, description: "Comprehensive code review covering security, performance, and maintainability. Returns structured markdown with 1-3 scale metrics.", arguments: [ { name: "code", description: "Code snippet to review (optional if using file context)", required: false, }, ], }, { name: PromptName.SERVER_INSTRUCTIONS, description: "Get comprehensive server usage instructions, capabilities, and workflow guidance. This is a fallback for when the client does not support server instructions.", arguments: [], }, ], }; }); // Handle prompt requests server.setRequestHandler(GetPromptRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (name === PromptName.CODE_REVIEW) { const validatedArgs = ManualReviewSchema.parse(args); const codeSection = validatedArgs.code ? `\n\nReview this code:\n\n\`\`\`\n${validatedArgs.code}\n\`\`\`` : "\n\nReview the code in the current context for potential issues"; // Use adversarial strategy as default for the general prompt const prompt = getReviewerPrompt("adversarial") + codeSection; return { messages: [ { role: "user", content: { type: "text", text: prompt, }, }, ], }; } if (name === PromptName.SERVER_INSTRUCTIONS) { // Return the loaded server instructions return { messages: [ { role: "user", content: { type: "text", text: prompts.instructions, }, }, ], }; } throw new Error(`Unknown prompt: ${name}`); }); const cleanup = async () => { // No cleanup needed for this server }; return { server, cleanup }; };

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/olaservo/mcp-code-crosscheck'

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