@cyanheads/openfoodfacts-mcp-server 0.3.7 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/AGENTS.md +3 -3
  2. package/CLAUDE.md +3 -3
  3. package/Dockerfile +3 -3
  4. package/README.md +13 -12
  5. package/changelog/0.3.x/0.3.8.md +35 -0
  6. package/dist/mcp-server/tools/definitions/browse-taxonomy.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/definitions/browse-taxonomy.tool.js +38 -10
  8. package/dist/mcp-server/tools/definitions/browse-taxonomy.tool.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/compare-products.tool.d.ts +2 -1
  10. package/dist/mcp-server/tools/definitions/compare-products.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/definitions/compare-products.tool.js +63 -27
  12. package/dist/mcp-server/tools/definitions/compare-products.tool.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/get-product.tool.d.ts +1 -1
  14. package/dist/mcp-server/tools/definitions/get-product.tool.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/get-product.tool.js +98 -45
  16. package/dist/mcp-server/tools/definitions/get-product.tool.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/index.d.ts +19 -5
  18. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  19. package/dist/mcp-server/tools/definitions/search-products.tool.d.ts +16 -3
  20. package/dist/mcp-server/tools/definitions/search-products.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/search-products.tool.js +137 -51
  22. package/dist/mcp-server/tools/definitions/search-products.tool.js.map +1 -1
  23. package/dist/services/openfoodfacts/openfoodfacts-service.d.ts +20 -2
  24. package/dist/services/openfoodfacts/openfoodfacts-service.d.ts.map +1 -1
  25. package/dist/services/openfoodfacts/openfoodfacts-service.js +150 -42
  26. package/dist/services/openfoodfacts/openfoodfacts-service.js.map +1 -1
  27. package/dist/services/openfoodfacts/types.d.ts +49 -14
  28. package/dist/services/openfoodfacts/types.d.ts.map +1 -1
  29. package/dist/services/openfoodfacts/types.js +8 -4
  30. package/dist/services/openfoodfacts/types.js.map +1 -1
  31. package/dist/services/taxonomy/taxonomy-service.d.ts +7 -5
  32. package/dist/services/taxonomy/taxonomy-service.d.ts.map +1 -1
  33. package/dist/services/taxonomy/taxonomy-service.js +27 -9
  34. package/dist/services/taxonomy/taxonomy-service.js.map +1 -1
  35. package/package.json +4 -4
  36. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** openfoodfacts-mcp-server
4
- **Version:** 0.3.7
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
4
+ **Version:** 0.3.8
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
8
8
  **Zod:** ^4.6.5
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** openfoodfacts-mcp-server
4
- **Version:** 0.3.7
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
4
+ **Version:** 0.3.8
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1
8
8
  **Zod:** ^4.6.5
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/Dockerfile CHANGED
@@ -14,7 +14,7 @@
14
14
  # output. A stage that compiles a native addon needs the target-arch toolchain
15
15
  # and cannot cross-compile this way — drop the flag there.
16
16
  # ==============================================================================
17
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS build
17
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS build
18
18
 
19
19
  WORKDIR /usr/src/app
20
20
 
@@ -46,7 +46,7 @@ RUN bun run build
46
46
  # A clean image rather than `FROM build`: the build stage's node_modules holds
47
47
  # devDependencies.
48
48
  # ==============================================================================
49
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS deps
49
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS deps
50
50
 
51
51
  WORKDIR /usr/src/app
52
52
 
@@ -114,7 +114,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
114
114
  # dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
115
115
  # and CMD, which run on the real target at container start.
116
116
  # ==============================================================================
117
- FROM oven/bun:1.4.2-slim AS production
117
+ FROM oven/bun:1.4.3-slim AS production
118
118
 
119
119
  WORKDIR /usr/src/app
120
120
 
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.3.7-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/openfoodfacts-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.2.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/openfoodfacts-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/openfoodfacts-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.2-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.3.8-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/openfoodfacts-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.3.1-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/openfoodfacts-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/openfoodfacts-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.3-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -36,8 +36,8 @@ Packaged-food data from Open Food Facts, a crowd-sourced database of 3M+ product
36
36
  | Tool | Description |
37
37
  |:-----|:------------|
38
38
  | `off_get_product` | Fetch one product by barcode: ingredients, allergens and traces, additives, scores, nutrition, and tags |
39
- | `off_search_products` | Search by text, tag filters, allergen exclusions, and per-100 g nutrient thresholds; returns summary rows with barcodes |
40
- | `off_compare_products` | Compare 2–10 products by barcode on per-100 g nutrition, Nutri-Score, NOVA, and Green-Score |
39
+ | `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 |
40
+ | `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 |
41
41
  | `off_browse_taxonomy` | Resolve a human term to the canonical tag ID that `off_search_products` filters on |
42
42
 
43
43
  ## Capability reference
@@ -45,32 +45,33 @@ Packaged-food data from Open Food Facts, a crowd-sourced database of 3M+ product
45
45
  ### `off_get_product` <sub>tool</sub>
46
46
 
