npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cyanheads/openfoodfacts-mcp-server

v0.1.8

Published

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.

Downloads

1,297

Readme

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://openfoodfacts.caseyjhand.com/mcp


Tools

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_product

Fetch a packaged food product by barcode (EAN-13 or UPC).

  • Accepts 8–14 digit barcodes (EAN-13, EAN-8, UPC-A, UPC-E)
  • Returns ingredients (raw text and parsed list with percent estimates, vegan/vegetarian flags), all 14 major allergens as tag IDs, E-number additives, Nutri-Score a–e, NOVA 1–4, Green-Score/Eco-Score, full nutriments per 100g and per serving, categories/labels/packaging/origins as canonical tag IDs, front image URL, and data completeness score (0–1)
  • Optional fields parameter restricts the response to a subset (e.g., scores only, or nutrition only)
  • Open Food Facts is crowd-sourced — a missing field means "not yet entered by contributors," not that the attribute is absent from the actual product
  • found: false means no contributor has recorded this barcode yet — not a product defect

off_search_products

Search Open Food Facts by text and/or structured tag filters.

  • Full-text search across product names, brands, and ingredients
  • Structured filters: categories_tag, brands_tag, labels_tag, nutrition_grade (a–e), nova_group (1–4), countries_tag
  • Text query and tag filters combine — a query with filters returns products that match the text and satisfy every filter (e.g., query: "dark chocolate" + labels_tag: en:organic + countries_tag: en:france)
  • All filter values are canonical tag IDs — use off_browse_taxonomy to resolve human terms (e.g., "organic" → en:organic)
  • Pagination via page (1-based) and page_size (1–50, default 20); response includes total count for computing total pages
  • Returns summary rows (barcode, name, brand, Nutri-Score, NOVA, categories) — use off_get_product for full label data
  • Result counts reflect contributed products, not total products on the market
  • Search rate-limited to ~10 requests/min by the Open Food Facts API

off_compare_products

Side-by-side nutrition and scoring comparison for 2–10 barcodes.

  • Accepts 2–10 barcodes, compared in the order provided
  • Returns a normalized comparison table: energy (kcal/100g), fat, saturated fat, sugars, salt, protein, fiber, Nutri-Score, NOVA group, and Green-Score
  • Missing nutrition data is preserved as null — comparisons are not imputed or estimated
  • not_found list identifies barcodes with no contributor record (partial results are not an error)

off_browse_taxonomy

Browse the canonical Open Food Facts tag vocabulary before building off_search_products filters.

  • Facets: categories, labels, allergens, additives, countries, nova_groups, nutrition_grades
  • Optional search parameter: case-insensitive substring match against tag ID or display name (e.g., "gluten"en:gluten, en:no-gluten, en:no-added-gluten)
  • Taxonomy is served from a curated in-process vocabulary — no live network call, so lookups are instant and offline
  • The categories facet is broad — pass a search term to narrow it to the relevant tags
  • limit controls results returned (1–100, default 20)

Features

Built on @cyanheads/mcp-ts-core:

  • Declarative tool definitions — single file per tool, framework handles registration and validation
  • Unified error handling — handlers throw, framework catches, classifies, and formats
  • Pluggable auth: none, jwt, oauth
  • Swappable storage backends: in-memory, filesystem, Supabase, Cloudflare KV/R2/D1
  • Structured logging with optional OpenTelemetry tracing
  • STDIO and Streamable HTTP transports

Open Food Facts-specific:

  • No API key required — the identifying User-Agent header (required by OFF terms) is baked into the service layer
  • Token-bucket rate limiting per endpoint class: product reads (~100/min), search (~10/min)
  • Automatic retry (3 attempts, 500ms base) with HTML error page detection for 503 during high load
  • Nutriments normalized from raw hyphenated keys (energy-kcal_100g) to underscore form — only the _100g and _serving variants are returned
  • Embedded tag taxonomy for off_browse_taxonomy — curated 200+ category subset, full allergen/label/additive vocabularies

Agent-friendly output:

  • found field on every product response — explicit false when a barcode has no contributor record, not a thrown error
  • Missing fields signal incomplete crowd-sourced data, not product attribute absence — surfaced in descriptions and format output
  • Computed scores (Nutri-Score, NOVA, Green-Score) returned as-is with regional caveat notes — not interpreted or normalized to health claims
  • not_found list in off_compare_products allows partial batch comparisons without request failure

Getting started

Public Hosted Instance

A 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"
    }
  }
}

Self-Hosted / Local

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

Prerequisites

  • Bun v1.3.0 or higher (or Node.js v24+).
  • No API key needed. The server sends an identifying User-Agent to comply with Open Food Facts' terms of service — this is baked in and requires no configuration.

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/openfoodfacts-mcp-server.git
  1. Navigate into the directory:
cd openfoodfacts-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env if you need to override rate limits or the base URL

Configuration

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.

Running the server

Local development

  • 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

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.

Project structure

| 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/. |

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools via the barrel in src/mcp-server/tools/definitions/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues and pull requests are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.