Connects Claude to the Open Food Facts crowd-sourced database of 3 million packaged food products. Exposes four tools: barcode lookup for full nutrition labels and ingredient parsing, filtered search across categories and nutrition grades, side-by-side product comparisons, and taxonomy browsing to find valid filter tags. Built on the framework author's own mcp-ts-core with embedded rate limiting and retry logic to stay within OFF's API constraints. The comparison tool is the standout feature here, letting you line up nutritional data for up to 10 products at once without managing individual requests. Reach for this when you need structured food data for meal planning, dietary analysis, or ingredient research. No API key required, runs over stdio or streamable HTTP, and includes a public hosted endpoint if you don't want to run it locally.
Look up food products by barcode, search by ingredient or nutrition filter, compare products side-by-side, and browse the canonical tag vocabulary via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://openfoodfacts.caseyjhand.com/mcp
Four tools for working with Open Food Facts — a free, crowd-sourced database of 3M+ packaged food products:
| Tool | Description |
|---|---|
off_get_product | Fetch a packaged food product by barcode. Returns name, brand, quantity, ingredients, allergens, additives, Nutri-Score, NOVA group, Green-Score, nutrition per 100g/serving, categories, labels, and data completeness. |
off_search_products | Search by text query and/or structured tag filters (category, brand, label, Nutri-Score grade, NOVA group, country). Returns summary rows with barcodes for follow-up lookups. |
off_compare_products | Side-by-side nutrition and scoring comparison for 2–10 products by barcode. Returns a normalized table of energy, macros, salt, Nutri-Score, NOVA, and Green-Score. |
off_browse_taxonomy | Browse and search the canonical tag vocabulary (categories, labels, allergens, additives, countries, NOVA groups, Nutri-Score grades) for use as filter values in off_search_products. |
off_get_productFetch a packaged food product by barcode (EAN-13 or UPC).
fields parameter restricts the response to a subset (e.g., scores only, or nutrition only)found: false means no contributor has recorded this barcode yet — not a product defectoff_search_productsSearch Open Food Facts by text and/or structured tag filters.
categories_tag, brands_tag, labels_tag, nutrition_grade (a–e), nova_group (1–4), countries_tagoff_browse_taxonomy to resolve human terms (e.g., "organic" → en:organic)page (1-based) and page_size (1–50, default 20); response includes total count for computing total pagesoff_get_product for full label dataoff_compare_productsSide-by-side nutrition and scoring comparison for 2–10 barcodes.
null — comparisons are not imputed or estimatednot_found list identifies barcodes with no contributor record (partial results are not an error)off_browse_taxonomyBrowse the canonical Open Food Facts tag vocabulary before building off_search_products filters.
categories, labels, allergens, additives, countries, nova_groups, nutrition_gradessearch parameter: case-insensitive substring match against tag ID or display name (e.g., "gluten" → en:gluten, en:no-gluten, en:no-added-gluten)search term when browsing categorieslimit controls results returned (1–100, default 20)Built on @cyanheads/mcp-ts-core:
none, jwt, oauthin-memory, filesystem, Supabase, Cloudflare KV/R2/D1Open Food Facts-specific:
User-Agent header (required by OFF terms) is baked into the service layerenergy-kcal_100g) to underscore form — only the _100g and _serving variants are returnedoff_browse_taxonomy — curated 200+ category subset, full allergen/label/additive vocabulariesAgent-friendly output:
found field on every product response — explicit false when a barcode has no contributor record, not a thrown errornot_found list in off_compare_products allows partial batch comparisons without request failureA public instance is available at https://openfoodfacts.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"openfoodfacts-mcp-server": {
"type": "streamable-http",
"url": "https://openfoodfacts.caseyjhand.com/mcp"
}
}
}
No API key is required. Add the following to your MCP client configuration file.
{
"mcpServers": {
"openfoodfacts-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/openfoodfacts-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"openfoodfacts-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/openfoodfacts-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"openfoodfacts-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/openfoodfacts-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
User-Agent to comply with Open Food Facts' terms of service — this is baked in and requires no configuration.git clone https://github.com/cyanheads/openfoodfacts-mcp-server.git
cd openfoodfacts-mcp-server
bun install
cp .env.example .env
# edit .env if you need to override rate limits or the base URL
All configuration is validated at startup via Zod schemas in src/config/server-config.ts.
| Variable | Description | Default |
|---|---|---|
OFF_BASE_URL | Open Food Facts API base URL. Override for local testing against a mock server. | https://world.openfoodfacts.org |
OFF_RATE_LIMIT_PRODUCT | Product read rate limit (requests/min). | 100 |
OFF_RATE_LIMIT_SEARCH | Search rate limit (requests/min). | 10 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error). | info |
LOGS_DIR | Log file directory (Node.js only). | <project-root>/logs |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Attribution: Open Food Facts data is released under the Open Database License (ODbL) 1.0. Downstream use must cite Open Food Facts.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t openfoodfacts-mcp-server .
docker run --rm -p 3010:3010 openfoodfacts-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/openfoodfacts-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/services/openfoodfacts | Open Food Facts API client — HTTP, rate limiting, retry, field normalization. |
src/services/taxonomy | Embedded tag vocabulary service for off_browse_taxonomy. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/tools/definitions/index.tsIssues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
OFF_BASE_URLdefault: https://world.openfoodfacts.orgOpen Food Facts API base URL override. Useful for testing against a mock server.
OFF_RATE_LIMIT_PRODUCTdefault: 100Product read rate limit (requests/min).
OFF_RATE_LIMIT_SEARCHdefault: 10Search rate limit (requests/min).
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.
com.mcparmory/google-search
io.github.pipeworx-io/brave-search
marcopesani/mcp-server-serper
brave/brave-search-mcp-server
com.mcparmory/google-search-console
acamolese/google-search-console-mcp