Nutriscan MCP connector
by si-essence
README.md
# nutriscan-mcp (Nutriscan MCP connector)
MCP server for packaged foods from [Open Food Facts](https://world.openfoodfacts.org) and generic foods from [USDA FoodData Central](https://fdc.nal.usda.gov), plus a local meal log. TypeScript, official `@modelcontextprotocol/sdk`, stdio + Streamable HTTP.
## Setup
```bash
npm install && npm run build
export NUTRISCAN_CONTACT=you@example.com # optional; sent in the Open Food Facts User-Agent
export USDA_API_KEY=your_data_gov_key # optional; omit to use the shared DEMO_KEY
npm start # stdio
npm run start:http # Streamable HTTP at http://localhost:3000/mcp (PORT to change), /health
npm test # mocked tests, no network and no key
npm run live-check # one real barcode, one real search, one USDA DEMO_KEY search
```
Claude Desktop / any stdio client:
```json
{
"mcpServers": {
"nutriscan": {
"command": "node",
"args": ["/workspace/connectors/nutriscan/dist/index.js"],
"env": { "USDA_API_KEY": "...", "NUTRISCAN_CONTACT": "you@example.com" }
}
}
}
```
Docker: `docker build -t nutriscan . && docker run -p 3000:3000 -e USDA_API_KEY=... -v nutriscan-data:/data nutriscan`
HTTP mode has no authentication. Keep it on localhost.
## Tools
| Tool | What it returns |
| --- | --- |
| `barcode_lookup` | One Open Food Facts product by EAN-8, UPC-A, or EAN-13: name, brand, Nutri-Score, NOVA, allergens, additives, nutrients per 100 g |
| `product_search` | Full-text name search (barcode, name, brand, Nutri-Score, NOVA). Optional `brand` and `country` |
| `allergens_additives` | Allergens, traces, E-numbers, and a short ingredients line for one barcode |
| `nutriscore_nova` | Nutri-Score letter (A best, E worst) and NOVA group (1–4), each with a one-line meaning |
| `nutrients_per_serving` | Nutrients scaled to one label serving, `servings`, or `grams` |
| `usda_food_search` | USDA FoodData Central search: `fdcId`, description, data type, nutrients per 100 g, sodium in mg |
| `compare_products` | Two barcodes side by side per 100 g. `barcode_a` / `barcode_b` come back as `a` / `b` |
| `meal_log_add` | Append one food to the local JSON log. A barcode snapshots nutrients for the amount eaten |
| `meal_log_list` | Newest local entries. Optional UTC `date` and `limit` |
| `meal_log_day_total` | Sum of one UTC day. Entries without nutrients are counted and not added |
A 12-digit UPC that is missing is retried once as an EAN-13 with a leading zero. Nutri-Score or NOVA being absent is not an error. These tools are not medical or dietary advice. A lower nutrient number is not always the better choice (protein and fiber often are desirable).
## Environment
| Variable | Default | Role |
| --- | --- | --- |
| `NUTRISCAN_CONTACT` | `contact@example.com` | Contact in the User-Agent `Nutriscan/0.1 (<contact>)` |
| `USDA_API_KEY` | `DEMO_KEY` | FoodData Central key. Never printed or returned |
| `NUTRISCAN_DATA_DIR` | `./data` | Directory for `meals.json` (relative to the process working directory) |
| `NUTRISCAN_OFF_SEARCH_PER_MINUTE` | `10` | Local cap on Open Food Facts searches |
| `NUTRISCAN_OFF_PRODUCT_PER_MINUTE` | `100` | Local cap on Open Food Facts product reads |
| `USDA_HOURLY_LIMIT` | `30` with `DEMO_KEY`, else `1000` | Local USDA hourly cap |
| `USDA_DAILY_LIMIT` | `50` with `DEMO_KEY`, else `100000` | Local USDA daily cap (resets 00:00 UTC) |
| `NUTRISCAN_OFF_BASE_URL` | `https://world.openfoodfacts.org` | Product and search host |
| `NUTRISCAN_USDA_BASE_URL` | `https://api.nal.usda.gov/fdc/v1` | FoodData Central host |
| `MCP_TRANSPORT` | stdio, or `http` | Same as `--http` |
| `PORT` | `3000` | HTTP listen port |
`DEMO_KEY` is a shared example key: about 30 requests per hour and 50 per day per IP. Sign up for a free key at https://fdc.nal.usda.gov/api-key-signup (about 1,000 requests per hour). The key is sent only as the `api_key` query parameter and is stripped from errors.
## Caching and rate limits
- In-memory TTL cache: product reads 1 hour, Open Food Facts search 10 minutes, USDA search 24 hours. A cache hit does not spend quota. The cache resets when the process restarts.
- Open Food Facts product reads (barcode, allergens, Nutri-Score, nutrients, compare, and a meal log that passes a barcode) share one budget, default 100 per minute. Searches have a separate budget, default 10 per minute.
- Open Food Facts documents its own platform caps (about 10 searches per minute, and a lower per-IP cap on product reads, recently described as about 15 per minute). Exceeding the platform cap can get an IP blocked. For unattended use, set `NUTRISCAN_OFF_PRODUCT_PER_MINUTE=15`.
- Product reads use `GET /api/v2/product/{barcode}` with a `fields` list so the response stays small. Name search uses the full-text search endpoint `GET /cgi/search.pl` (`search_terms`, `json=1`) because API v2 search does not accept free-text `search_terms`. If that endpoint returns HTTP 502 or 503, the same search is sent once to Search-a-licious at `https://search.openfoodfacts.org/search`.
- USDA calls are capped locally to the demo limits when the key is `DEMO_KEY`, and to about 1,000 per hour otherwise. When a limit is hit, the tool returns `isError` with a `[rate-limit]` message and a retry hint. Cached data is still served.
- Every Open Food Facts request sends `User-Agent: Nutriscan/0.1 (<contact>)`.
## Meal log
`meal_log_add`, `meal_log_list`, and `meal_log_day_total` read and write `{ "entries": [ ... ] }` at `$NUTRISCAN_DATA_DIR/meals.json`. Dates are UTC. Nothing is uploaded. A barcode stores a snapshot of the nutrients for the grams eaten, so later edits on Open Food Facts do not change history. Entries without a barcode are listed but left out of the nutrient sum (`withoutNutrients`). The file is capped at 5,000 entries. Invalid JSON is reported and not overwritten.
## Attribution and licenses
Data from Open Food Facts (openfoodfacts.org), available under the ODbL.
The Open Food Facts database is under the Open Database License (ODbL). Database contents are under the Database Contents License. The ODbL is share-alike: if you publish a database derived from Open Food Facts data, that database must be released under the ODbL as well. Looking up products through this server does not by itself publish a database. Product images on Open Food Facts are under CC-BY-SA and are not returned here. Do not use the Open Food Facts name or logo to imply that Open Food Facts endorses this project. Product names and pack shots belong to their owners.
USDA FoodData Central data are in the public domain under CC0 1.0. Please cite: U.S. Department of Agriculture, Agricultural Research Service. FoodData Central, 2019. fdc.nal.usda.gov.
## License
MIT, see [LICENSE](LICENSE). This is an independent, unofficial connector and is not endorsed by the Nutriscan project or its owners.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues