Skip to main content
Glama
JagjeevanAK

OpenFoodFacts-mcp

by JagjeevanAK
github-issues-tool.ts25.8 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { requestSampling, createGitHubRoadmapRequest } from "../sampling/sampling-service.js"; /** * Fetch GitHub issues from the Open Food Facts organization * @param repo Repository name within the openfoodfacts organization * @param state Issue state (open, closed, all) * @param labels Comma-separated list of labels to filter by * @returns Array of GitHub issues */ async function fetchGitHubIssues(repo: string, state: string = "open", labels: string = "") { try { // Construct the GitHub API URL const baseUrl = "https://api.github.com/repos/openfoodfacts"; const url = `${baseUrl}/${repo}/issues?state=${state}&per_page=100${labels ? `&labels=${labels}` : ""}`; const response = await fetch(url, { headers: { "Accept": "application/vnd.github.v3+json", "User-Agent": "OpenFoodFacts-MCP-Server" } }); if (!response.ok) { throw new Error(`GitHub API error: ${response.status} - ${response.statusText}`); } const issues = await response.json(); return issues; } catch (error: any) { console.error(`Error fetching GitHub issues:`, error); throw new Error(`Failed to fetch GitHub issues: ${error.message}`); } } /** * Determine the issue type (bug, enhancement, feature, etc.) based on labels or content * @param issue GitHub issue data * @returns Issue type category */ function determineIssueType(issue: any): string { // Check labels first if (issue.labels && Array.isArray(issue.labels)) { const labelNames = issue.labels.map((label: any) => label.name.toLowerCase()); if (labelNames.some((name: string) => name.includes('bug') || name.includes('error') || name.includes('fix'))) { return 'bug'; } if (labelNames.some((name: string) => name.includes('enhancement') || name.includes('improve'))) { return 'enhancement'; } if (labelNames.some((name: string) => name.includes('feature') || name.includes('new'))) { return 'feature'; } } // Check title as fallback const titleLower = issue.title.toLowerCase(); if (titleLower.includes('bug') || titleLower.includes('fix') || titleLower.includes('error')) { return 'bug'; } if (titleLower.includes('enhance') || titleLower.includes('improve')) { return 'enhancement'; } if (titleLower.includes('feature') || titleLower.includes('add') || titleLower.startsWith('new')) { return 'feature'; } // Default return 'general'; } /** * Determine if the issue is primarily frontend or backend related * @param issue GitHub issue data * @returns Component type (frontend, backend, other) */ function determineComponentType(issue: any): string { // Check labels first if (issue.labels && Array.isArray(issue.labels)) { const labelNames = issue.labels.map((label: any) => label.name.toLowerCase()); if (labelNames.some((name: string) => name.includes('frontend') || name.includes('ui') || name.includes('app') || name.includes('mobile'))) { return 'frontend'; } if (labelNames.some((name: string) => name.includes('backend') || name.includes('server') || name.includes('api') || name.includes('database'))) { return 'backend'; } } // Check body content if (issue.body) { const bodyLower = issue.body.toLowerCase(); // Frontend indicators if (bodyLower.includes('javascript') || bodyLower.includes('css') || bodyLower.includes('html') || bodyLower.includes('react') || bodyLower.includes('vue') || bodyLower.includes('angular') || bodyLower.includes('ui') || bodyLower.includes('user interface') || bodyLower.includes('mobile app')) { return 'frontend'; } // Backend indicators if (bodyLower.includes('server') || bodyLower.includes('database') || bodyLower.includes('api') || bodyLower.includes('perl') || bodyLower.includes('mongodb') || bodyLower.includes('endpoint')) { return 'backend'; } } return 'other'; } /** * Create a specialized system prompt based on issue type and component * @param issueType Issue type (bug, enhancement, feature, general) * @param componentType Component type (frontend, backend, other) * @returns Specialized system prompt */ function createSpecializedPrompt(issueType: string, componentType: string): string { // Base prompt sections that all prompts will include const basePromptSections = [ "ISSUE SUMMARY: Brief summary of the issue", "TESTING STRATEGY: How to test the solution", "POTENTIAL CHALLENGES: Any obstacles or considerations" ]; // Specialized prompts by issue type if (issueType === 'bug') { const bugSections = [ "ROOT CAUSES: Identify the likely cause of this bug with evidence from the issue description", "REPRODUCTION STEPS: Clear steps to reproduce the problem", "IMPACT ASSESSMENT: How this bug affects users and system reliability", "BUG FIX APPROACH: Targeted solution focused on fixing the specific defect", "VERIFICATION PROCESS: How to verify the bug is actually fixed" ]; // Further specialize by component type if (componentType === 'frontend') { return `You are an expert frontend developer specializing in the Open Food Facts ecosystem. Analyze this bug issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...bugSections, "BROWSER COMPATIBILITY: Any browser-specific concerns", "UI/UX CONSIDERATIONS: How the fix should maintain design consistency"].join("\n")}`; } else if (componentType === 'backend') { return `You are an expert backend developer specializing in the Open Food Facts ecosystem. Analyze this bug issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...bugSections, "DATA INTEGRITY: How this bug affects data quality", "PERFORMANCE IMPLICATIONS: Any performance concerns with the fix"].join("\n")}`; } else { return `You are an expert software developer specializing in the Open Food Facts ecosystem. Analyze this bug issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...bugSections].join("\n")}`; } } else if (issueType === 'enhancement') { const enhancementSections = [ "CURRENT LIMITATIONS: What's currently limiting the functionality", "IMPROVEMENT OPPORTUNITIES: Specific areas that can be enhanced", "IMPLEMENTATION APPROACHES: Several possible approaches to implement the enhancement", "RECOMMENDED APPROACH: The best approach with justification and trade-offs", "IMPLEMENTATION STEPS: Step-by-step guide to implement the enhancement" ]; if (componentType === 'frontend') { return `You are an expert frontend developer specializing in the Open Food Facts ecosystem. Analyze this enhancement issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...enhancementSections, "UI/UX IMPROVEMENTS: How this enhances user experience", "ACCESSIBILITY CONSIDERATIONS: How to ensure the enhancement is accessible"].join("\n")}`; } else if (componentType === 'backend') { return `You are an expert backend developer specializing in the Open Food Facts ecosystem. Analyze this enhancement issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...enhancementSections, "API DESIGN PRINCIPLES: How this fits into the API architecture", "SCALABILITY CONSIDERATIONS: How this enhancement scales"].join("\n")}`; } else { return `You are an expert software developer specializing in the Open Food Facts ecosystem. Analyze this enhancement issue and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...enhancementSections].join("\n")}`; } } else if (issueType === 'feature') { const featureSections = [ "USER VALUE: Why this feature matters to users", "FEATURE SCOPE: Clear definition of what's in and out of scope", "DESIGN CONSIDERATIONS: Key design decisions for implementing this feature", "ARCHITECTURE IMPLICATIONS: How this feature fits into the overall system", "IMPLEMENTATION PLAN: Comprehensive plan with technical requirements", "CODE EXAMPLES: Pseudo-code or actual code examples where appropriate" ]; if (componentType === 'frontend') { return `You are an expert frontend developer specializing in the Open Food Facts ecosystem. Analyze this feature request and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...featureSections, "UI/UX DESIGN: Wireframes or description of the user interface", "FRONTEND TECHNOLOGIES: Recommended technologies for implementation"].join("\n")}`; } else if (componentType === 'backend') { return `You are an expert backend developer specializing in the Open Food Facts ecosystem. Analyze this feature request and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...featureSections, "DATA MODELING: How data should be structured for this feature", "API DESIGN: Required API endpoints and their specifications"].join("\n")}`; } else { return `You are an expert software developer specializing in the Open Food Facts ecosystem. Analyze this feature request and provide detailed recommendations with these sections:\n\n${[...basePromptSections, ...featureSections].join("\n")}`; } } else { // General default prompt return "You are an expert software developer and open source contributor specializing in the Open Food Facts ecosystem. Analyze the provided GitHub issue and provide detailed recommendations with these sections:\n\n1. ISSUE SUMMARY: Brief summary of the issue\n2. ROOT CAUSES: Identify potential underlying causes\n3. SOLUTION APPROACHES: Several possible implementation approaches\n4. RECOMMENDED SOLUTION: The best approach with justification\n5. IMPLEMENTATION STEPS: Detailed steps to implement the solution\n6. CODE EXAMPLES: Pseudo-code or actual code examples where appropriate\n7. TESTING STRATEGY: How to test the solution\n8. POTENTIAL CHALLENGES: Any obstacles or considerations"; } } /** * Create a sampling request for analyzing a GitHub issue with specialized prompts * @param issue GitHub issue data * @returns Sampling request configuration */ function createIssueAnalysisRequest(issue: any, orgRepoContext: string) { // Determine issue and component type const issueType = determineIssueType(issue); const componentType = determineComponentType(issue); // Get specialized prompt const systemPrompt = createSpecializedPrompt(issueType, componentType); return { messages: [ { role: "user" as const, content: { type: "text" as const, text: `Analyze this GitHub issue from the Open Food Facts ${orgRepoContext} repository and provide detailed recommendations:\n\n${JSON.stringify(issue, null, 2)}` } } ], modelPreferences: { hints: [ { name: "claude-3" }, { name: "gemini-2.0-flash" }, { name: "gemini-2.0" }, { name: "gemini-2.5-pro" }, { name: "sonnet" }, { name: "gpt-4.1" }, { name: "gpt-4o" } ], intelligencePriority: 0.9, speedPriority: 0.5, costPriority: 0.3 }, systemPrompt, includeContext: "thisServer" as const, temperature: 0.2, maxTokens: 4000, stopSequences: ["[END]"] }; } /** * Create specialized pattern analysis prompt based on issue labels and focus * @param label Label used to filter issues * @returns Specialized pattern analysis prompt */ function createSpecializedPatternPrompt(label: string): string { const labelLower = label.toLowerCase(); // Base sections all pattern analysis will include const baseSections = [ "COMMON THEMES: Identify recurring issues or themes", "PRIORITY RECOMMENDATIONS: Which issues should be addressed first and why", "RESOURCE ALLOCATION: How to allocate developer resources efficiently" ]; if (labelLower.includes('bug') || labelLower.includes('error') || labelLower.includes('fix')) { const bugSections = [ "BUG PATTERNS: Identify recurring bug patterns across issues", "ROOT CAUSE ANALYSIS: Common underlying causes for these bugs", "PREVENTION STRATEGIES: How to prevent similar bugs in the future", "TECHNICAL DEBT: Areas where code quality might be causing bugs", "QUALITY ASSURANCE: Recommendations for improving QA processes" ]; return `You are an expert software quality analyst specializing in the Open Food Facts ecosystem. Analyze these bug-related GitHub issues and identify patterns and systemic improvements with these sections:\n\n${[...baseSections, ...bugSections].join("\n")}`; } if (labelLower.includes('enhancement') || labelLower.includes('improve')) { const enhancementSections = [ "ENHANCEMENT PATTERNS: Common areas targeted for improvement", "USER EXPERIENCE: How these enhancements collectively impact UX", "TECHNICAL MODERNIZATION: Opportunities for technical upgrades", "IMPLEMENTATION STRATEGY: How to approach these enhancements holistically", "PRIORITIZATION FRAMEWORK: Criteria for selecting which enhancements to implement first" ]; return `You are an expert software architect specializing in the Open Food Facts ecosystem. Analyze these enhancement-related GitHub issues and identify patterns and systemic improvements with these sections:\n\n${[...baseSections, ...enhancementSections].join("\n")}`; } if (labelLower.includes('feature') || labelLower.includes('new')) { const featureSections = [ "FEATURE VISION: How these features collectively shape the product", "STRATEGIC ALIGNMENT: How features align with project goals", "IMPLEMENTATION COMPLEXITY: Assessment of development complexity", "FEATURE DEPENDENCIES: How features relate to and depend on each other", "ROADMAP PLANNING: Suggested sequence for feature implementation" ]; return `You are an expert product manager specializing in the Open Food Facts ecosystem. Analyze these feature-related GitHub issues and identify patterns and systemic improvements with these sections:\n\n${[...baseSections, ...featureSections].join("\n")}`; } if (labelLower.includes('frontend') || labelLower.includes('ui') || labelLower.includes('mobile')) { const frontendSections = [ "UI/UX PATTERNS: Recurring user interface concerns", "FRONTEND ARCHITECTURE: Systemic improvement opportunities", "DESIGN SYSTEM: Recommendations for UI consistency", "FRONTEND PERFORMANCE: Performance optimization opportunities", "CROSS-PLATFORM CONSIDERATIONS: Issues affecting multiple platforms" ]; return `You are an expert frontend developer specializing in the Open Food Facts ecosystem. Analyze these frontend-related GitHub issues and identify patterns and systemic improvements with these sections:\n\n${[...baseSections, ...frontendSections].join("\n")}`; } if (labelLower.includes('backend') || labelLower.includes('api') || labelLower.includes('server')) { const backendSections = [ "API DESIGN PATTERNS: Recurring API design concerns", "DATABASE OPTIMIZATIONS: Data storage and retrieval improvements", "SCALABILITY CONCERNS: Issues affecting system scaling", "BACKEND ARCHITECTURE: Architectural improvement opportunities", "PERFORMANCE BOTTLENECKS: Areas of backend performance concern" ]; return `You are an expert backend developer specializing in the Open Food Facts ecosystem. Analyze these backend-related GitHub issues and identify patterns and systemic improvements with these sections:\n\n${[...baseSections, ...backendSections].join("\n")}`; } // Default pattern analysis prompt return "You are an expert software developer and open source contributor specializing in the Open Food Facts ecosystem. Analyze the provided GitHub issues and identify patterns and systemic improvements with these sections:\n\n1. COMMON THEMES: Identify recurring issues or themes\n2. SYSTEMIC PROBLEMS: Potential underlying architectural or process issues\n3. PRIORITY RECOMMENDATIONS: Which issues should be addressed first and why\n4. LONG-TERM IMPROVEMENTS: Strategic changes to reduce similar issues\n5. SUGGESTED PROCESS CHANGES: How the development process might be improved\n6. RESOURCE ALLOCATION: How to allocate developer resources efficiently"; } /** * Create a sampling request for analyzing multiple GitHub issues to identify patterns * @param issues Array of GitHub issue data * @returns Sampling request configuration */ function createIssuesPatternAnalysisRequest(issues: any[], orgRepoContext: string, label: string = "") { // Extract essential data to avoid token limit issues const simplifiedIssues = issues.map(issue => ({ number: issue.number, title: issue.title, labels: issue.labels.map((l: any) => l.name), created_at: issue.created_at, body: issue.body?.substring(0, 500) + (issue.body?.length > 500 ? "..." : ""), comments: issue.comments })); // Get specialized pattern analysis prompt based on label const systemPrompt = createSpecializedPatternPrompt(label); return { messages: [ { role: "user" as const, content: { type: "text" as const, text: `Analyze these GitHub issues from the Open Food Facts ${orgRepoContext} repository and identify patterns and systemic improvements:\n\n${JSON.stringify(simplifiedIssues, null, 2)}` } } ], modelPreferences: { hints: [ { name: "claude-3" }, { name: "gemini-2.0-flash" }, { name: "gemini-2.0" }, { name: "gemini-2.5-pro" }, { name: "sonnet" }, { name: "gpt-4.1" }, { name: "gpt-4o" } ], intelligencePriority: 0.9, speedPriority: 0.4, costPriority: 0.3 }, systemPrompt, includeContext: "thisServer" as const, temperature: 0.3, maxTokens: 4000, stopSequences: ["[END]"] }; } /** * Register GitHub issues analysis tools with the MCP server */ export function registerGitHubIssuesTools(server: McpServer) { // Register the tool to analyze a single GitHub issue server.tool( "analyzeGitHubIssue", { repo: z.string().describe("The repository name within the openfoodfacts organization"), issueNumber: z.number().describe("The GitHub issue number to analyze") }, async ({ repo, issueNumber }) => { try { // Fetch the specific issue by constructing the URL directly const baseUrl = "https://api.github.com/repos/openfoodfacts"; const url = `${baseUrl}/${repo}/issues/${issueNumber}`; const response = await fetch(url, { headers: { "Accept": "application/vnd.github.v3+json", "User-Agent": "OpenFoodFacts-MCP-Server" } }); if (!response.ok) { return { isError: true, content: [ { type: "text", text: `GitHub issue #${issueNumber} not found or API error: ${response.status}` } ] }; } const issue = await response.json(); // Create sampling request for AI analysis with specialized prompt const samplingRequest = createIssueAnalysisRequest(issue, `${repo}`); // Request LLM completion through the client const response2 = await requestSampling(server, samplingRequest); return { content: [ { type: "text", text: `Analysis of GitHub Issue #${issueNumber} - ${issue.title}:\n\n${response2.content.text || "No analysis available"}` } ] }; } catch (error) { console.error("Error in analyzeGitHubIssue tool:", error); return { isError: true, content: [ { type: "text", text: `Failed to analyze GitHub issue: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // Register the tool to analyze multiple GitHub issues by label server.tool( "analyzeGitHubIssuesByLabel", { repo: z.string().describe("The repository name within the openfoodfacts organization"), label: z.string().describe("The label to filter issues by"), state: z.enum(["open", "closed", "all"]).default("open").describe("Filter issues by state"), limit: z.number().default(10).describe("Maximum number of issues to analyze") }, async ({ repo, label, state, limit }) => { try { // Fetch issues with the specific label const issues = await fetchGitHubIssues(repo, state, label); if (!issues || issues.length === 0) { return { isError: true, content: [ { type: "text", text: `No GitHub issues found with label '${label}' in state '${state}'` } ] }; } // Limit the number of issues to analyze const limitedIssues = issues.slice(0, limit); // Create sampling request for pattern analysis with specialized prompt const samplingRequest = createIssuesPatternAnalysisRequest(limitedIssues, `${repo}`, label); // Request LLM completion through the client const response = await requestSampling(server, samplingRequest); return { content: [ { type: "text", text: `Analysis of ${limitedIssues.length} GitHub Issues with label '${label}':\n\n${response.content.text || "No analysis available"}` } ] }; } catch (error) { console.error("Error in analyzeGitHubIssuesByLabel tool:", error); return { isError: true, content: [ { type: "text", text: `Failed to analyze GitHub issues: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // Register the tool to recommend a prioritized roadmap based on open issues server.tool( "createGitHubIssueRoadmap", { repo: z.string().describe("The repository name within the openfoodfacts organization"), timeframe: z.enum(["short", "medium", "long"]).default("medium").describe("Timeframe for the roadmap"), focusArea: z.string().optional().describe("Optional focus area (e.g., 'mobile', 'backend', 'ui')") }, async ({ repo, timeframe, focusArea }) => { try { // Fetch open issues let issues = await fetchGitHubIssues(repo, "open"); if (!issues || issues.length === 0) { return { isError: true, content: [ { type: "text", text: `No open GitHub issues found in repository '${repo}'` } ] }; } // Filter by focus area if provided if (focusArea) { issues = issues.filter((issue: any) => { // Check if focus area appears in title, body, or labels const focusAreaLower = focusArea.toLowerCase(); const titleMatch = issue.title.toLowerCase().includes(focusAreaLower); const bodyMatch = issue.body && issue.body.toLowerCase().includes(focusAreaLower); const labelsMatch = issue.labels.some((l: any) => l.name.toLowerCase().includes(focusAreaLower)); return titleMatch || bodyMatch || labelsMatch; }); if (issues.length === 0) { return { isError: true, content: [ { type: "text", text: `No open GitHub issues found related to '${focusArea}'` } ] }; } } // Limit to a reasonable number for analysis const limitedIssues = issues.slice(0, 30); // Prepare simplified issues array for the roadmap request const simplifiedIssues = limitedIssues.map((i: any) => ({ number: i.number, title: i.title, labels: i.labels.map((l: any) => l.name), created_at: i.created_at, updated_at: i.updated_at, comments: i.comments })); // Use the dedicated sampling request function for roadmap generation const samplingRequest = createGitHubRoadmapRequest( simplifiedIssues, repo, timeframe, focusArea ); // Request LLM completion through the client const response = await requestSampling(server, samplingRequest); return { content: [ { type: "text", text: `${timeframe.charAt(0).toUpperCase() + timeframe.slice(1)}-Term Roadmap for ${repo}${focusArea ? ` (${focusArea})` : ''}:\n\n${response.content.text || "No roadmap available"}` } ] }; } catch (error) { console.error("Error in createGitHubIssueRoadmap tool:", error); return { isError: true, content: [ { type: "text", text: `Failed to create GitHub issues roadmap: ${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/JagjeevanAK/OpenFoodFacts-MCP'

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