47
47
  - `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 fetched
48
- - Returns ingredients (raw text plus a parsed tree up to three levels deep with `percent_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 100 g and per serving, category/label/packaging/origin/country tags, `image_url`, and `completeness` (0–1)
48
+ - Returns ingredients (raw text plus a parsed tree up to three levels deep with `percent_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)
49
49
  - `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_found`
50
50
 
51
51
  ---
52
52
 
53
53
  ### `off_search_products` <sub>tool</sub>
54
54
 
55
- - `query` (up to 24 words; every word except common stop words must match the name, generic name, categories, labels, or brand) plus tag filters `categories_tag`, `brands_tag`, `labels_tag` (up to 10, all must apply), `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 per-100 g `nutrient_filters`, all combined as AND; `page_size` 1–50 (default 20), optional `sort_by`
56
- - Summary rows carry `barcode`, `product_name`, `brands`, `nutriscore_grade`, `nova_group`, `ecoscore_grade`, and `categories_tags`, alongside `total`, `total_is_lower_bound`, `last_page`, and `omitted`; responses using an exclusion carry `exclusion_coverage`, since products with no allergen data entered pass it
55
+ - `query` (up to 24 words; every word except common stop words must match the name, generic name, categories, labels, or brand, and a hyphen or other punctuation between words separates them, so `milk-chocolate` requires both) plus tag filters `categories_tag`, `brands_tag`, `labels_tag` (up to 10, all must apply), `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; `page_size` (default 20) up to 50 per call, with a larger value up to 10,000 served at 50 and flagged in `page_size_capped` while `page` counts pages of 50; optional `sort_by`
56
+ - Summary rows carry `barcode`, `product_name`, `brands`, `nutriscore_grade`, `nova_group`, `ecoscore_grade`, and `categories_tags`, alongside `total`, `total_is_lower_bound`, `page_size`, `last_page`, and `omitted`; `truncated` marks a page with more matches beyond it, never the final page; responses using an exclusion carry `exclusion_coverage`, since products with no allergen data entered pass it
57
+ - With `nutrient_filters`, each row also carries `indexed_nutriments`: the text index's values for the constrained nutrients (per 100 g, or per 100 ml for liquids), keyed like `off_get_product` (`sugars_100g`, `energy_kcal_100g`). They are the values the filters were checked against and can differ from the current record `off_get_product` returns
57
58
  - `query` or `nutrient_filters` routes the search to a text index that lags the live database (flagged in `text_index_snapshot`), serves 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, and a page past either bound fails as `page_out_of_range`
58
59
 
59
60
  ---
60
61
 
61
62
  ### `off_compare_products` <sub>tool</sub>
62
63
 
63
- - 2–10 `barcodes`, returned one row each in input order
64
- - Rows carry `found`, `nutriscore_grade`, `nova_group`, `ecoscore_grade`, per-100 g energy, fat, saturated fat, sugars, salt, protein, and fiber, and `completeness`; missing values stay absent, never imputed
64
+ - 2–100 `barcodes`, returned one row each in input order; a repeated barcode is compared once (two identical barcodes yield one row), and those past the 10th distinct one are listed in `unprocessed` without being fetched
65
+ - Rows carry `found`, `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 imputed
65
66
  - `not_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 rest
66
67
 
67
68
  ---
68
69
 
69
70
  ### `off_browse_taxonomy` <sub>tool</sub>
70
71
 
71
- - `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` 1–100 (default 20), with no paging
72
+ - `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) up to 100 per call, with a larger value up to 200 returning the first 100; a cut names how many matched and whether a larger `limit` or a narrower term reaches the rest; no paging
72
73
  - Returns `tags[]` of `id` and `name`, where `id` goes to `off_search_products` unchanged; `nova_groups` and `nutrition_grades` come back complete with `total_in_facet`
73
- - The five open facets resolve against the live Open Food Facts taxonomy and fall back to a small offline sample, with a `notice`, when it is unreachable or the budget is spent; omitting `search` returns only that sample
74
+ - The five open facets resolve against the live Open Food Facts taxonomy and fall back to a small offline sample, with a `notice`, when it is unreachable, gives no usable answer, or the budget is spent; omitting `search` returns only that sample
74
75
 
75
76
  ## Features
76
77
 
@@ -80,7 +81,7 @@ Open Food Facts-specific:
80
81
 
81
82
  - No API key; the identifying `User-Agent` Open Food Facts' terms require is built in
82
83
  - Per-endpoint request budgets (product 15/min, search 10/min, taxonomy 10/min), counted per upstream attempt; transient failures (5xx other than 501, timeouts, 429 with `Retry-After`) retry up to 4 attempts, while a 4xx or 501 is sent once
83
- - Nutriments normalized from hyphenated keys (`energy-kcal_100g` → `energy_kcal_100g`), with named macros plus `additional_100g` / `additional_serving` maps that carry each nutrient's own unit
84
+ - Nutriments normalized from hyphenated keys (`energy-kcal_100g` → `energy_kcal_100g`), with named macros plus `additional_100g` / `additional_serving` maps that label each nutrient with the unit Open Food Facts normalizes it to (g, kJ, kcal, % vol, %, mmol/l, or none for pH and glycemic index)
84
85
  - Tag filters take canonical IDs (`en:organic`, `en:no-gluten`); on text searches a case variant, synonym, or brand name is canonicalized before it is sent (`US` → `en:united-states`, `Nutella` → `nutella`)
