Initial commit: Mealie MCP server
Ports a working Hermes agent skill to an MCP server. The skill's value was not its Mealie endpoints but ~25 operational rules found by running imports against the live instance; those are now code with tests rather than prompt text. The server owns deterministic mechanics — auth, the non-default User-Agent Cloudflare requires, the `extension` field on cover uploads, PATCH instead of the 500-ing bulk-action endpoints, decimal-comma normalization, and restoring Swedish display text after parsing. Language and taste judgement stay with the model, fed by the four reference notes shipped as MCP resources. verify_recipe runs the finished-import definition as code so an agent cannot report success on a recipe with an English ingredient line, a missing cover, or ingredients still in the "Click Parse" state. Home Assistant is optional and isolated; the Obsidian meal plan is out of scope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# External Mealie hostname via Cloudflare
|
||||
|
||||
## Symptom
|
||||
Authenticated API calls to `https://recept.famfallman.com` can fail with:
|
||||
|
||||
- HTTP `403 Forbidden`
|
||||
- response body: `error code: 1010`
|
||||
- `Server: cloudflare` header
|
||||
|
||||
This is not the same as a bad Mealie token (`401`).
|
||||
|
||||
## Reproduction
|
||||
Python `urllib` with its default user-agent may trigger the block:
|
||||
|
||||
```python
|
||||
import os, urllib.request
|
||||
base = 'https://recept.famfallman.com'
|
||||
token = os.environ['MEALIE_API_TOKEN']
|
||||
req = urllib.request.Request(
|
||||
f'{base}/api/users/self',
|
||||
headers={'Authorization': f'Bearer {token}'},
|
||||
)
|
||||
urllib.request.urlopen(req, timeout=30)
|
||||
```
|
||||
|
||||
Observed failure pattern:
|
||||
- default `Python-urllib/...` user-agent -> `403`
|
||||
- explicit `User-Agent: Hermes-Debug/1.0` -> `200`
|
||||
- `curl` with explicit user-agent -> `200`
|
||||
|
||||
## Working patterns
|
||||
|
||||
### curl
|
||||
```bash
|
||||
BASE_URL="${MEALIE_BASE_URL:-https://recept.famfallman.com}"
|
||||
AUTH=(-H "Authorization: Bearer $MEALIE_API_TOKEN")
|
||||
UA=(-H "User-Agent: Hermes-Debug/1.0")
|
||||
curl -s "$BASE_URL/api/users/self" "${AUTH[@]}" "${UA[@]}"
|
||||
```
|
||||
|
||||
### Python
|
||||
```python
|
||||
headers={
|
||||
'Authorization': f'Bearer {token}',
|
||||
'User-Agent': 'Hermes-Debug/1.0',
|
||||
}
|
||||
```
|
||||
|
||||
## Interpretation
|
||||
If the same token works on the LAN URL and the external host returns Cloudflare `403/1010`, suspect edge/WAF bot filtering before suspecting Mealie auth, DNS, or the token.
|
||||
|
||||
## Import-response quirk discovered in the same session
|
||||
`POST /api/recipes/create/url` may return a JSON string slug like:
|
||||
|
||||
```json
|
||||
"grillat-laxpaket-med-sparris"
|
||||
```
|
||||
|
||||
Do not assume the import response is a full object. Follow with:
|
||||
|
||||
```bash
|
||||
curl -s "$BASE_URL/api/recipes/<slug>" "${AUTH[@]}" "${UA[@]}"
|
||||
```
|
||||
|
||||
to verify title, image, and parsed ingredients.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Fredrik + familjens smakprofil för Mealie-import
|
||||
|
||||
Den här profilen är tänkt att användas när nya recept ska väljas, prioriteras, importeras eller filtreras för Fredrik och familjen.
|
||||
|
||||
## Baslinje från nuvarande Mealie-bibliotek
|
||||
Analysen bygger på cirka 198 recept i Mealie.
|
||||
|
||||
Tydliga mönster i befintliga recept:
|
||||
- mycket **kyckling**, **lax**, **pasta**, **ris**, **grytor** och **ugnsrätter**
|
||||
- stark dragning åt **medelhav/italienskt** och **asiatiskt**
|
||||
- återkommande smaker: **citron**, **vitlök**, **parmesan**, **feta**, **örter**, **chili**, **kokos**, **curry**
|
||||
- tydlig acceptans för både **vardagsmat** och **helgigare rätter**
|
||||
- familjen verkar gilla rätter som är **krämiga**, **smakrikt kryddade**, **syrliga/friska** och ofta har tydlig sälta/umami
|
||||
|
||||
Exempel på representativa recept i biblioteket:
|
||||
- Grönkålssoppa med grön curry
|
||||
- Svamp- och salsicciapasta med tryffel
|
||||
- Kycklingcurry med ris
|
||||
- Citron- och örtris med halloumi
|
||||
- Grillad lax med krämig citron- och spenatsås
|
||||
- Asiatiska köttbullar i krämig kokos- och panengcurrysås
|
||||
- Persisk biff/ärtgryta – Khoresh Gheymeh
|
||||
- Djoje Kebab – saffransdoftande persiska kycklingspett
|
||||
- Fesenjan - Kycklinggryta med valnötter och granatäpple
|
||||
|
||||
## Smaker att prioritera
|
||||
Prioritera recept med en eller flera av dessa egenskaper:
|
||||
|
||||
### Proteiner
|
||||
- kyckling
|
||||
- lax och annan mild fisk
|
||||
- lamm i rätt sammanhang
|
||||
- nöt i gryta, kebab, spett eller färsrätter
|
||||
- korv i smakrika vardagsrätter
|
||||
- vegetariskt när det är tydligt smakdrivet, t.ex. halloumi, feta, örter, citron, svamp eller baljväxter
|
||||
|
||||
### Smakprofil
|
||||
- syrligt och fräscht: citron, lime, granatäpple, yoghurt, örter
|
||||
- varmt och kryddigt: curry, chili, saffran, spiskummin, paprika, vitlök
|
||||
- runt och krämigt: kokosmjölk, yoghurt, ost, gräddiga eller sammetslena såser
|
||||
- umami och sälta: parmesan, feta, brynt/rostad smak, tomat, lök
|
||||
|
||||
### Rätttyper
|
||||
- grytor
|
||||
- soppor
|
||||
- ugnsrätter och gratänger
|
||||
- risrätter
|
||||
- pastarätter
|
||||
- grillat/spett/kebab
|
||||
- familjevänliga plock- eller brickrätter
|
||||
|
||||
## Persisk/iransk prioritering
|
||||
Eftersom Carin/familjen vill ha in mer iranskt ska iranska recept ges **extra hög prioritet** även om de är underrepresenterade i nuvarande bibliotek.
|
||||
|
||||
### Särskilt önskvärda iranska rätter
|
||||
- khoresh-rätter, t.ex. ghormeh sabzi, gheymeh, fesenjan
|
||||
- kebab, koobideh, joojeh/djoojeh, barg
|
||||
- risrätter/polo, särskilt med saffran, dill, bär eller tahdig
|
||||
- ash och andra persiska soppor
|
||||
- rätter med granatäpple, valnötter, saffran, torkad lime, sumak, mynta eller mycket örter
|
||||
- vardagsvänliga iranska kyckling-, ris- och grytrecept
|
||||
|
||||
### Iranska recept ska gärna vara
|
||||
- autentiska eller tydligt iranskinspirerade, inte bara "mellanöstern" i största allmänhet
|
||||
- familjevänliga och möjliga att laga hemma utan alltför specialiserad restaurangutrustning
|
||||
- smakrika men inte beroende av extrem hetta
|
||||
- skrivna/importerade på svenska i Mealie
|
||||
|
||||
## Negativa signaler / lägre prioritet
|
||||
Prioritera ned recept som är:
|
||||
- torra och lågintensiva utan syra, örtighet eller krydddjup
|
||||
- väldigt söta utan balans
|
||||
- ultraprocessade/snackiga snarare än riktiga middagsrecept
|
||||
- alltför amerikanska dessert-/fastfoodkopior om de inte är ovanligt bra
|
||||
- starkt fokuserade på beige buffémat utan friskhet, örter, syra eller kryddkaraktär
|
||||
|
||||
## Praktisk rankingregel för importbevakning
|
||||
När flera kandidater finns, prioritera i ungefär denna ordning:
|
||||
1. iranskt/persiskt som verkar gott och trovärdigt
|
||||
2. recept med kyckling, lax, gryta, ris, citron, örter, yoghurt, saffran, vitlök eller curry
|
||||
3. familjevänliga vardagsrätter med tydlig smakprofil
|
||||
4. vegetariska recept om de fortfarande känns smakrika och "riktiga" som middag
|
||||
5. övrigt
|
||||
|
||||
## Instruktion för framtida importjobb/cron
|
||||
När ett automatiskt jobb ska välja recept att importera:
|
||||
- välj hellre **färre men mer träffsäkra** recept än många svaga
|
||||
- favorisera svenska eller lättöversatta recept med tydliga ingredienser och steg
|
||||
- om två recept verkar lika bra: välj det som är mer **iranskt**, mer **familjevänligt**, eller mer i linje med **kyckling/lax/gryta/ris/citron/örter**
|
||||
- undvik dubletter mot befintliga Mealie-recept
|
||||
- om iranska recept hittas: var mer generös med import även om exakt smakmatch är något osäkrare, eftersom biblioteket aktivt ska breddas åt det hållet
|
||||
@@ -0,0 +1,44 @@
|
||||
# Allrecipes post-import pitfalls
|
||||
|
||||
Den här referensen dokumenterar konkreta fel som observerats i live-körning mot Fredriks Mealie-instans och ska användas när recept importeras från Allrecipes eller andra engelskspråkiga receptsidor.
|
||||
|
||||
## Verifierade problem
|
||||
|
||||
### 1. `image` i API betyder inte nödvändigtvis att omslagsbilden syns i UI
|
||||
- Ett recept kan ha ett icke-tomt `image`-fält och ändå sakna synlig omslagsbild i gränssnittet.
|
||||
- Därför måste importflödet alltid köra ett explicit bildsteg efter import.
|
||||
- För lokal upload på Fredriks Mealie-instans måste `PUT /api/recipes/<slug>/image` skicka med multipart-fältet `extension` (t.ex. `jpg` eller `webp`). Utan det kan uppladdningen fastna i validering och bilden blir inte korrekt sparad.
|
||||
|
||||
### 2. Bulk-actions för taggar/kategorier är opålitliga i denna instans
|
||||
Följande endpoints gav 500-fel under verkliga körningar:
|
||||
- `/api/recipes/bulk-actions/tag`
|
||||
- `/api/recipes/bulk-actions/categorize`
|
||||
|
||||
Använd i stället direkt PATCH på receptets `tags` och `recipeCategory`.
|
||||
|
||||
### 3. Mealie-parsern kan förstöra svenska display-rader
|
||||
Exempel på observerade fel:
|
||||
- `4,7 dl basmatiris` blev något i stil med `47⁄10 liter ...`
|
||||
- parsern gav konstiga bråktecken, dubblerade enheter och skräptecken i `display`
|
||||
- vissa parser-resultat gav märkliga `food.name`-värden för svenska rader
|
||||
|
||||
### 4. Svensk decimal-komma är känsligt
|
||||
Parsern hanterar ofta struktur bättre om decimal-komma först normaliseras till punkt för parseranropet, men den normaliserade raden ska inte användas som slutlig `display` i Mealie.
|
||||
|
||||
## Rekommenderad motstrategi
|
||||
|
||||
1. Bevara alltid en canonical svensk ingrediensrad separat.
|
||||
2. Använd parsern endast för `quantity`, `unit`, `food`.
|
||||
3. Återställ den mänskliga svenska raden till `display` och `note` efter parse.
|
||||
4. Om parsern ger `food.name` utan `id` och du behöver strukturen kvar efter PATCH, skapa/återanvänd riktig Mealie food först.
|
||||
5. För Allrecipes: om scrape-bild inte blir stabil i UI, ladda ner hero-bilden och använd `PUT /api/recipes/<slug>/image`.
|
||||
6. Om Köket.se- och YouTube-importer fortsätter att visa fungerande omslagsbilder medan Allrecipes-importer inte gör det, behandla det som ett källspecifikt Allrecipes-problem. Gå då direkt till explicit hero-bild-download + `PUT /api/recipes/<slug>/image` i stället för bredare generell felsökning.
|
||||
7. Om parsern fortfarande gör visningen ful efter cleanup, välj läsbarhet före struktur för just de trasiga raderna: nollställ `quantity` / `unit` / `food` och spara rena svenska `display` / `note` i stället för att behålla dålig automatisk rendering.
|
||||
|
||||
## Definition av klar import
|
||||
Ett importerat recept räknas inte som klart förrän:
|
||||
- svensk titel/beskrivning/instruktioner är satta
|
||||
- `recipeIngredient[].display` ser normala ut för människa
|
||||
- parsern inte lämnar receptet i `Click Parse`-läge
|
||||
- bild har satts eller verifierats explicit
|
||||
- taggar/kategorier är applicerade eller medvetet hoppade över
|
||||
@@ -0,0 +1,31 @@
|
||||
# Köket-attribution efter Mealie-import
|
||||
|
||||
När ett recept importeras från `koket.se` följer inte receptmakare/program alltid med på ett synligt sätt i Mealie.
|
||||
|
||||
Verifierat arbetssätt:
|
||||
|
||||
1. Importera receptet via `POST /api/recipes/create/url`.
|
||||
2. Läs källsidan och hämta explicit attribution från sidan själv:
|
||||
- `Av: <namn>` → använd som `extras.author`
|
||||
- `Från: <program>` → använd som `extras.source`
|
||||
3. Läs tillbaka receptet via slug.
|
||||
4. PATCH:a receptet så att attribution sparas både som metadata och synligt:
|
||||
|
||||
```json
|
||||
{
|
||||
"extras": {
|
||||
"author": "Zeina Mourtada",
|
||||
"source": "Kökets middag"
|
||||
},
|
||||
"description": "... Recept av Zeina Mourtada. Från Kökets middag."
|
||||
}
|
||||
```
|
||||
|
||||
Varför båda behövs:
|
||||
- `extras.author` / `extras.source` är API-korrekt lagring.
|
||||
- En kort text i `description` gör attributionen synlig i vanliga Mealie-vyer.
|
||||
|
||||
Regel:
|
||||
- Gissa aldrig författare eller källa.
|
||||
- Ta bara värden som uttryckligen står på källsidan.
|
||||
- Verifiera efter PATCH att både `extras` och `description` faktiskt uppdaterades.
|
||||
Reference in New Issue
Block a user