etiquette-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@etiquette-mcpget the nutrition label for Nutella, barcode 3017620422003"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
etiquette-mcp
An MCP server that looks a food product up by barcode, name or keywords across several French grocery sources, self-checks each nutrition label it finds, and returns the best-supported label together with how strongly the sources agree, per-store prices and the raw labels.
Tools
Tool | Purpose |
| Search by barcode, name or keywords (optional brand and size). Up to 5 products grouped across stores, with a match score, a macro preview and store offers. |
| Best nutrition label for one product, from a barcode, a |
|
|
| The exact same product across stores, cheapest total price first, with the price spread. |
| Average values for a kind of food without a pack (fruit, vegetables, meat, staples) from Ciqual, the French food composition table. |
| Sources, their capabilities and recent health, and the server version. |
Every product lookup tool accepts sources to restrict the stores used (default: all).
A get_label answer contains:
status:found,ambiguous(seecandidates, each with a score and a macro preview),not_found, orincompletewhen sources did not answer in time (retry afterretry_after_s)no_good_match/requested_brand_found: the best match is weak, or the brand given was not foundlabel.nutrients: per nutrient, the chosen value with its agreement (strong,weak,majority,single,brand_ownerorconflict), how many sources agree, dissenting values and</ traces qualifiersready_to_apply: flat values per 100 g/ml (salt and sodium), per pack when the pack size is known, and per portion when a label states one; nutrients the sources disagree on stay null and are listed inconflictswith each valueconfidence: overall level (high/medium/low/none), independent source count, missing nutrients, self-check results, flags andflag_detailsnaming the source and nutrient behind each flagadvice: a short instruction for the calling modelvariants: same brand, other sizes or flavours;product.other_sizes: other pack sizes with the same label (a description matching only those is answered directly)ingredients(with language),allergens,traces,nutriscore, andofferswith prices per storesources: the status of every source for this request (ok,not_found,timeout,rate_limited,error, orskippedwhen it was not needed or the answer came from the cache)per_source: each source's structured label, raw label text, unmapped extra nutrients (vitamins, minerals, …) and when the source last updated it, if known
With compact: true (the default for get_labels), offers are reduced to the list of stores and the per-source
labels, ingredients, allergens, traces, variants and similar products are left out.
All values are per 100 g or per 100 ml (label.basis).
Related MCP server: Open Food Facts MCP Server
How a label is decided
Identity: offers from different sources are the same product when they share a barcode, or, without one, the same brand, pack size and name (a swapped word such as another flavour is a different product).
Normalisation: sodium is converted to salt, swapped kJ/kcal values are repaired, zeros from sources that use 0 for "unknown" and values a source marks as its own estimate are ignored, and impossible values are dropped.
Self-check: energy is compared with fat, carbohydrates, protein and fibre, and kJ with kcal. Inconsistent labels count less; an identifiable decimal slip, or a 0 that explains missing energy, removes the faulty value.
Voting: per nutrient, values agreeing within label rounding (or within EU declared-value tolerances) are clustered and weighted by source quality. The chosen value is always a published value, never an average. Copies of another source's label count once.
Brand owner: for a store brand, the label of the shop that makes or brands the product is the reference. When it passes the self-check, its values are kept whatever other sources say (
brand_owner), and it alone gives high confidence. Its shop also shows which of the brand's products are current: when it sells products matching a search, the brand's products it doesn't sell (often discontinued ones kept by open databases) are left out.
Sources
Open Food Facts, La Fourche, Kazidomi, Biocoop, La Vie Claire, Naturalia, La Belle Vie, Chronodrive and Picard. Requests are anonymous: no cookies, no accounts, and a user agent that only names the project (with its repository URL for Open Food Facts, which asks API clients to identify themselves). Each source is paced to its documented limits or a polite rate, and results are cached in SQLite.
With OFF_LOCAL_DB=true, barcodes and product names are looked up in a local copy of the whole Open Food Facts
database kept in DATA_DIR (about 5 GB, twice that while it is rebuilt), with the API as fallback for products
added since the last update. The server builds the copy itself on first start (about an hour, in the background,
answering from the API meanwhile), then applies the daily updates and rebuilds it monthly while the current copy keeps
answering; list_sources shows its state.
Generic foods come from the Ciqual table published by ANSES (Licence Ouverte / Etalab 2.0), included with the server: about 3,500 foods named in French and English, values per 100 g.
Running
With the published Docker image (amd64 and arm64), using an .env file based on .env.example:
docker run -d --name etiquette-mcp -p 3000:3000 --env-file .env -v etiquette-data:/data \
ghcr.io/houssamotarid/etiquette-mcp:latestFrom source (Node.js 24+):
npm ci
cp .env.example .env # set MCP_AUTH_TOKEN (openssl rand -hex 32)
npm run dev # or: npm run build && npm startOr build the image yourself: docker compose up -d --build.
The MCP endpoint is POST /mcp (streamable HTTP, stateless) with Authorization: Bearer <MCP_AUTH_TOKEN>.
GET /health needs no authentication.
Configuration
Variable | Default | |
| (required) | Bearer token, at least 32 characters |
|
| |
|
| SQLite cache and the local Open Food Facts copy |
|
| Time limit per source call |
|
| Time limit per tool call; late sources keep filling the cache |
|
| How long a tool call may wait for queued Open Food Facts lookups |
|
| Keep a local copy of Open Food Facts (see Sources) |
|
|
|
| Comma-separated source ids | |
|
| Open Food Facts requests also carry the project URL |
Development
npm run check # typecheck + lint + tests
npm run smoke # live lookups against the real sources (-- --check: fail on missing products or source errors)
npm run eval # score a running server on real lookups (EVAL_URL, MCP_AUTH_TOKEN)
npm run ciqual # rebuild src/data/ciqual.json from the latest Ciqual tablesrc/
domain/ nutrient model, barcode, quantity, number and text helpers
engine/ pure decision logic: normalisation, self-check, voting, identity, confidence
sources/ one adapter per source + free-text label parser
infra/ HTTP client with per-host limits, SQLite cache, logging, health
app/ the use cases behind the tools, presentation of results
mcp/ tool definitions and HTTP transport
data/ the Ciqual table, generated by npm run ciqual
test/ unit tests and recorded source fixturesLicense
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Look up a packaged food by barcode, search and compare products, and check listed allergens.
Barcode lookup, nutrition search, and product comparison for 3M+ crowd-sourced food products.
USDA FoodData Central nutrient lookup, FDA recall watch, EU FMCG labelling via remote MCP
Signed US food & grocery data: Kroger prices, nutrition, allergens, recalls, diet filters.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to access the Open Food Facts database to query detailed food product information, nutritional data, and environmental scores. Supports product lookup by barcode, smart search with filtering, nutritional analysis, product comparison, and dietary recommendations to help users make informed food choices.51MIT
- FlicenseNot gradedqualityDmaintenanceEnables LLMs to search for food products and retrieve detailed nutritional information from the Open Food Facts database using product names or barcodes.-
- AlicenseNot gradedqualityBmaintenanceEnables querying Open Food Facts product database by barcode, full-text search, category, brand, or country.11 npmMIT
- AlicenseNot gradedqualityAmaintenanceLook up food products by barcode, search by ingredient or nutrition filter, compare products side-by-side, and browse the canonical tag vocabulary via MCP.189 npm1Apache 2.0