85
86
  - Every field is contributor-entered, so a missing one means not yet recorded rather than absent from the product; Nutri-Score, NOVA, and Green-Score are Open Food Facts' own computed grades, returned as-is, and carry regional formula caveats
86
87
 
@@ -89,7 +90,7 @@ Agent-friendly output:
89
90
  - Per-serving nutrition carries its denominator: `serving_size`, `serving_quantity`, `serving_quantity_unit`, and a note when none is recorded
90
91
  - Graceful partial failure: `off_compare_products` returns resolved rows alongside `not_found` and `failed`
91
92
  - Explicit count bounds: `total_is_lower_bound`, `last_page`, and `omitted` on searches, and an exhausted page is reported as such rather than as zero matches
92
- - Typed failures: each carries a declared `reason` (`upstream_error`, `upstream_timeout`, `upstream_rejected`, `rate_limited`, plus per-tool input reasons) and a recovery hint on both client surfaces
93
+ - Typed failures: each carries a declared `reason` (`upstream_error`, `upstream_timeout`, `upstream_rejected`, `rate_limited`, plus per-tool input reasons) and a recovery hint on both client surfaces; a success response missing its result fields is a retryable `upstream_error` (a `failed` entry on `off_compare_products`; the noticed offline fallback on `off_browse_taxonomy`), never a confident empty result or a not-found
93
94
 
94
95
  ## Getting started
95
96
 
