Files
mcp-familywall/SPEC.md
T
marcus bf086a4f84 feat(meal-planner): add is_from_recipe_box field to get_meal_plan (v0.10.3)
Join recipeList[] from API response as a lookup table: isRecipe="true"
means a real recipe from the recipe box, "false" is a free-text stub.
Dish entries get is_from_recipe_box=true/false; meal entries get null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 11:31:51 +02:00

811 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Family Wall API Spezifikation
Erarbeitet durch Browser-Traffic-Analyse und React-Fiber-Analyse (April 2026).
Es gibt keine offizielle API-Dokumentation.
## Base URL
https://api.familywall.com/api
## Authentifizierung
### Login
POST https://api.familywall.com/api/log2in
Content-Type: application/x-www-form-urlencoded
**Request-Parameter:**
| Parameter | Wert |
|---|---|
| `identifier` | E-Mail-Adresse |
| `password` | Passwort |
| `type` | `"email"` (verifiziert) |
**Response (Erfolg):**
```json
{ "r": { "r": <SessionObject> } }
```
**Response (Fehler):**
```json
{ "ex": { "ex": <ErrorObject> } }
{ "un": { "un": <ErrorObject> } }
```
Der Server setzt nach erfolgreichem Login ein Session-Cookie:
`Set-Cookie: JSESSIONID=<session-id>`
### Folgecalls (nach Login)
| | |
|---|---|
| **Cookie** | `JSESSIONID=<session-id>` |
| **Header** | `Tokencsrf: <session-id>` (identisch zur JSESSIONID) |
| **Content-Type** | `application/x-www-form-urlencoded` |
### Logout
POST https://api.familywall.com/api/log2out
Keine Parameter. Session wird serverseitig invalidiert.
### Session-Strategie
Kein Session-Caching. Jeder MCP-Tool-Call führt folgende Sequenz aus:
```
POST /api/log2in → Session-ID
POST /api/<endpoint> → Nutzdaten (ggf. mehrere Calls in einer Session)
POST /api/log2out → Session invalidieren
```
Credentials (E-Mail + Passwort) werden einmalig via `mcp-familywall setup`
im OS Keyring gespeichert (Keys: `email`, `password`).
## Fehlerbehandlung
### Silent-Fail Warnung
Die API gibt Fehler manchmal NICHT auf Top-Level zurück, sondern eingebettet:
```json
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
```
`fw_client.py` prüft beide Ebenen (`a00.un.un` und Top-Level `ex`/`un`)
und wirft `FamilyWallError`. Nie eine Response als Erfolg werten ohne
beide Fehlerebenen zu prüfen.
### Sentinel-Wert `$empty`
Das FiZ-Server-Framework nutzt `$empty` als Sentinel-String um optionale
Felder zu löschen. Alle anderen Werte (`""`, `null`, `"null"`, `"0"`,
`"-1"`, `"remove"`, `"clear"`, Epoch-Timestamps) werden vom Server mit
`"is not a valid Date"` abgelehnt.
**Aktuell genutzt für:**
- `dueDate=$empty` → löscht Fälligkeitsdatum in `taskupdate2`
**Gefunden durch:** React-Fiber-Analyse des nativen `datetime-local`-Inputs
nach Klick auf "Löschen" im Custom-Datepicker wird der Input-Wert auf
`"$empty"` gesetzt, was beim Speichern an die API gesendet wird.
## Bekannte Endpoints
### `famlistfamily` Kreise + Mitglieder abrufen
POST https://api.familywall.com/api/famlistfamily
**Body-Parameter:** keine
**Response-Struktur:**
```
a00.r.r[] → Kreise
.metaId → Kreis-ID (z.B. "family/23431854")
.name → Kreis-Name
.members[] → Mitglieder des Kreises
.accountId → numerische User-ID
.firstName → Anzeigename (bevorzugen)
.name → E-Mail als Fallback
.role → Familienrolle (Parent/Child/Unknown)
.right → Berechtigung (SuperAdmin etc.)
.color → Profilfarbe (#RRGGBB)
.medias[0].pictureUrl → Avatar-URL
.identifiers[type=Email].value → E-Mail-Adresse
```
### `accgetallfamily` Listen, Tasks, Kategorien abrufen
POST https://api.familywall.com/api/accgetallfamily
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `a01call` | `"taskcategorysync"` |
| `a02call` | `"tasksync"` |
**Response-Struktur:**
```
a00.r.r[] → Kategorien (taskcategorysync)
.metaId → Kategorie-ID
.name → Kategoriename (Systembezeichnung)
.taskListType → SHOPPING_LIST oder TODOS
.sortingIndexByTaskList → Sortierreihenfolge pro Liste
.rights.canDelete → "true" = custom, null = System
.locale → Sprachcode (de/en/fr/...), fehlt bei custom
a02.r.r.updatedCreated[] → Tasks (tasksync)
.metaId → Task-ID
.text → Aufgabentext
.description → optionale Beschreibung
.taskListId → Listen-ID
.complete → "true" / "false" (String!)
.taskCategoryId → Kategorie-ID (optional)
.dueDate → Fälligkeitsdatum (ISO 8601, optional)
.assignee[] → Liste von Member-IDs (optional)
.assigneeIds[] → alternativ zu assignee[]
```
### `wallget` Wall-Aktivitäten abrufen
POST https://api.familywall.com/api/wallget
**Response-Struktur:**
```
a00.r.r[]
.metaId → Post-ID (z.B. "wall/23431854_31119189")
.type → STATUS, FAMILY_CREATED, etc.
.text → Post-Text
.modifDate → Timestamp (ISO 8601)
.creator.accountId → Author-ID
.moodMap → {"<accountId>": ["STAR"]} für Likes
.moodStarShortcut → true wenn geliked
.comments[] → Kommentare
```
### `taskcreate2` Task erstellen
POST https://api.familywall.com/api/taskcreate2
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `taskListId` | ja | Listen-metaId |
| `text` | ja | Aufgabentext |
| `description` | nein | Beschreibung |
| `taskCategoryId` | nein | Kategorie-metaId (vollständig, z.B. `taskCategory/23431854_200`) |
| `dueDate` | nein | ISO 8601 (z.B. `2026-04-30T18:00:00`) |
| `assignee` | nein | Member-accountId, mehrfach sendbar für mehrere Zuweisungen |
**Response:**
```
a00.r.r → vollständiges Task-Objekt
.metaId → neue Task-ID
```
### `taskupdate2` Task aktualisieren
POST https://api.familywall.com/api/taskupdate2
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `metaId` | ja | Task-metaId |
| `text` | nein | neuer Titel |
| `description` | nein | neue Beschreibung |
| `taskCategoryId` | nein | neue Kategorie-metaId |
| `dueDate` | nein | ISO 8601 oder `$empty` zum Löschen |
| `assignee` | nein | Member-accountId (mehrfach sendbar), `""` zum Entfernen aller |
| `taskListId` | nein | neue Listen-metaId (verschiebt Task) |
**Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
**Response:**
```
a00.r.r → vollständiges Task-Objekt
```
### `taskmark` Task abhaken/wiedereröffnen
POST https://api.familywall.com/api/taskmark
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `taskId` | Task-metaId ⚠️ nicht `metaId`! |
| `complete` | `"true"` oder `"false"` (String!) |
**Response:**
```
a00.r.r → Task-Objekt mit lastAction: "MARK_COMPLETED"
```
### `metadelete` Task löschen
POST https://api.familywall.com/api/metadelete
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `id` | Task-metaId ⚠️ nicht `metaId`! |
**Response:**
```
a00.r.r → "true" (String)
```
### `wallmood` Post liken
POST https://api.familywall.com/api/wallmood
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `wall_message_id` | Post-metaId ⚠️ nicht `wallId` oder `id`! |
| `moodType` | `"STAR"` für Like |
**Bekannte Einschränkungen:**
- Unlike: Endpoint/Parameter unbekannt (Service Worker verschlüsselt Request-Body)
- Self-Like: API antwortet 200, macht aber serverseitig nichts
- `moodType="NONE"` und andere Werte haben keine Wirkung
**Response:**
```
a00.r.r → Wall-Objekt mit moodMap, refAction: "MOOD_STAR"
```
### `taskcategoryput` Kategorie erstellen/aktualisieren
POST https://api.familywall.com/api/taskcategoryput
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `name` | ja | Kategoriename |
| `emoji` | nein | Unicode-Emoji oder beliebiger String |
**Hinweis:** Kategorien sind family-wide `listId` hat keine Wirkung.
**Response:**
```
a00.r.r → Kategorie-Objekt mit metaId
```
### `taskcategorydelete` Kategorie löschen
POST https://api.familywall.com/api/taskcategorydelete
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `id` | Kategorie-metaId ⚠️ nicht `metaId`! |
**Hinweis:** System-Kategorien (`rights.canDelete=null`) können technisch
gelöscht werden, sind dann aber dauerhaft weg und nicht wiederherstellbar.
MCP-Server schützt dagegen durch Check auf `rights.canDelete`.
### `taskcreatelist` Liste erstellen
POST https://api.familywall.com/api/taskcreatelist
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `name` | ja | Listen-Name (max 200 Zeichen) |
| `taskListType` | ja | `"SHOPPING_LIST"` oder `"TODOS"` |
| `sharedToAll` | nein | `"true"` / `"false"` (default: `"true"`) |
| `color` | nein | Hex-Farbwert z.B. `"#4784EC"` |
| `emoji` | nein | Unicode-Emoji z.B. `"🛒"` |
| `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) |
**Scope-Verhalten:**
- Ohne `scope`: Liste wird im primären Kreis des Accounts erstellt
- Mit `scope=family/XXXX`: Liste wird im angegebenen Kreis erstellt
- Die `metaId` der neuen Liste kodiert den Kreis: `taskList/CIRCLENUM_LISTNUM`
- Parameter `familyId`, `circleId`, `family`, `id` → werden ignoriert, nur `scope` wirkt
**Response:**
```
a00.r.r → vollständiges Listen-Objekt
.metaId → neue Listen-ID (z.B. "taskList/23431854_29759623")
.name → Listen-Name
.taskListType → SHOPPING_LIST oder TODOS
.sharedToAll → "true" / "false"
.emoji → Unicode-Emoji (fehlt wenn nicht gesetzt)
.color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt)
.familyId → Kreis-metaId des erstellten Liste
.rights.canDelete → "true" (user-created lists)
```
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `taskupdatelist` Liste aktualisieren
POST https://api.familywall.com/api/taskupdatelist
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `metaId` | ja | Listen-metaId ⚠️ nicht `id`! |
| `name` | nein | Neuer Listen-Name |
| `color` | nein | Hex-Farbwert z.B. `"#E53935"` |
| `emoji` | nein | Unicode-Emoji z.B. `"🧪"` |
| `scope` | nein | Kreis-metaId für sekundäre Kreise; automatisch aus metaId abgeleitet |
**Hinweis:** Nur übergebene Felder werden geändert (Partial Update).
Felder die nicht mitgeschickt werden bleiben auf dem Server unverändert.
System-Listen (`rights.canUpdate` fehlt oder `!= "true"`) können nicht geändert werden.
MCP-Server prüft `rights.canUpdate` vor dem Update via `taskgettasklists`.
**Response:**
```
a00.r.r → vollständiges Listen-Objekt (analog taskcreatelist)
.metaId → Listen-ID
.name → (aktualisierter) Listen-Name
.taskListType → SHOPPING_LIST oder TODOS
.emoji → (aktualisiertes) Unicode-Emoji
.color → (aktualisierter) Hex-Farbwert
.familyId → Kreis-metaId
.rights.canUpdate → "true" für bearbeitbare Listen
.rights.canDelete → "true" für löschbare Listen
.lastAction → "UPDATED"
```
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `taskdeletelist` Liste löschen
POST https://api.familywall.com/api/taskdeletelist
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `id` | ja | Listen-metaId ⚠️ nicht `listId` oder `taskListId`! |
| `scope` | nein | Kreis-metaId (erforderlich für sekundäre Kreise) |
**Response:**
```
a00.r.r → "true" (String)
```
**Hinweis:** Löscht die Liste und alle enthaltenen Tasks unwiderruflich.
System-Listen (`rights.canDelete` fehlt oder `null`) sind nicht löschbar.
MCP-Server prüft dies vor dem Löschen via `taskgettasklists`.
Für Listen in sekundären Kreisen muss `scope=family/XXXX` mitgeschickt werden.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `taskgettasklists` Listen abrufen
POST https://api.familywall.com/api/taskgettasklists
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `scope` | nein | Kreis-metaId z.B. `"family/23447378"` (ohne scope → primärer Kreis) |
**Scope-Verhalten (Server):**
- Ohne `scope`: Nur Listen des primären Kreises werden zurückgegeben
- Mit `scope=family/XXXX`: Nur Listen des angegebenen Kreises
- Es gibt keinen servereitigen Filter für mehrere Kreise gleichzeitig
- Andere Parameter (`familyId`, `circleId`, etc.) werden ignoriert
**Scope-Verhalten (MCP-Tool get_lists, v0.8.2+):**
- Ohne `scope`: Der MCP-Server ruft für **jeden Kreis** separate `taskgettasklists(scope=<circle_id>)`-Calls ab und merged die Ergebnisse
- Mit `scope=family/XXXX` oder `scope="Kreis Name"`: Nur dieser Kreis (wie bisher)
**Response-Struktur:**
```
a00.r.r[] → Liste aller Task-Listen des Kreises
.metaId → Listen-ID (z.B. "taskList/23431854_29740942")
.name → Systembezeichnung oder Benutzer-Name
.taskListType → SHOPPING_LIST oder TODOS
.familyId → Kreis-metaId (z.B. "family/23431854")
.emoji → Unicode-Emoji oder "" (leerer String = kein Emoji)
.color → Hex-Farbwert z.B. "#E53935" (fehlt wenn nicht gesetzt)
.remainingTaskNumber → offene Tasks (String)
.totalTaskNumber → Gesamt-Tasks (String)
.sharedToAll → "true" / "false"
.sharedMemberIds[] → Member-accountIds
.rights.canDelete → "true" = löschbar, fehlt/leer = Systemliste
.systemId → vorhanden nur bei Systemlisten (z.B. "-10", "-11")
```
**metaId-Encoding:** `taskList/<family_num>_<list_num>` — die `family_num` kodiert den Kreis.
Beispiel: `taskList/23431854_29740942` gehört zu `family/23431854`.
**Hinweis emoji/color:**
- `emoji`: Systemlisten liefern `""`, user-created Listen liefern den Emoji-String
oder `""` wenn kein Emoji gesetzt. Normalisierung: `""``null` im MCP-Server.
- `color`: Fehlt komplett wenn nicht gesetzt (nicht `null` oder `""`).
Normalisierung: fehlendes Feld → `null` im MCP-Server.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
## Systembezeichnungen für Listen-Namen
| Systembezeichnung | Deutsch |
|---|---|
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
| `SYS-CAT-TODOLIST` | `Aufgaben` |
## Debug-Logging
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
vollständige Request-Bodies und Responses nach stderr.
**Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
**Wann FW_DEBUG=1 nutzen:**
- Neue Endpoints verifizieren
- Parameter-Namen unbekannt
- Silent-Fail debuggen
- Service Worker blockiert Browser-DevTools
## Service Worker
Die Family Wall Web-App registriert einen Service Worker der bestimmte
HTTP-Requests abfängt und modifiziert bevor sie das Netzwerk erreichen.
Betroffen sind u.a. Unlike-Calls und möglicherweise andere schreibende
Operationen.
**Folge:** Browser-DevTools Network-Tab und JS-Interceptoren (XHR/Fetch)
zeigen nicht den echten Request-Body für diese Calls.
**Lösung:** `FW_DEBUG=1` auf MCP-Server-Seite zeigt was tatsächlich gesendet wird.
### `mprecipeput` Rezept erstellen
POST https://api.familywall.com/api/mprecipeput
**Wichtig:** Alle Parameter haben das Präfix `recipe.` (verifiziert via FW_DEBUG=1).
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `recipe.name` | ja | Rezeptname |
| `recipe.isRecipe` | ja | immer `"true"` |
| `recipe.description` | nein | Kurzbeschreibung |
| `recipe.ingredients` | nein | Zutaten als Freitext (Zeilen mit `\n` trennen) |
| `recipe.instructions` | nein | Anleitung als Freitext (Zeilen mit `\n` trennen) |
| `recipe.prepTime` | nein | Zubereitungszeit in Minuten (String, z.B. `"30"`) |
| `recipe.cookTime` | nein | Kochzeit in Minuten (String, z.B. `"60"`) |
| `recipe.serves` | nein | Portionen (String, z.B. `"4"`) |
| `recipe.url` | nein | externe URL |
**Response:**
```
a00.r.r → vollständiges Rezept-Objekt
.metaId → neue Rezept-ID (z.B. "recipe/23431854_10968866")
.name → Rezeptname
.description → Beschreibung
.ingredients → Zutaten Freitext (API liefert \r\n als Zeilenumbrüche)
.ingredientsList[] → auto-geparste Zutaten (read-only, vom Server generiert)
.metaId → "recipeIngredient/<id>"
.name → Zutatname
.instructions → Anleitung Freitext
.prepTime → Zubereitungszeit als String ("30")
.cookTime → Kochzeit als String ("60")
.serves → Portionen als String ("4")
.url → externe URL (fehlt wenn leer)
.isRecipe → "true"
.isFavorite → "false"
.recipeCategories[] → []
.recipeCategoryIdList[] → []
.rights.canDelete → "true" für eigene Rezepte
.rights.canUpdate → "true" für eigene Rezepte
.familyId, .accountId, .creationDate, .moodMap, .moodStarShortcut
```
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `mprecipeput` Rezept aktualisieren (Update)
POST https://api.familywall.com/api/mprecipeput
Identisch zum Create-Aufruf, aber mit zusätzlichem `recipe.metaId`-Parameter.
Der Server unterscheidet Create vs. Update anhand ob `recipe.metaId` vorhanden ist.
Nur geänderte Felder müssen mitgeschickt werden (Partial Update).
`recipe.name` und `recipe.isRecipe="true"` sollten immer mitgeschickt werden.
**Zusätzlicher Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `recipe.metaId` | ja (für Update) | metaId des zu aktualisierenden Rezepts |
**Newline-Hinweis:** Zutaten und Anleitung müssen echte `\n`-Zeichen enthalten
(nicht die zwei-Zeichen-Sequenz `\n`). Literale Backslash-n werden vom Server
als ein Element interpretiert → kein Splitting in `ingredientsList`.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `metasync` (id='recipe') Alle Rezepte abrufen
POST https://api.familywall.com/api/metasync
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `id` | `"recipe"` (lowercase, enum-Wert) |
**Response-Struktur:**
```
a00.r.r.updatedCreated[] → Liste aller Rezepte der Familie
→ Felder identisch mit mprecipeput-Response (siehe oben)
```
**Hinweis:** Der Parameter `id` nimmt einen MetaIdTypeEnum-Wert, kein tatsächliches Objekt.
Nur `"recipe"` (lowercase) funktioniert `"RECIPE"`, `"Recipe"` und andere Schreibweisen
liefern `MetaIdTypeEnum`-Fehler.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `metadelete` Rezept löschen
POST https://api.familywall.com/api/metadelete
Identisch mit dem Task-Löschen-Endpoint. Funktioniert auch für Rezepte.
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `id` | Rezept-metaId (z.B. `"recipe/23431854_10968866"`) |
**Response:**
```
a00.r.r → "true" (String)
```
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `acccreatefamily` Kreis erstellen
POST https://api.familywall.com/api/acccreatefamily
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `name` | ja | Kreis-Name |
**Response:**
```
a00.r.r → numerische Kreis-ID als String (z.B. "23447369")
→ vollständige metaId: "family/" + id
```
**Hinweise:**
- Der Server kapitalisiert den ersten Buchstaben des Namens.
- Kreise können über die API nicht gelöscht werden (`metadelete` → "delete not supported").
- Vollständige Kreisdaten (incl. Name) via `famlistfamily` im gleichen Session-Call abrufbar.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `accinvite` Mitglied einladen
POST https://api.familywall.com/api/accinvite
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `familyId` | ja | Kreis-metaId (z.B. `"family/23447369"`) |
| `identifier` | ja | E-Mail-Adresse des Eingeladenen |
| `role` | ja | `"Unknown"` (einziger verifizierter Enum-Wert für `FamilyRoleTypeEnum`) |
| `firstname` | ja | Vorname des Eingeladenen |
**Einschränkung:** Der Endpoint funktioniert nur für Personen, die noch kein
Family Wall-Konto haben. Bei bestehenden Accounts antwortet der Server mit:
`"This api is now only used to create and invite an account with login only."`
**Fehler-Struktur** (HTTP 200, aber Fehler unter `a00.ex`):**
```json
{"a00": {"ex": {"ex": {"FiZClassId": "17", "message": "..."}}}}
```
`fw_client.py` prüft `a00.ex` und wirft `FamilyWallError`.
**Response (Erfolg):**
```
a00.r.r → Einladungsobjekt (Struktur unbekannt, da nicht testbar mit Test-Account)
```
**Bekannte Enum-Werte für `role`:**
- `"Unknown"` ✓ verifiziert
- `"Parent"`, `"Child"``FamilyRoleTypeEnum` decode error
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
### `accupdatefamily` Kreis umbenennen
POST https://api.familywall.com/api/accupdatefamily
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `name` | ja | Neuer Kreis-Name |
| `scope` | nein | Kreis-metaId z.B. `"family/23449644"` (ohne scope → primärer Kreis) |
**Response:**
```
a00.r.r → vollständiges Kreis-Objekt
.metaId → Kreis-metaId (z.B. "family/23449644")
.name → (aktualisierter) Kreis-Name (erster Buchstabe wird kapitalisiert)
.family_id → numerische Kreis-ID (ohne "family/"-Prefix)
.members[] → Mitglieder-Liste
```
**Hinweise:**
- Ohne `scope`: aktualisiert den PRIMARY Kreis des Accounts.
- Mit `scope=family/XXXX`: aktualisiert den angegebenen Kreis (primär oder sekundär).
- Der Server kapitalisiert immer den ersten Buchstaben des Namens.
- Nur der Name ist über die API änderbar (kein Bild, keine Farbe).
**Vorherige Dokumentation korrigiert:** Frühere SPEC-Einträge vermerkten,
dass `id`/`familyId` ignoriert werden. Mit `scope=` funktioniert die
Zielauswahl korrekt für beliebige Kreise.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1 (scope= auf sekundärem Kreis)
### `adminwipefamily` Kreis löschen
POST https://api.familywall.com/api/adminwipefamily
Löscht einen Kreis und alle zugehörigen Inhalte (Listen, Tasks, Rezepte, Wall-Posts).
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `scope` | ja | Kreis-metaId (z.B. `family/23447378`) |
**Response (Erfolg):**
```json
{ "a00": { "r": { "r": "true" } } }
```
**Hinweise:**
- Löscht unwiderruflich alle Inhalte des Kreises.
- Der primäre Kreis (`isFirstFamily="true"` in `famlistfamily`) kann NICHT gelöscht werden.
- MCP-Server prüft `isFirstFamily` vor dem Löschen via `famlistfamily` im gleichen Session-Call.
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1 (family/23447371 erfolgreich gelöscht)
## Meal Planner API
Premium-Feature. Endpoints extrahiert aus JS-Bundle `startupmodule.js?vfw=2026-04-03T140539`.
Browser-Analyse blockiert durch Service Worker. Response-Struktur wird nach erstem Deploy
mit `FW_DEBUG=1` verifiziert.
### `mplistinterval` Essensplan für Datumsbereich abrufen
POST https://api.familywall.com/api/mplistinterval
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `from` | ja | Start-Datum ISO 8601 (z.B. `"2026-04-13"`) |
| `to` | ja | End-Datum ISO 8601 (z.B. `"2026-04-19"`) |
**Response-Struktur:**
```
a00.r.r
.from → angefragtes Start-Datum (z.B. "2026-04-13")
.to → angefragtes End-Datum (z.B. "2026-04-19")
.list[] → geplante Mahlzeiten (dish-Objekte)
.metaId → "dish/<family_num>_<dish_num>"
.date → Datum der Mahlzeit (z.B. "2026-04-17")
.type → Mahlzeiten-Typ (s.u.)
.name → Anzeigename (z.B. "Biga Pizzateig")
.recipeId → verknüpfte Rezept-metaId oder fehlt wenn freier Text
.familyId → Kreis-metaId
.accountId → Ersteller-accountId
.sortingIndex → Sortierung (numerischer Timestamp als String)
.rights.canUpdate → "true" wenn bearbeitbar
.rights.canDelete → "true" wenn löschbar
.recipeList[] → Rezept-Objekte der verknüpften Gerichte
.metaId → Rezept-ID (z.B. "recipe/16282169_7932720")
.isRecipe → "true" = echtes Rezept aus der Rezeptbox (hat Zutaten etc.)
"false" = Freitext-Stub (nur Name, keine Zutaten)
.mealList[] → Freitext-Notizen (meal-Objekte)
.metaId → "meal/<family_num>_<meal_num>"
.date → Datum der Mahlzeit (z.B. "2026-04-17")
.type → Mahlzeiten-Typ (BREAKFAST/LUNCH/SNACK/DINNER)
.note → Freitext-Notiz (z.B. "Test")
.serves → Portionen als String (z.B. "1")
.rights.canUpdate → "true" wenn bearbeitbar
.rights.canDelete → "true" wenn löschbar
```
**Mahlzeiten-Typen:**
- `BREAKFAST` Frühstück
- `LUNCH` Mittagessen
- `SNACK` Snack
- `DINNER` Abendessen
**Hinweis:** `recipeList` wird vom MCP-Tool nicht zurückgegeben.
Rezept-Details bei Bedarf separat via `get_recipe` abrufen.
`list[]` (dish) und `mealList[]` (meal) werden gemergt und nach Datum + Typ sortiert zurückgegeben.
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
### Weitere Meal Planner Endpoints (nicht implementiert)
| Endpoint | Parameter | Bedeutung |
|---|---|---|
| `mpcreate` | `name`, `date`, `type`, `clientOpId` | Mahlzeit mit freiem Text erstellen |
| `mpcreateByRecipeId` | `recipeId`, `date`, `type`, `clientOpId` | Mahlzeit aus Rezept erstellen |
| `mpmealput` | Mahlzeiten-Objekt (encoded) | Mahlzeit aktualisieren |
| `mpmove` | `metaId`, `date`, `type`, `clientOpId` | Mahlzeit zu anderem Datum/Typ verschieben |
| `mpdelete` | `metaId` | Mahlzeit löschen |
| `mpsettings` | | Einstellungen lesen |
**Quelle:** JS-Bundle-Analyse (startupmodule.js).
**Status:** Endpunkte bekannt, Response-Struktur unbekannt.
### Recipe Categories API Details
Recipe categories are managed via `mprecipeput` endpoint. Each recipe can have zero or more
categories assigned via `recipe.recipeCategoryIdList` (sent multiple times for each category).
**Response structure** (in `mprecipeput` response):
```
a00.r.r
.recipeCategoryIdList[] → List of assigned category IDs (e.g. ["category/23431854_2"])
.recipeCategories[] → List of system names (e.g. ["KIDS_LOVE"])
```
**Category IDs** have format `category/<family_id>_<index>`. Categories are family-wide.
Standard categories (_2 to _6) are always available and derived from the family's metaId.
No API endpoint needed IDs are constructed as `category/<familyId>_<N>`.
Premium accounts may have additional categories beyond the 5 available in free tier.
### `metamood` Rezept-Reaktion (Stern setzen)
POST https://api.familywall.com/api/metamood
**Body-Parameter:**
| Parameter | Pflicht | Wert |
|---|---|---|
| `id` | ja | Rezept-metaId ⚠️ nicht `metaId`! |
| `moodType` | nein | `"STAR"`, `"LIKE"`, `"BEST_MOMENT"`, ... (Wert wird angenommen aber ignoriert) |
**Response:**
```
a00.r.r → Rezept-Objekt (FiZClassId: "8004")
.metaId → Rezept-ID
.isFavorite → aktueller Wert (unverändert)
.moodStarShortcut → aktueller Wert (unverändert)
.moodMap → aktueller Wert (unverändert)
```
**Bekannte Einschränkungen:**
- Eigene Rezepte können nicht reagiert werden (Self-Reaction-Restriction, analog zu `wallmood`)
- Der Endpoint akzeptiert Requests (HTTP 200, kein Fehler), verändert aber bei eigenen Rezepten keinen State
- `FiZClassId: "8004"` im Response deutet auf "verarbeitet aber nicht geändert" hin
- Möglicherweise funktioniert der Endpoint für Rezepte anderer Familienmitglieder (nicht testbar mit Einzel-Account)
**Entdeckt:** 2026-04-17 via FW_DEBUG=1 Probing
### `mpstar` Rezept als Favorit markieren (Service Worker blockiert)
POST https://api.familywall.com/api/mpstar
**Status:** Nicht direkt zugänglich. Der Server antwortet mit `"The call mpstar is not registered"`.
Der Browser-Aufruf `data-fw-onclick="mpstar"` wird vom Service Worker abgefangen und transformiert,
bevor er den Server erreicht. Die Transformation ist ohne Service-Worker-Analyse nicht bekannt.
**Hypothesen zum echten Endpoint:**
- Könnte eine transformierte Form von `metamood` sein (mit Verschlüsselung/Token)
- Könnte ein separater "Lesezeichen"-Endpoint sein der `isFavorite` setzt (vs. `moodMap`-Stern)
- Könnte Premium-only sein
**Zum Entsperren nötig:**
- Service-Worker-Quellcode analysieren (DevTools Application > Service Workers > Source)
- ODER: `FW_DEBUG=1` auf einem echten Multi-User-Account mit Rezepten anderer Mitglieder testen
**Entdeckt:** 2026-04-17 via FW_DEBUG=1 Probing (ausgiebige Endpoint-Suche)
## Offene Punkte
- Unlike-Endpoint (Service Worker blockiert Analyse)
- `mpstar` / `isFavorite` für Rezepte (Service Worker blockiert Analyse, siehe oben)
- Erinnerungen (reminder) nur Premium-Account
- Wiederholungen (repeat) nur Premium-Account
- Rezept-Kategorien-Listing-Endpoint (derzeit keine API, müssen aus Rezepten extrahiert werden)
- mpadditemtolist (nicht nötig Family Wall kann das nativ über die Web-App)
- Einladung bestehender FamilyWall-Nutzer (accinvite nur für neue Accounts)