Skip to main content
Glama
FoodNotes-org

foodex2-encoder

README.md
# foodex2-encoder

Library and stdio MCP server that turns an English food description into an
EFSA FoodEx2 code (MTX catalogue): base term, facet descriptors, and labelled
free text for what the code cannot express.

This repository is the core. A hosted version is available at https://foodex2-encoder.foodnotes.org/ .

## What you need

- Node.js 20+
- Python 3 (one-time catalogue build from the vendored EFSA `.ecf`)
- An OpenAI-compatible model endpoint and API key (OpenRouter is the default)

## Install

```bash
git clone https://github.com/FoodNotes-org/foodex2-encoder.git
cd foodex2-encoder
git submodule update --init
npm install
npm run build:catalogue
npm run build:embeddings
```

`data/` is generated locally (~25 MB) and is gitignored. Rebuild after updating
the `vendor/efsa-catalogues` submodule.

## Model endpoint

Copy `.env` (or export the same variables). OpenRouter needs only a key:

```bash
FOODEX2_LLM_API_KEY=sk-or-…          # or OPENROUTER_API_KEY
# optional:
# FOODEX2_LLM_BASE_URL=https://openrouter.ai/api/v1
# FOODEX2_MODEL=openai/gpt-5.4
```

Any other OpenAI-compatible endpoint works the same way (`FOODEX2_LLM_BASE_URL`,
`FOODEX2_LLM_API_KEY`, `FOODEX2_MODEL`). The **server** calls that endpoint for
classify / select / residual steps; your chat client’s subscription is separate.

## Try it (CLI)

```bash
npm run cli -- encode "orange juice"
npm run cli -- encode "fried rice with chicken"
npm run cli -- traverse "eggplant"    # walk only (debug)
```

Lexical hits return in about a second. Descriptions that need a catalogue walk
typically take 15–25 seconds.

## Try it (MCP stdio)

```bash
npm run server
```

Point a client at that process. Cursor example (`mcp.json`):

```json
{
  "mcpServers": {
    "foodex2-encoder": {
      "command": "npx",
      "args": ["tsx", "/ABS/PATH/TO/foodex2-encoder/src/server.ts"],
      "env": {
        "FOODEX2_LLM_API_KEY": "sk-or-…"
      }
    }
  }
}
```

Tools: `encode` (main), `search_terms`, `get_term`. `encode` returns the code,
base term, facets, free text, fit, method, and a short `explanation`.

## Tests and eval

```bash
npm test                 # unit tests (no model)
npm run lexical-eval     # lexical base-term cases (no model)
npm run encode-eval      # end-to-end gold set (needs a model key)
```

`eval/encode-base-term.yaml` is a small, focused regression set: one realistic
case per capability (catalogue synonym, model synonym, named dish, restraint,
and so on). Gold rules can require or forbid facets and free-text fragments;
`expected` may list several acceptable base codes (any-of).

`npm run encode-eval` records the model and MTX version, and on a complete run
writes `eval/results/encode-base-term.json` for publishing or sharing with the
public core repo. Always cite the model named in that file’s `_provenance`.

## Layout

```
src/encode/     lexical → traverse → select → residuals
src/search/     lexical + embedding retrieval
src/server.ts   stdio MCP
src/tools.ts    tool handlers (shared surface for a future Worker wrapper)
data/           generated catalogue + embeddings (local)
eval/           gold cases + results artifact
vendor/         EFSA catalogues (submodule)
```

## Licence

MIT — see [LICENSE](LICENSE). Copyright (c) 2026 e-accent BV.

That grant covers this software. EFSA catalogue files under `vendor/efsa-catalogues/`
(and generated `data/` derived from them) remain subject to EFSA’s terms.