@@ -0,0 +1,35 @@
1
+ ---
2
+ summary: "Open Food Facts read fixes: text search treats punctuation inside a word as part of it, malformed upstream bodies fail as retryable upstream_error, liquids are labeled per 100 ml, and over-cap page_size, barcodes, and limit are served bounded instead of rejected."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.8 — 2026-10-13
8
+
9
+ ## Added
10
+
11
+ - **`indexed_nutriments` on `off_search_products` rows** (#58) — a search with `nutrient_filters` returns the text index's values for the constrained nutrients, keyed like `off_get_product` (`sugars_100g`). They can differ from the current record.
12
+ - **`page_size` and `page_size_capped` on `off_search_products`** (#57) — report the page size served and whether the request was cut to it.
13
+ - **`unprocessed` on `off_compare_products`** (#57) — barcodes past the 10th distinct one, listed without being fetched.
14
+
15
+ ## Changed
16
+
17
+ - **Over-cap reads are served bounded instead of rejected** (#57) — `off_search_products` accepts `page_size` up to 10000 and serves 50 (`page` counts pages of 50); `off_compare_products` accepts up to 100 `barcodes` and fetches 10; `off_browse_taxonomy` accepts `limit` up to 200 and returns 100. A cut taxonomy listing, at any `limit`, names how many tags matched and the call that reaches more.
18
+ - **`off_search_products` text queries treat `_`, in-word `'` / `’` / `.`, and decimal commas as part of a word** (#50) — `milk-chocolate`, `milk/chocolate`, and `lait 1,5%` now require each part.
19
+ - **`off_compare_products` compares a repeated barcode once** (#57) — two identical barcodes yield one row and take one of the 10 slots.
20
+ - **Nutrition is labeled per 100 g, or per 100 ml for liquids** (#51) — across tool descriptions, output descriptions, and the table headings; values are unchanged, and a record with only per-serving values gets no per-100 heading.
21
+ - **`off_get_product` labels open-map nutrients with the unit of the nutrient itself** (#52), not its `_unit` sibling.
22
+ - **`off_compare_products` shows the brand on every text row label** (#56) — the Scores and Nutrition rows and the low-completeness note each read `name (brand)`, `Barcode N (brand)`, or `Barcode N`, so a brand-only product keeps its brand and two same-name products stay distinguishable.
23
+ - **Final result pages no longer carry `truncated`, `shown`, or `cap`** (#55) — `truncated` now means more results lie beyond the page.
24
+
25
+ ## Fixed
26
+
27
+ - **A 2xx body missing its result fields fails as the retryable `upstream_error`** (#54) — instead of reading as a product `not_found`, a zero-match search, or a tag with no match. In `off_compare_products` it lists in `failed`; in `off_browse_taxonomy` it takes the noticed offline fallback.
28
+ - **A non-JSON 2xx body fails as `upstream_error`** (#61), not a reason-less `ValidationError`.
29
+ - **Taxonomy lookups end as cancelled when the caller aborts** (#60), instead of degrading to `lookup_failed` or the offline sample.
30
+
31
+ ## Dependencies
32
+
33
+ - `@cyanheads/mcp-ts-core` ^0.13.14 → ^0.13.15 (framework skills synced)
34
+ - dev: `@types/node` ^26.6.4 → ^26.6.5
35
+ - Bun 1.4.2 → 1.4.3 (`packageManager`, Docker base images, README badge)
@@ -1 +1 @@
1
- {"version":3,"file":"browse-taxonomy.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-taxonomy.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIjD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EA2IhC,CAAC"}
1
+ {"version":3,"file":"browse-taxonomy.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-taxonomy.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAWjD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAiKhC,CAAC"}
@@ -3,8 +3,14 @@
3
3
  * @module mcp-server/tools/definitions/browse-taxonomy
4
4
  */
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { MAX_AUTOCOMPLETE_SIZE } from '../../../services/openfoodfacts/openfoodfacts-service.js';
6
7
  import { getTaxonomyService } from '../../../services/taxonomy/taxonomy-service.js';
7
8
  import { mdInline, mdInlineCode } from '../../../utils/markdown.js';
9
+ /**
10
+ * Most tags one call returns. A larger `limit`, up to the suggestions one live lookup reads
11
+ * (`MAX_AUTOCOMPLETE_SIZE`), returns this many and discloses the cut when more matched.
12
+ */
13
+ const TAXONOMY_LIMIT_CAP = 100;
8
14
  export const offBrowseTaxonomyTool = tool('off_browse_taxonomy', {
9
15
  title: 'Browse Food Facts Taxonomy',
10
16
  description: 'Resolve a human term to the canonical Open Food Facts tag ID that off_search_products filters on. Covers categories, labels/certifications, allergens, additives, countries, NOVA groups, and Nutri-Score grades. Pass a search term to resolve against the Open Food Facts vocabulary, which holds tens of thousands of tags; omitting it returns only a small reference list for each facet except NOVA groups and Nutri-Score grades, which are complete. Most tag IDs use the "en:" prefix (e.g. "en:organic", "en:no-gluten", "en:crustaceans"); NOVA groups return bare digits "1"-"4" and Nutri-Score grades bare letters "a"-"e". Pass the id through to off_search_products exactly as returned. Category tags are frequently plural ("kombucha" resolves to "en:kombuchas"), so use the returned id rather than constructing one.',
@@ -33,9 +39,9 @@ export const offBrowseTaxonomyTool = tool('off_browse_taxonomy', {
33
39
  .number()
34
40
  .int()
35
41
  .min(1)
36
- .max(100)
42
+ .max(MAX_AUTOCOMPLETE_SIZE)
37
43
  .default(20)
38
- .describe('Maximum entries to return (1–100, default 20). There is no offset or page input: Open Food Facts offers no cursor for this lookup, so narrow the search term rather than paging. The tag spelling the term itself (e.g. "lentil" → en:lentils) is listed first among the live matches, so it is not the one a small limit cuts.'),
44
+ .describe(`Maximum entries to return (default 20). One call returns at most ${TAXONOMY_LIMIT_CAP}: a larger value, up to ${MAX_AUTOCOMPLETE_SIZE} (the most suggestions one lookup reads), returns the first ${TAXONOMY_LIMIT_CAP} and, when more matched, says how many did. There is no offset or page input: Open Food Facts offers no cursor for this lookup, so narrow the search term rather than paging. The tag spelling the term itself (e.g. "lentil" → en:lentils) is listed first among the live matches, so it is not the one a small limit cuts.`),
39
45
  }),
40
46
  output: z.object({
41
47
  facet: z.string().describe('The facet name that was queried (echoes the input).'),
@@ -58,15 +64,19 @@ export const offBrowseTaxonomyTool = tool('off_browse_taxonomy', {
58
64
  notice: z
59
65
  .string()
60
66
  .optional()
61
- .describe('Caveat about the answer — that the listing is a limited reference list rather than the full vocabulary, that Open Food Facts was unreachable, or that nothing matched and why.'),
67
+ .describe(`Caveat about the answer — that the listing is a limited reference list rather than the full vocabulary, that the live vocabulary could not be reached or gave no usable answer, that nothing matched and why, or that the limit cut matches, naming how many matched and the next call: a larger limit while one would list more, otherwise a narrower term, which is the only route past the first ${TAXONOMY_LIMIT_CAP} (a limit above ${TAXONOMY_LIMIT_CAP} is named too).`),
62
68
  truncated: z.boolean().optional().describe('True when more tags exist beyond the limit.'),
63
69
  shown: z.number().optional().describe('Number of tags returned.'),
64
- cap: z.number().optional().describe('The limit that was applied.'),
70
+ cap: z
71
+ .number()
72
+ .optional()
73
+ .describe(`The limit that was applied — at most ${TAXONOMY_LIMIT_CAP}.`),
65
74
  },
66
75
  async handler(input, ctx) {
67
76
  const svc = getTaxonomyService();
77
+ const limit = Math.min(input.limit, TAXONOMY_LIMIT_CAP);
68
78
  // The service trims and treats a blank term as absent, so the raw input goes through as given.
69
- const result = await svc.search(input.facet, input.search, input.limit, ctx);
79
+ const result = await svc.search(input.facet, input.search, limit, ctx);
70
80
  ctx.log.info('Taxonomy browsed', {
71
81
  facet: input.facet,
72
82
  search: input.search,
@@ -79,14 +89,32 @@ export const offBrowseTaxonomyTool = tool('off_browse_taxonomy', {
79
89
  // pre-filter vocabulary total.
80
90
  //
81
91
  // `notice` is one slot, and ctx.enrich.truncated() writes it from `guidance`, so the service's
82
- // caveat is routed through that argument when both apply rather than being emitted separately
83
- // and silently overwritten. It is the more useful next move than the generic raise-the-limit
84
- // default: on an unfiltered browse the answer is to pass a search term, not a larger cap.
92
+ // caveat follows the cut guidance in that argument rather than being emitted separately and
93
+ // silently overwritten: on an unfiltered browse it says to pass a search term.
94
+ //
95
+ // The cut guidance names the match count and a call that can bring more forward. Below the
96
+ // cap that is a larger limit, or a narrower term when there is one. At the cap a larger limit
97
+ // returns these same tags, so only a narrower term reaches the rest; a limit above the cap is
98
+ // named too, since it is no longer what the cut was measured against. With every match
99
+ // returned, nothing was withheld and nothing is disclosed. The cap branch can say "the term":
100
+ // every embedded sample is smaller than the cap, so only a live search term reaches it.
85
101
  if (result.tags.length < result.matched_in_facet) {
102
+ const matched = result.matched_in_facet;
103
+ let cut;
104
+ if (limit === TAXONOMY_LIMIT_CAP) {
105
+ cut = `${limit < input.limit ? `limit ${input.limit} is above the ${TAXONOMY_LIMIT_CAP} tags one call returns; the` : 'The'} term matched ${matched} tags, and these are the first ${TAXONOMY_LIMIT_CAP}. Narrow the search term with a more specific word to bring the tag you need into the first ${TAXONOMY_LIMIT_CAP} — there is no offset to page past them.`;
106
+ }
107
+ else {
108
+ const hasTerm = Boolean(input.search?.trim());
109
+ const raise = matched <= TAXONOMY_LIMIT_CAP
110
+ ? `Raise limit to ${matched} to list them all`
111
+ : `Raise limit to ${TAXONOMY_LIMIT_CAP} for the first ${TAXONOMY_LIMIT_CAP}`;
112
+ cut = `${hasTerm ? 'The term matched' : 'The listing holds'} ${matched} tags, and these are the first ${limit}. ${raise}${hasTerm ? ', or narrow the search term with a more specific word' : ''}.`;
113
+ }
86
114
  ctx.enrich.truncated({
87
115
  shown: result.tags.length,
88
- cap: input.limit,
89
- ...(result.notice && { guidance: result.notice }),
116
+ cap: limit,
117
+ guidance: [cut, result.notice].filter(Boolean).join(' '),
90
118
  });
91
119
  }
92
120
  else if (result.notice) {
@@ -1 +1 @@
1
- {"version":3,"file":"browse-taxonomy.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-taxonomy.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browse-taxonomy.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-taxonomy.tool.ts"],"names":[],"mappings":"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"}
@@ -31,10 +31,11 @@ export declare const offCompareProductsTool: import("@cyanheads/mcp-ts-core").To
31
31
  reason: z.ZodString;
32
32
  error: z.ZodString;
33
33
  }, z.core.$strip>>>;
34
+ unprocessed: z.ZodOptional<z.ZodArray<z.ZodString>>;
34
35
  }, z.core.$strip>, readonly [{
35
36
  readonly reason: "upstream_error";
36
37
  readonly code: JsonRpcErrorCode.ServiceUnavailable;
37
- readonly when: "Open Food Facts returns a 5xx other than 501 or 504, serves an HTML error page with a 2xx or 5xx status, or is unreachable for a barcode — reported in that barcode's failed entry rather than failing the call";
38
+ readonly when: "Open Food Facts returns a 5xx other than 501 or 504, serves an HTML error page with a 2xx or 5xx status, answers 2xx with a malformed envelope, or is unreachable for a barcode — reported in that barcode's failed entry rather than failing the call";
38
39
  readonly retryable: true;
39
40
  readonly thrownBy: "service";
40
41
  readonly recovery: "Retry the barcodes listed in failed after a brief pause. Rows that already resolved are kept, so only the failures need repeating.";
@@ -1 +1 @@
1
- {"version":3,"file":"compare-products.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/compare-products.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAoJ3E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+RjC,CAAC"}
1
+ {"version":3,"file":"compare-products.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/compare-products.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAgL3E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgUjC,CAAC"}
@@ -9,6 +9,18 @@ import { BARCODE_PATTERN, BARCODE_PATTERN_MESSAGE, } from '../../../services/ope
9
9
  import { mdInline, mdTableCell } from '../../../utils/markdown.js';
10
10
  /** Fields needed for comparison — narrower than full product fetch. */
11
11
  const COMPARE_FIELDS = 'product_name,brands,nutriscore_grade,nova_group,ecoscore_grade,nutriments,completeness';
12
+ /**
13
+ * Most distinct barcodes one call fetches. More, up to the input's hard ceiling, are listed in
14
+ * `unprocessed` without being fetched, so one compare never spends more of the per-minute product
15
+ * budget than this.
16
+ */
17
+ const COMPARE_BARCODE_CAP = 10;
18
+ /** The next step for barcodes left unprocessed, sized to the 2-barcode minimum of a compare. */
19
+ function unprocessedNextStep(count) {
20
+ return count === 1
21
+ ? 'look it up with off_get_product, or compare it with barcodes from this call in another off_compare_products call.'
22
+ : `compare them in another off_compare_products call, up to ${COMPARE_BARCODE_CAP} barcodes at a time.`;
23
+ }
12
24
  /** Extract a numeric value from the raw nutriments map. */
13
25
  function n(raw, key) {
14
26
  const v = raw[key];
@@ -65,9 +77,19 @@ function zeroFoundSummary(notFound, failed) {
65
77
  : 'Open Food Facts refused every request as formed, so check the barcode digits before sending them again.';
66
78
  return `No barcode could be checked — every fetch failed, so nothing was learned about these products. ${recovery}`;
67
79
  }
68
- return ('Open Food Facts answered for every barcode and holds no contributor record for any of them. ' +
80
+ return ('Open Food Facts answered for every barcode compared and holds no contributor record for any of them. ' +
69
81
  'The products may exist without being entered yet — check the digits, or find them by name with off_search_products.');
70
82
  }
83
+ /**
84
+ * A row's text label: `name (brand)`, else `Barcode N (brand)`, else `Barcode N`. Names and brands
85
+ * are contributor-edited, so each is escaped for the context the label lands in — a table cell or
86
+ * an inline note. One helper for every label keeps a brand-only row, or two same-name rows, from
87
+ * reading differently from one table to the next.
88
+ */
89
+ function rowLabel(row, escapeFor) {
90
+ const label = row.product_name ? escapeFor(row.product_name) : `Barcode ${row.barcode}`;
91
+ return row.brands ? `${label} (${escapeFor(row.brands)})` : label;
92
+ }
71
93
  /** Build a comparison row from a raw product. */
72
94
  function buildCompareRow(barcode, raw) {
73
95
  if (!raw)
@@ -113,7 +135,7 @@ function buildCompareRow(barcode, raw) {
113
135
  }
114
136
  export const offCompareProductsTool = tool('off_compare_products', {
115
137
  title: 'Compare Food Products Side-by-Side',
116
- description: 'Side-by-side nutrition and scoring comparison for 2–10 products by barcode. Returns a normalized table of energy (kcal/100g), fat, saturated fat, sugars, salt, protein, fiber, Nutri-Score, NOVA group, and Green-Score. Designed for "which of these cereals is healthiest?" or "compare these pasta brands" workflows. Missing nutrition data for any product is preserved as absent — comparisons are not imputed. A batch is not all-or-nothing: barcodes that resolve are returned even when others fail, with confirmed-missing barcodes listed in not_found and failed fetches listed separately in failed. Scores carry regional formula caveats. Data under ODbL 1.0 — cite Open Food Facts in downstream use.',
138
+ description: 'Side-by-side nutrition and scoring comparison for 2–10 products per call by barcode; a repeated barcode is compared once, so two identical barcodes yield one row, and barcodes past the 10th distinct one are listed in unprocessed rather than fetched. Returns a normalized table of energy (kcal), fat, saturated fat, sugars, salt, protein, and fiber per 100 g, or per 100 ml for liquids, plus Nutri-Score, NOVA group, and Green-Score. Designed for "which of these cereals is healthiest?" or "compare these pasta brands" workflows. Missing nutrition data for any product is preserved as absent — comparisons are not imputed. A batch is not all-or-nothing: barcodes that resolve are returned even when others fail, with confirmed-missing barcodes listed in not_found and failed fetches listed separately in failed. Scores carry regional formula caveats. Data under ODbL 1.0 — cite Open Food Facts in downstream use.',
117
139
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
118
140
  input: z.object({
119
141
  barcodes: z
@@ -122,8 +144,8 @@ export const offCompareProductsTool = tool('off_compare_products', {
122
144
  .regex(BARCODE_PATTERN, BARCODE_PATTERN_MESSAGE)
123
145
  .describe('Product barcode, digits only: 4–40 digits after any leading zeros.'))
124
146
  .min(2)
125
- .max(10)
126
- .describe('2–10 barcodes to compare, returned as one row each in input order. Example: ["3017620422003", "7622210100146"].'),
147
+ .max(100)
148
+ .describe(`Barcodes to compare, returned as one row each in input order; a repeated barcode is compared once, so two identical barcodes yield one row. One call compares at most ${COMPARE_BARCODE_CAP} distinct barcodes: up to 100 are accepted, and those past the ${COMPARE_BARCODE_CAP}th distinct one are returned in unprocessed without being fetched. Example: ["3017620422003", "7622210100146"].`),
127
149
  }),
128
150
  output: z.object({
129
151
  products: z
@@ -154,38 +176,38 @@ export const offCompareProductsTool = tool('off_compare_products', {
154
176
  energy_kcal_100g: z
155
177
  .number()
156
178
  .optional()
157
- .describe('Energy per 100g in kcal. Absent when not entered.'),
179
+ .describe('Energy in kcal per 100 g, or per 100 ml for liquids. Absent when not entered.'),
158
180
  fat_100g: z
159
181
  .number()
160
182
  .optional()
161
- .describe('Total fat per 100g in grams. Absent when not entered.'),
183
+ .describe('Total fat in grams per 100 g, or per 100 ml for liquids. Absent when not entered.'),
162
184
  saturated_fat_100g: z
163
185
  .number()
164
186
  .optional()
165
- .describe('Saturated fat per 100g in grams. Absent when not entered.'),
187
+ .describe('Saturated fat in grams per 100 g, or per 100 ml for liquids. Absent when not entered.'),
166
188
  sugars_100g: z
167
189
  .number()
168
190
  .optional()
169
- .describe('Total sugars per 100g in grams. Absent when not entered.'),
191
+ .describe('Total sugars in grams per 100 g, or per 100 ml for liquids. Absent when not entered.'),
170
192
  salt_100g: z
171
193
  .number()
172
194
  .optional()
173
- .describe('Salt per 100g in grams. Absent when not entered.'),
195
+ .describe('Salt in grams per 100 g, or per 100 ml for liquids. Absent when not entered.'),
174
196
  proteins_100g: z
175
197
  .number()
176
198
  .optional()
177
- .describe('Protein per 100g in grams. Absent when not entered.'),
199
+ .describe('Protein in grams per 100 g, or per 100 ml for liquids. Absent when not entered.'),
178
200
  fiber_100g: z
179
201
  .number()
180
202
  .optional()
181
- .describe('Dietary fiber per 100g in grams. Often absent.'),
203
+ .describe('Dietary fiber in grams per 100 g, or per 100 ml for liquids. Often absent.'),
182
204
  completeness: z
183
205
  .number()
184
206
  .optional()
185
207
  .describe('Data completeness 0–1. Low values mean many fields are missing.'),
186
208
  })
187
209
  .describe('A single product comparison row.'))
188
- .describe('Comparison rows in input order — one per barcode whose fetch completed, whether or not a record exists. Barcodes whose fetch failed have no row here; they appear in failed.'),
210
+ .describe('Comparison rows in input order — one per distinct barcode whose fetch completed, whether or not a record exists. Barcodes whose fetch failed have no row here; they appear in failed. Barcodes past the cap appear in unprocessed.'),
189
211
  succeeded: z.number().describe('Number of barcodes that resolved to a found product.'),
190
212
  not_found: z
191
213
  .array(z.string().describe('Barcode with no contributor record, as provided in input.'))
@@ -202,12 +224,16 @@ export const offCompareProductsTool = tool('off_compare_products', {
202
224
  .describe('A single barcode whose fetch failed.'))
203
225
  .optional()
204
226
  .describe('Barcodes whose fetch failed, with the per-barcode reason. Absent when every fetch completed. A barcode listed here is unknown, not absent from Open Food Facts — retry it with off_get_product before concluding anything about the product.'),
227
+ unprocessed: z
228
+ .array(z.string().describe('Barcode left unprocessed, as provided in input.'))
229
+ .optional()
230
+ .describe(`Distinct barcodes past the ${COMPARE_BARCODE_CAP}th, in input order — never fetched, so nothing is known about them. Absent when the batch held ${COMPARE_BARCODE_CAP} or fewer distinct barcodes. Compare them in another off_compare_products call (2–${COMPARE_BARCODE_CAP} barcodes), or look a single one up with off_get_product.`),
205
231
  }),
206
232
  errors: [
207
233
  {
208
234
  reason: 'upstream_error',
209
235
  code: JsonRpcErrorCode.ServiceUnavailable,
210
- when: "Open Food Facts returns a 5xx other than 501 or 504, serves an HTML error page with a 2xx or 5xx status, or is unreachable for a barcode — reported in that barcode's failed entry rather than failing the call",
236
+ when: "Open Food Facts returns a 5xx other than 501 or 504, serves an HTML error page with a 2xx or 5xx status, answers 2xx with a malformed envelope, or is unreachable for a barcode — reported in that barcode's failed entry rather than failing the call",
211
237
  retryable: true,
212
238
  thrownBy: 'service',
213
239
  recovery: 'Retry the barcodes listed in failed after a brief pause. Rows that already resolved are kept, so only the failures need repeating.',
@@ -239,8 +265,12 @@ export const offCompareProductsTool = tool('off_compare_products', {
239
265
  ],
240
266
  async handler(input, ctx) {
241
267
  const svc = getOpenFoodFactsService();
242
- // Fetch all products in parallel
243
- const settlements = await Promise.allSettled(input.barcodes.map((barcode) => svc.getProductFields(barcode, COMPARE_FIELDS, ctx)));
268
+ // A repeated barcode takes one slot and one fetch. Past the cap, distinct barcodes are listed
269
+ // rather than fetched, so the product budget is spent only on the barcodes compared.
270
+ const distinct = [...new Set(input.barcodes)];
271
+ const compared = distinct.slice(0, COMPARE_BARCODE_CAP);
272
+ const unprocessed = distinct.slice(COMPARE_BARCODE_CAP);
273
+ const settlements = await Promise.allSettled(compared.map((barcode) => svc.getProductFields(barcode, COMPARE_FIELDS, ctx)));
244
274
  const products = [];
245
275
  const not_found = [];
246
276
  const failed = [];
@@ -249,8 +279,8 @@ export const offCompareProductsTool = tool('off_compare_products', {
249
279
  // barcode Open Food Facts confirmed it has no record of, so a rejection is always a failure to
250
280
  // learn anything about that barcode — reporting it as not_found would assert the opposite.
251
281
  // Failures stay per-barcode so a mixed batch keeps the rows that did resolve.
252
- for (let i = 0; i < input.barcodes.length; i++) {
253
- const barcode = input.barcodes[i];
282
+ for (let i = 0; i < compared.length; i++) {
283
+ const barcode = compared[i];
254
284
  const result = settlements[i];
255
285
  if (result.status === 'rejected') {
256
286
  failed.push(describeFailure(barcode, result.reason, (reason) => ctx.recoveryFor(reason).recovery.hint));
@@ -267,12 +297,19 @@ export const offCompareProductsTool = tool('off_compare_products', {
267
297
  }
268
298
  }
269
299
  ctx.log.info('Product comparison completed', {
270
- total: input.barcodes.length,
300
+ total: compared.length,
271
301
  succeeded,
272
302
  not_found: not_found.length,
273
303
  failed: failed.length,
304
+ unprocessed: unprocessed.length,
274
305
  });
275
- return { products, succeeded, not_found, ...(failed.length > 0 && { failed }) };
306
+ return {
307
+ products,
308
+ succeeded,
309
+ not_found,
310
+ ...(failed.length > 0 && { failed }),
311
+ ...(unprocessed.length > 0 && { unprocessed }),
312
+ };
276
313
  },
277
314
  format: (result) => {
278
315
  const attempted = result.products.length + (result.failed?.length ?? 0);
@@ -289,6 +326,9 @@ export const offCompareProductsTool = tool('off_compare_products', {
289
326
  }
290
327
  lines.push('');
291
328
  }
329
+ if (result.unprocessed && result.unprocessed.length > 0) {
330
+ lines.push(`**Not compared (over the ${COMPARE_BARCODE_CAP}-barcode cap):** ${result.unprocessed.join(', ')} — ${unprocessedNextStep(result.unprocessed.length)}\n`);
331
+ }
292
332
  const found = result.products.filter((p) => p.found);
293
333
  if (found.length === 0) {
294
334
  lines.push(zeroFoundSummary(result.not_found, result.failed ?? []));
@@ -299,11 +339,7 @@ export const offCompareProductsTool = tool('off_compare_products', {
299
339
  lines.push('| Product | Barcode | Found | Nutri-Score | NOVA | Eco-Score | Completeness |');
300
340
  lines.push('|:--------|:--------|:------|:------------|:-----|:----------|:-------------|');
301
341
  for (const p of result.products) {
302
- // Product names and brands are contributor-edited: a bare pipe would add a column to this
303
- // row and a line break would end it, so both are neutralized before the cell is written.
304
- const name = p.product_name
305
- ? `${mdTableCell(p.product_name)}${p.brands ? ` (${mdTableCell(p.brands)})` : ''}`
306
- : `Barcode ${p.barcode}`;
342
+ const name = rowLabel(p, mdTableCell);
307
343
  // The exact scalar rides alongside the rounded percentage: 0.7875 and 0.79 both render as
308
344
  // "79%", and a text-only client has no second call that would recover the difference.
309
345
  const completeness = p.completeness !== undefined
@@ -313,18 +349,18 @@ export const offCompareProductsTool = tool('off_compare_products', {
313
349
  lines.push(`| ${name} | ${p.barcode} | ${p.found} | ${grade(p.nutriscore_grade)} | ${p.nova_group ?? 'N/A'} | ${grade(p.ecoscore_grade)} | ${completeness} |`);
314
350
  }
315
351
  // Nutrition table
316
- lines.push('\n### Nutrition per 100g');
352
+ lines.push('\n### Nutrition per 100 g / 100 ml');
317
353
  lines.push('| Product | Energy (kcal) | Fat (g) | Sat. Fat (g) | Sugars (g) | Salt (g) | Protein (g) | Fiber (g) |');
318
354
  lines.push('|:--------|:-------------|:--------|:-------------|:-----------|:---------|:------------|:----------|');
319
355
  for (const p of found) {
320
- const name = p.product_name === undefined ? `Barcode ${p.barcode}` : mdTableCell(p.product_name);
356
+ const name = rowLabel(p, mdTableCell);
321
357
  const fmt = (v) => (v !== undefined ? String(v) : 'N/A');
322
358
  lines.push(`| ${name} | ${fmt(p.energy_kcal_100g)} | ${fmt(p.fat_100g)} | ${fmt(p.saturated_fat_100g)} | ${fmt(p.sugars_100g)} | ${fmt(p.salt_100g)} | ${fmt(p.proteins_100g)} | ${fmt(p.fiber_100g)} |`);
323
359
  }
324
360
  // Completeness notices
325
361
  const lowCompleteness = found.filter((p) => p.completeness !== undefined && p.completeness < 0.5);
326
362
  if (lowCompleteness.length > 0) {
327
- lines.push(`\n*Low completeness (< 50%): ${lowCompleteness.map((p) => mdInline(p.product_name ?? p.barcode)).join(', ')} — many fields may be missing.*`);
363
+ lines.push(`\n*Low completeness (< 50%): ${lowCompleteness.map((p) => rowLabel(p, mdInline)).join(', ')} — many fields may be missing.*`);
328
364
  }
329
365
  lines.push('\n*Data: Open Food Facts (ODbL 1.0) — crowd-sourced. "N/A" = not yet entered.*');
330
366
  return [{ type: 'text', text: lines.join('\n') }];
@@ -1 +1 @@
1
- {"version":3,"file":"compare-products.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/compare-products.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compare-products.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/compare-products.tool.ts"],"names":[],"mappings":"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"}