2bc03e2165
- acccreatefamily endpoint creates a new circle (returns numeric ID) - accinvite endpoint invites new users by email (familyId, identifier, role, firstname) - fw_client now detects a00.ex errors (was only checking a00.un before) - New modules/circles.py with FamilyRoleTypeEnum constants - SPEC.md updated with acccreatefamily, accinvite, accupdatefamily docs - Note: circle deletion not supported by FW API (metadelete → "delete not supported") - Note: accinvite only works for new (non-existing) FW accounts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
169 lines
7.5 KiB
Markdown
169 lines
7.5 KiB
Markdown
# mcp-familywall
|
||
|
||
## Kontext
|
||
|
||
Dieses Projekt entwickelt `mcp-familywall` – einen MCP-Server für den
|
||
Zugriff auf Family Wall (familywall.com). Der MCP-Server läuft lokal
|
||
und wird in Claude Desktop eingebunden.
|
||
|
||
|
||
## Infrastruktur
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Family Wall API** | `https://api.familywall.com/api` |
|
||
| **Gitea** | `https://gitea.gecheckt.de/marcus/mcp-familywall` |
|
||
| **Lokaler Code** | `D:\Dev\Projects\mcp-familywall` |
|
||
| **Sprache** | Python 3.12+, `uv`, MCP SDK, `httpx`, `keyring`, `click`, `rich` |
|
||
|
||
|
||
## Deploy-Workflow (nach jeder Code-Änderung)
|
||
|
||
1. Claude Code committet und pusht (bei Berechtigungsfehler: bis zu 2 Retries, je 1s warten)
|
||
2. Marcus installiert lokal und startet Claude Desktop neu
|
||
|
||
## Aktueller Stand
|
||
|
||
### Implementierte Tools (v0.7.0)
|
||
|
||
| Kategorie | Tools |
|
||
|---|---|
|
||
| Lesen | `get_circles`, `get_members`, `get_lists`, `get_tasks`, `get_categories`, `get_activities` |
|
||
| Tasks | `create_task`, `update_task`, `toggle_task`, `delete_task` |
|
||
| Listen | `create_list`, `delete_list` |
|
||
| Kategorien | `create_category`, `delete_category` |
|
||
| Aktivitäten | `like_post` |
|
||
| Rezepte | `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe` |
|
||
| Kreise | `create_circle`, `add_member_to_circle` |
|
||
|
||
## Roadmap
|
||
|
||
- v0.4.x: Kategorie-Management, Task-Felder (due_date, assignee, list_id) ✓
|
||
- v0.5.x: Listen-Management (create_list, delete_list) ✓
|
||
- v0.5.1: emoji + color in get_lists / create_list ✓
|
||
- v0.5.2: Mengenkonvention im create_task Docstring ✓
|
||
- v0.5.3: Kategorie-Auto-Assign-Hinweis im create_task Docstring ✓ (nachgeliefert in v0.6.1)
|
||
- v0.6.0: Rezept-Box (get_recipes, get_recipe, create_recipe, delete_recipe) ✓
|
||
- v0.6.1: update_recipe + Bugfix Zeilenumbrüche in create_recipe ✓
|
||
- v0.7.0: create_circle + add_member_to_circle ✓ ← aktuell
|
||
- v0.7.1: mpadditemtolist (Zutaten → Einkaufsliste)
|
||
- v0.5.3: update_list (Umbenennen, emoji/color ändern), Sharing-Verwaltung
|
||
- v0.8.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||
|
||
|
||
## Architektur-Entscheidungen
|
||
|
||
### Session-Strategie
|
||
Kein Session-Caching. Jeder Tool-Call führt Login → API-Call → Logout durch.
|
||
Optimierung: Mehrere API-Calls in einer Session bündeln (login → call1 → call2 → logout)
|
||
um unnötige HTTP-Roundtrips zu vermeiden. Credentials liegen im OS Keyring
|
||
(nur `email` + `password`), kein `session_id`.
|
||
|
||
### Kreise (Scopes)
|
||
Family Wall kennt mehrere Kreise (z.B. Familie, erweiterter Familienkreis).
|
||
`get_lists` unterstützt optionalen `scope`-Parameter zur Filterung.
|
||
Ohne `scope` werden alle Kreise zurückgegeben.
|
||
|
||
### Listen-Namen
|
||
Systembezeichnungen (z.B. `SYS-CAT-SHOPPINGLIST`) werden in deutsche
|
||
Klarnamen übersetzt. Mapping-Tabelle in `modules/lists.py`.
|
||
|
||
### Kategorien
|
||
- Kategorien sind family-wide, nicht list-spezifisch (`taskcategoryput` gilt für alle Listen)
|
||
- System-Kategorien: `rights.canDelete=null` → nicht löschbar
|
||
- Custom-Kategorien: `rights.canDelete="true"` → löschbar
|
||
- Locale-Filter: default `"de"` – Custom-Kategorien haben kein locale-Feld
|
||
und werden immer angezeigt unabhängig vom Locale-Parameter
|
||
|
||
### Fehlerbehandlung
|
||
Die API gibt Fehler manchmal als `a00.un.un` zurück (nicht Top-Level).
|
||
`fw_client.py` prüft beides und wirft `FamilyWallError`. Nie silent-fail
|
||
als Erfolg werten – immer `a00.un` und Top-Level `ex`/`un` prüfen.
|
||
|
||
### Service Worker
|
||
Die Family Wall Web-App nutzt einen Service Worker der bestimmte
|
||
HTTP-Requests abfängt und modifiziert. Browser-DevTools und JS-Interceptoren
|
||
können den echten Request-Body in diesen Fällen nicht sehen.
|
||
→ Immer `FW_DEBUG=1` für Traffic-Analyse nutzen, nicht Browser-DevTools.
|
||
|
||
|
||
## Claude Code – Implementierungsregeln
|
||
|
||
- **Feature complete before next feature** – jedes Feature vollständig
|
||
implementieren, testen und verifizieren bevor das nächste beginnt
|
||
- **Kein destruktives Probing** – keine Probe-Calls auf System-Kategorien,
|
||
echte Listen oder echte Tasks; immer Test-Objekte anlegen und danach
|
||
sofort löschen
|
||
- Fehlerbehandlung: API-Fehler als verständliche Meldung zurückgeben, keine Stacktraces
|
||
- Keine Secrets in stderr-Ausgaben (Passwort bei Debug-Logging maskieren)
|
||
- Type Hints und Docstrings konsequent verwenden (Englisch)
|
||
- Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest`
|
||
- Alle Texte (Docstrings, Kommentare, README): Englisch
|
||
- Debug-Logging via `FW_DEBUG=1` Umgebungsvariable
|
||
- Nach jeder Aufgabe: git commit + push. Bei Berechtigungsfehler: 1s warten, bis zu 2 Retries
|
||
- .gitignore eigenständig pflegen (Credentials, __pycache__, .venv, .env, *.pyc etc.)
|
||
- README.md + CLAUDE.md im Projekt-Root bei jedem Aufruf aktualisieren
|
||
- Confirmation-Pattern in Docstrings: `IMPORTANT: Ask the user for confirmation
|
||
before calling this tool.` für destruktive oder schreibende Operationen
|
||
|
||
|
||
## Bekannte API-Eigenheiten und Fallstricke
|
||
|
||
### Sentinel-Wert `$empty`
|
||
Das FiZ-Framework nutzt `$empty` als Sentinel um optionale Felder zu löschen.
|
||
Normale Werte wie `""`, `null`, `"null"`, `"0"` werden vom Server abgelehnt.
|
||
Aktuell genutzt für: `dueDate=$empty` (Fälligkeitsdatum entfernen)
|
||
|
||
### Silent-Fail via `a00.un.un`
|
||
Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||
```json
|
||
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
|
||
```
|
||
→ `fw_client` muss beide Ebenen prüfen
|
||
|
||
### Bekannte Parameter-Namen (verifiziert)
|
||
| Endpoint | Parameter | Wert/Format |
|
||
|---|---|---|
|
||
| `taskcreate2` | `taskListId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee` | – |
|
||
| `taskupdate2` | `metaId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee`, `taskListId` | – |
|
||
| `taskupdate2` | `dueDate` löschen | `$empty` |
|
||
| `taskmark` | `taskId`, `complete` | `"true"`/`"false"` |
|
||
| `metadelete` | `id` | metaId des Tasks / Rezepts |
|
||
| `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like |
|
||
| `taskcategoryput` | `name`, `emoji` | – |
|
||
| `taskcategorydelete` | `id` | metaId der Kategorie |
|
||
| `taskcreatelist` | `name`, `taskListType`, `sharedToAll`, `color`, `emoji` | `taskListType`: `"SHOPPING_LIST"`/`"TODOS"` |
|
||
| `taskdeletelist` | `id` | metaId der Liste |
|
||
| `mprecipeput` | `recipe.name`, `recipe.isRecipe="true"`, `recipe.description`, `recipe.ingredients`, `recipe.instructions`, `recipe.prepTime`, `recipe.cookTime`, `recipe.serves`, `recipe.url` | Alle mit `recipe.`-Prefix! |
|
||
| `mprecipeput` (Update) | zusätzlich `recipe.metaId` | Vorhandene ID → Update statt Create |
|
||
| `metasync` (Rezepte lesen) | `id="recipe"` | liefert `a00.r.r.updatedCreated[]` |
|
||
| `acccreatefamily` | `name` | liefert numerische Kreis-ID als String in `a00.r.r` |
|
||
| `accinvite` | `familyId`, `identifier`, `role="Unknown"`, `firstname` | nur für neue FW-Accounts |
|
||
| `accupdatefamily` | `name` | aktualisiert immer den PRIMARY Kreis (ignoriert `id`/`familyId`) |
|
||
|
||
### Self-Like-Restriction
|
||
Eigene Posts können nicht geliked werden. API antwortet 200, macht aber nichts.
|
||
|
||
### Unlike nicht möglich
|
||
Service Worker verschlüsselt den Unlike-Request-Body.
|
||
Endpoint unbekannt. `like_post(like=False)` gibt Fehlermeldung zurück.
|
||
|
||
|
||
## Test-Credentials (nur für Entwicklung)
|
||
|
||
| | |
|
||
|---|---|
|
||
| E-Mail | `marcus@gecheckt.de` |
|
||
| Passwort | `Lasdas1234` |
|
||
|
||
Hinweis: Das ist ein kostenloser Test-Account ohne Premium-Features.
|
||
Der echte Account (Premium) hat andere Credentials die im Keyring gespeichert sind.
|
||
|
||
|
||
## Hintergrund
|
||
|
||
Marcus ist Senior Software Engineer (Java, Jakarta EE).
|
||
Präferenz: State-of-the-Art, Best Practices, saubere Architektur.
|
||
Automatisierung spart Zeit für die Familie. 🌱
|