Skip to main content
Glama
JagjeevanAK

OpenFoodFacts-mcp

by JagjeevanAK
resource-registry.ts6.39 kB
import { handleFileContent } from "./file-content.js"; import { handleProjectInfo, handleProjectStructure } from "./project-info.js"; import { handleTaxonomy } from "./taxonomy.js"; import { handleStaticResource } from "./static-resources.js"; import { handleResourceTemplate } from "./resource-templates.js"; import { rootsRegistry } from "../roots/index.js"; /** * Resource descriptor interface */ export interface ResourceDescriptor { uri: string; name: string; description: string; mimeType?: string; developerOnly?: boolean; // Flag to mark resources only available in developer mode } /** * List of available resources in the MCP server */ export const availableResources: ResourceDescriptor[] = [ { uri: "openfoodfacts://info", name: "Project Information", description: "Overview of the Open Food Facts project and its components", mimeType: "application/json" }, { uri: "openfoodfacts://schema", name: "Database Schema", description: "Information about the Open Food Facts data model" }, { uri: "openfoodfacts://api-docs", name: "API Documentation", description: "Documentation of the Open Food Facts API endpoints" }, { uri: "openfoodfacts://taxonomy/categories", name: "Categories Taxonomy", description: "Food categories taxonomy used in Open Food Facts" }, // Developer-specific resources - only available in developer mode { uri: "openfoodfacts://structure/", name: "Project Structure", description: "Navigate the directory structure of the Open Food Facts codebase", developerOnly: true }, { uri: "openfoodfacts://code-patterns", name: "Code Patterns", description: "Common patterns and conventions in the Open Food Facts codebase", developerOnly: true }, { uri: "openfoodfacts://file-organization", name: "File Organization", description: "How files are organized in the Open Food Facts project", developerOnly: true }, { uri: "openfoodfacts://template/", name: "Resource Templates", description: "Templates for common Open Food Facts development tasks", developerOnly: true } ]; /** * Filter resources based on developer mode * @param resources List of resources to filter * @returns Filtered resources based on developer mode */ export function filterResourcesForStandardMode(resources: ResourceDescriptor[]): ResourceDescriptor[] { return resources.filter(resource => !resource.developerOnly); } /** * Check if a resource URI is accessible based on current roots * and developer mode * * @param uri The resource URI to check * @param developerMode Whether developer mode is enabled * @returns True if the resource is accessible, false otherwise */ function isResourceAccessible(uri: string, developerMode = false): boolean { // If not in developer mode, check if the resource is developer-only if (!developerMode) { const resource = availableResources.find(r => r.uri === uri || uri.startsWith(r.uri)); if (resource && resource.developerOnly) { return false; } } // OpenFoodFacts specific URIs are always accessible if they pass the developer mode check if (uri.startsWith('openfoodfacts://')) { return true; } // For file:// URIs and other URIs, check if they're within roots return rootsRegistry.isWithinRoots(uri); } /** * Route a resource request to the appropriate handler * @param uri The resource URI * @param developerMode Whether developer mode is enabled * @returns Response for the requested resource */ export async function routeResourceRequest(uri: string, developerMode = false) { const url = new URL(uri); // Check if the resource is accessible based on defined roots and developer mode if (!isResourceAccessible(uri, developerMode)) { return { contents: [{ uri, text: `Access denied: The requested resource is either not within any defined root or requires developer mode`, isError: true }] }; } // Route to the appropriate handler based on URI pattern try { // Handle project structure resource if (url.protocol === "openfoodfacts:" && url.pathname.startsWith("//structure")) { return await handleProjectStructure(uri); } // Handle file content resource if (url.protocol === "openfoodfacts:" && url.pathname.startsWith("//file/")) { return await handleFileContent(uri, url); } // Handle project info resource if (uri === "openfoodfacts://info") { return await handleProjectInfo(uri); } // Handle taxonomy resource if (url.protocol === "openfoodfacts:" && url.pathname.startsWith("//taxonomy/")) { // Special case for categories taxonomy which is handled as a static resource if (uri === "openfoodfacts://taxonomy/categories") { return handleStaticResource(uri); } // All other taxonomies go through the dynamic handler return await handleTaxonomy(uri, url); } // Handle resource templates if (url.protocol === "openfoodfacts:" && url.pathname.startsWith("//template")) { return await handleResourceTemplate(uri); } // Handle static resources (schema, api-docs, code-patterns, file-organization) if ([ "openfoodfacts://schema", "openfoodfacts://api-docs", "openfoodfacts://code-patterns", "openfoodfacts://file-organization" ].includes(uri)) { return handleStaticResource(uri); } // Handle file URLs - check if they are within allowed roots if (url.protocol === "file:") { const root = rootsRegistry.getRootForURI(uri); if (root) { // Process file request if within a root // For now we'll just return a placeholder return { contents: [{ uri, text: `File content accessed via root: ${root.name || root.uri}`, mimeType: "text/plain" }] }; } else { throw new Error(`File is not within any defined root`); } } // Resource not found throw new Error(`Resource not found: ${uri}`); } catch (error) { // Return error response return { contents: [{ uri, text: `Error processing request: ${error instanceof Error ? error.message : String(error)}`, 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/JagjeevanAK/OpenFoodFacts-MCP'

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