openfoodfacts-mcp-server-22d18f — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited openfoodfacts-mcp-server-22d18f (Plugin) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center"> <h1>@cyanheads/openfoodfacts-mcp-server</h1> <p><b>Look up food products by barcode, search by keyword, tag, allergen, or nutrition filter, compare products side-by-side, and browse the canonical tag vocabulary via MCP. STDIO or Streamable HTTP.</b> <div>4 Tools</div> </p> </div>
<div align="center">
</div>
<div align="center">
</div>
<div align="center">
Public Hosted Server: https://openfoodfacts.caseyjhand.com/mcp
</div>
Packaged-food data from Open Food Facts, a crowd-sourced database of 3M+ products. Look up items by barcode, search by text, tags, allergens, and nutrient thresholds, compare products side by side, and resolve everyday terms to the canonical tags the filters take.
| Tool | Description |
|---|---|
off_get_product | Fetch one product by barcode: ingredients, allergens and traces, additives, scores, nutrition, and tags |
off_search_products | Search by text, tag filters, allergen exclusions, and nutrient thresholds (per 100 g, or per 100 ml for liquids); returns summary rows with barcodes |
off_compare_products | Compare up to 10 products per call by barcode on nutrition (per 100 g, or per 100 ml for liquids), Nutri-Score, NOVA, and Green-Score |
off_browse_taxonomy | Resolve a human term to the canonical tag ID that off_search_products filters on |
| Skill | Description |
|---|---|
openfoodfacts-guide | Judgment for calling the tools: allergy checks that never overclaim, choosing among a product's barcodes, where tag IDs come from, grade ranges, free-from searches, and the shared search budget. Served over MCP as skill://openfoodfacts-guide/SKILL.md and shipped in the plugin, npm package, and .mcpb bundle. |
off_get_product <sub>tool</sub>barcode is digits only, 4–40 digits after any leading zeros; optional fields trims the response and pulls in what a field depends on (nutriments brings the serving size), with requested_fields echoing what was fetchedpercent_estimate), allergens_tags, traces_tags, additives_tags, ingredients_analysis_tags (vegan, vegetarian, palm-oil verdicts), nutriscore_grade, nova_group, ecoscore_grade (Green-Score), nutriments (per serving, and per 100 g, or per 100 ml for liquids), category/label/packaging/origin/country tags, image_url, and completeness (0–1)traces_tags: ["en:none"] means the label states no traces, while an empty array means none entered yet; a barcode no contributor has recorded fails as not_foundoff_search_products <sub>tool</sub>query (up to 24 words, all of which must match the name, generic name, categories, labels, or brand; punctuation separates words, so milk-chocolate requires both) plus the tag filters categories_tag, brands_tag, labels_tag (up to 10), allergens_tag, traces_tag, ingredients_analysis_tag, additives_tag, nutrition_grade, nova_group, countries_tag, the exclude_allergens / exclude_traces lists (up to 14 each), and up to 18 nutrient_filters (per 100 g, or per 100 ml for liquids), all combined as AND; optional sort_bypage_size defaults to 20 and is served at up to 50 per call; a larger value (up to 10,000) is served at 50 and flagged in page_size_capped, and page counts pages of 50. Rows carry barcode, product_name, brands, nutriscore_grade, nova_group, ecoscore_grade, and categories_tags, plus total, total_is_lower_bound, last_page, omitted, and truncated (more matches beyond this page). Exclusions add exclusion_coverage, since products with no allergen data entered pass them; nutrient_filters add indexed_nutriments (the index values the filters checked, which can differ from off_get_product)query or nutrient_filters searches a text index that lags the live database (flagged in text_index_snapshot), reaches only the first 10,000 results, and rejects additives_tag as additives_filter_needs_tag_search; tag-only searches read the live database through page 10. A page past either limit fails as page_out_of_rangeoff_compare_products <sub>tool</sub>barcodes, one row each in input order; repeated barcodes are compared once, and those past the 10th distinct one are listed in unprocessed without being fetchedfound, nutriscore_grade, nova_group, ecoscore_grade, energy, fat, saturated fat, sugars, salt, protein, and fiber (per 100 g, or per 100 ml for liquids), and completeness; missing values stay absent, never imputednot_found lists barcodes with no contributor record and failed lists fetches that failed, each with a reason; a failed barcode gets no row and never blocks the restoff_browse_taxonomy <sub>tool</sub>facet is one of categories, labels, allergens, additives, countries, nova_groups, nutrition_grades; search matches a substring of the tag ID, name, or a synonym ("shellfish" → en:crustaceans); limit (default 20) returns up to 100 per call (a value up to 200 still returns 100); a cut result says how many matched; no pagingtags[] of id and name, where id goes to off_search_products unchanged; nova_groups and nutrition_grades come back complete with total_in_facetnotice, when it is unavailable; omitting search returns only that sampleBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Open Food Facts-specific:
User-Agent Open Food Facts' terms require is built inenergy-kcal_100g → energy_kcal_100g), with additional_100g / additional_serving maps that label each nutrient with its uniten:organic, en:no-gluten); on text searches a case variant, synonym, or brand name is canonicalized first (US → en:united-states)Agent-friendly output:
serving_size, serving_quantity, serving_quantity_unit, and a note when none is recordedoff_compare_products returns resolved rows alongside not_found and failedtotal_is_lower_bound, last_page, and omitted on searchesreason (upstream_error, upstream_timeout, upstream_rejected, rate_limited, plus per-tool input reasons) and a recovery hint; an incomplete upstream response is a retryable upstream_error, never an empty result or a not-foundConnect to the hosted instance, no install:
claude mcp add --transport http openfoodfacts-mcp-server https://openfoodfacts.caseyjhand.com/mcpOr run it locally:
claude mcp add openfoodfacts-mcp-server -- npx -y @cyanheads/openfoodfacts-mcp-serverFor other clients, add this to the MCP config file:
{
"mcpServers": {
"openfoodfacts-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/openfoodfacts-mcp-server"]
}
}
}No API key or account is needed: the server identifies itself to Open Food Facts with its own User-Agent. For Docker or a source build, see Running the server.
| Variable | Description | Default |
|---|---|---|
OFF_BASE_URL | Open Food Facts API base URL. | https://world.openfoodfacts.org |
OFF_RATE_LIMIT_PRODUCT | Product read budget (requests/min). The default is Open Food Facts' published per-IP limit; lower it on a shared outbound IP. | 15 |
OFF_RATE_LIMIT_SEARCH | Search budget (requests/min). The default is Open Food Facts' published per-IP limit. | 10 |
OFF_RATE_LIMIT_TAXONOMY | Taxonomy lookup budget (requests/min), shared by off_browse_taxonomy, tag canonicalization on text searches, and exclusion checks. | 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 |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Requires Bun v1.4.0 or higher (or Node.js v24+).
git clone https://github.com/cyanheads/openfoodfacts-mcp-server.git
cd openfoodfacts-mcp-server && bun install
cp .env.example .env # optional: override rate limits or the base URL # One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against specdocker run --rm -p 3010:3010 ghcr.io/cyanheads/openfoodfacts-mcp-server:latestThe image defaults to HTTP transport, stateless session mode, and logs to /var/log/openfoodfacts-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/openfoodfacts-mcp-server:latest"]. To build locally, docker build -t 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 the published skills, 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 | Tag vocabulary — live resolution, offline sample, and tag-value canonicalization. |
src/utils | Markdown escaping for contributor-entered values in text output. |
skills/ | Published Agent Skills for the agents that call the tools. |
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 are welcome — see CONTRIBUTING.md for the forms and what makes a report actionable. Report vulnerabilities through private disclosure, never a public issue. Run checks and tests before submitting:
bun run devcheck
bun run testApache-2.0 — see LICENSE for details. Open Food Facts data is released under the Open Database License (ODbL) 1.0; downstream use must cite Open Food Facts.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.