# AntiCoco đŸ„„đŸš« Serveur **MCP** qui donne accĂšs Ă  ton compte **HelloFresh** (France, `hellofresh.fr`) pour : lire le menu de la semaine, **exclure des ingrĂ©dients** (la **noix de coco** en prioritĂ©), **proposer une shortlist** de recettes, puis **enregistrer ta sĂ©lection** aprĂšs confirmation. Client MCP visĂ© : **Hermes** (Nous Research), qui tourne sur le mĂȘme homelab. > ⚠ HelloFresh n'a pas d'API publique. AntiCoco s'appuie sur l'API interne `gw/` du site > (non documentĂ©e, susceptible de changer) — **usage strictement personnel**. ## État (validĂ© de bout en bout, 2026-06) ✅ **Boucle complĂšte testĂ©e sur le vrai compte** : lecture du menu (`menus-service/menus` → dĂ©tails batch `recipes/recipes?ids=
`), filtrage coco (4/85 dĂ©tectĂ©es, faux positifs des tags internes neutralisĂ©s), proposition classĂ©e, et **Ă©criture rĂ©elle rĂ©ussie** (`PUT /v1/carts/{week}`, HTTP 200) — sĂ©lection par index de course, ids de compte dĂ©rivĂ©s dynamiquement. ✅ **Auth autonome (pur HTTP)** : le token (30 min) est rafraĂźchi par un simple `POST /gw/refresh` (le endpoint que la SPA appelle), **sans navigateur**. Le refresh_token roule par fenĂȘtres de 60 j, remises Ă  zĂ©ro Ă  chaque refresh → un homelab **allumĂ© en continu** reste authentifiĂ© sans intervention ni re-sync. Le navigateur headless ne sert plus que de filet de secours. > ⚠ La chaĂźne de refresh **peut casser** (homelab Ă©teint trop longtemps, ou refresh_token > consommĂ© hors `_refresh_session`) → `POST /gw/refresh` renvoie `invalid_grant` et > `auth_status()` passe Ă  `logged_in:false`. Le seul remĂšde est de **refaire le login CDP** > (cf. ci-dessous). **Restaurer un ancien `.session/` ne marche pas** : les refresh_token sont > rotatifs, les anciens sont morts. > ⚠ La connexion **directe** automatisĂ©e (Playwright/Chromium qui remplit le formulaire) est > bloquĂ©e par l'anti-bot HelloFresh. La session se crĂ©e donc via **attache CDP Ă  ton vrai Chrome** > (`tools/attach_capture.py`), oĂč le login marche normalement. ## Architecture ``` Hermes ──HTTP──▶ server.py (FastMCP, :9200/mcp) navigateur ──▶ server.py (UI admin, :9200/) ├─ hellofresh/auth.py session storage_state + refresh HTTP /gw/refresh ├─ hellofresh/api.py httpx : menu, dĂ©tails, deliveries, PUT cart ├─ hellofresh/filter.py exclusion (coco !) + scoring prĂ©fĂ©rences ├─ hellofresh/webui.py page d'admin + API JSON (Ă©dition Ă  chaud) └─ config/ excludes.json · prefs.json · endpoints.json ``` ## Mise en route ### 1. Installer ```bash pip install -r requirements.txt playwright install chromium cp .env.example .env ``` ### 2. CrĂ©er (ou rĂ©-crĂ©er) la session — login via TON Chrome, anti-bot contournĂ© C'est **la seule mĂ©thode de login qui marche** : un navigateur pilotĂ© par Playwright (mĂȘme avec `channel=chrome`) est bloquĂ© par l'anti-bot HelloFresh. On lance donc le vrai Chrome en mode debug et on s'y **attache en CDP** (lecture seule). MĂȘme procĂ©dure pour la 1Êłá”‰ session **et** pour une rĂ©-authentification aprĂšs `invalid_grant`. ```bash # 1) Lance ton vrai Chrome en mode debug avec un PROFIL DÉDIÉ (Chrome ≄149 refuse le debug # sur le profil par dĂ©faut). Ta fenĂȘtre Chrome habituelle peut rester ouverte. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ --remote-debugging-port=9222 --user-data-dir="$HOME/.hf-chrome-debug" \ https://www.hellofresh.fr/my-account/deliveries/menu # 2) Connecte-toi dans cette fenĂȘtre (page de login = https://www.hellofresh.fr/login). # 3) Pendant que Chrome reste ouvert et connectĂ©, attache la capture : python tools/attach_capture.py # capture trafic + exporte .session/storage_state.json ``` `attach_capture` doit tourner **pendant** que tu (re)charges la page menu, pour capter le trafic. Il **Ă©crase** `.session/storage_state.json` avec la session fraĂźche (cookies ~60 j) — ne pas restaurer un vieux backup Ă  la place (refresh_token rotatif = anciens morts). VĂ©rifie ensuite : `python -c "from hellofresh import auth, json; print(auth.auth_status())"` → `logged_in: true`. `config/endpoints.json` est dĂ©jĂ  rempli ; rejoue `attach_capture` si l'API change. ### 3. Tester en local (headless, comme le homelab) ```bash python server.py # auth via storage_state, refresh token automatique ``` ## DĂ©ploiement homelab (Docker) `.session/` et `.env` ne sont **jamais** versionnĂ©s. Workflow : ```bash # 1. Sur le Mac : gĂ©nĂ©rer la session (cf. « Mise en route » §2 → .session/storage_state.json) # 2. Pousser le code git add -A && git commit -m "..." && git push # 3. Synchroniser la session : storage_state.json SEUL (cookie = refresh_token vierge). # NE PAS copier token.json (son refresh_token a pu ĂȘtre consommĂ© localement → invalide). scp .session/storage_state.json homelab:/home/jerem/AntiCoco/.session/ # 4. Sur le homelab : supprimer l'ancien token.json (root, Ă©crit par le conteneur) pour que # la chaĂźne reparte du cookie frais, puis dĂ©ployer. ssh homelab cd /home/jerem/AntiCoco docker exec anticoco rm -f /app/.session/token.json # sinon un vieux RT mort persiste git pull && docker compose up -d --build # bootstrap + vĂ©rif de la chaĂźne (doit afficher True puis logged_in: true) : docker exec anticoco python -c "from hellofresh import auth; print('refresh', bool(auth._refresh_session())); print(auth.auth_status())" ``` VĂ©rifier : `curl -s http://127.0.0.1:9200/mcp` (le serveur rĂ©pond au handshake MCP). > ⚠ **Un seul propriĂ©taire du refresh_token.** Le refresh_token est **rotatif** (chaque > `/gw/refresh` invalide le prĂ©cĂ©dent). Le Mac sert UNIQUEMENT Ă  capturer la session ; une fois > `storage_state.json` synchronisĂ©, **ne lance plus `server.py` ni d'appel API depuis le Mac** — > sinon Mac et homelab se disputent le token et la chaĂźne casse (`invalid_grant`). Le homelab, > seul consommateur, reste authentifiĂ© tant qu'il tourne. En cas de casse : refaire §2 + §3-4. ## IntĂ©gration Hermes Enregistrer AntiCoco dans la config MCP de Hermes (cĂŽtĂ© homelab), URL `http://127.0.0.1:9200/mcp` (transport streamable-http). > Si Hermes n'accepte que le **stdio**, changer la derniĂšre ligne de `server.py` > (`mcp.run(transport="stdio")`) et lancer le serveur en sous-processus — le reste est identique. ## Outils MCP exposĂ©s | Outil | RĂŽle | |-------|------| | `hf_auth_status()` | Ă©tat de connexion | | `hf_login()` | (re)connexion + capture token | | `hf_list_weeks()` | semaines modifiables | | `hf_next_delivery()` | **prochaine box rĂ©ellement sĂ©lectionnĂ©e** (≈4 recettes) + date/cutoff, images servables — prĂȘt Telegram | | `hf_favorites()` | recettes **favorites** du compte (images servables) | | `hf_get_menu(week)` | toutes les recettes, avec `contains_excluded` et `is_favorite` | | `hf_propose(week, count=0)` | shortlist **sans coco ni premium**, classĂ©e par prĂ©fĂ©rences | | `hf_confirm_selection(week, recipe_ids, allow_premium=False)` | **Ă©crit** la sĂ©lection (refuse coco **et** recettes payantes hors abonnement) | | `hf_get_excludes()` / `hf_add_exclude(term)` / `hf_remove_exclude(term)` | gĂ©rer la liste d'exclusion | Les recettes premium (supplĂ©ment, `chargeSetting` cĂŽtĂ© HelloFresh) portent `is_premium` / `surcharge_eur` dans chaque rĂ©ponse. `hf_confirm_selection` les refuse par dĂ©faut ; `allow_premium=True` accepte sciemment le surcoĂ»t. ## Interface web d'admin Sur le **mĂȘme port que MCP** : `http://127.0.0.1:9200/` (MCP reste sur `/mcp`). Édition Ă  chaud (prise en compte au prochain appel, sans redĂ©marrage) de : - la **liste d'exclusion** (coco & co) ; - les **prĂ©fĂ©rences** de scoring `liked` / `disliked`. Le port `9200` est exposĂ© sur tout le LAN (cf. `docker-compose.yml`), **sans authentification** — rĂ©seau privĂ© de confiance. AccĂšs direct : `http://:9200/`. ## Configuration - `config/excludes.json` — ingrĂ©dients bannis (matching insensible casse/accents). Coco dĂ©jĂ  listĂ©e. - `config/prefs.json` — mots-clĂ©s `liked`/`disliked` pour classer les propositions. - `config/endpoints.json` — URLs gateway rĂ©elles + transfo CDN images (`image_cdn_*` : les URLs recettes CloudFront `
/0,0/image/X` rĂ©pondent en 502, réécrites vers `img.hellofresh.com/
/hellofresh_s3/image/X`). SĂ©lection courante via `my-deliveries/menu`, favoris via `cfs/v2/favorites/recipe`. Re-dĂ©couverte : `tools/probe_selection.py` / `tools/probe_menu_capture.py` (attache CDP).