diff --git a/CLAUDE.md b/CLAUDE.md index 7c58cfd..de0214e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,7 +24,7 @@ und wird in Claude Desktop eingebunden. ## Aktueller Stand -### Implementierte Tools (v0.7.1) +### Implementierte Tools (v0.7.2) | Kategorie | Tools | |---|---| @@ -34,7 +34,7 @@ und wird in Claude Desktop eingebunden. | 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` | +| Kreise | `create_circle`, `delete_circle`, `add_member_to_circle` | ## Roadmap @@ -46,8 +46,9 @@ und wird in Claude Desktop eingebunden. - 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 ✓ -- v0.7.1: get_lists scope fix + create_list circle_id + delete_list scope ✓ ← aktuell -- v0.7.2: mpadditemtolist (Zutaten → Einkaufsliste) +- v0.7.1: get_lists scope fix + create_list circle_id + delete_list scope ✓ +- v0.7.2: delete_circle ✓ ← aktuell +- v0.7.3: 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) @@ -147,6 +148,7 @@ Fehler bei falschen Parametern kommen nicht immer auf Top-Level: | `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`) | +| `adminwipefamily` | `scope` | Kreis-metaId; löscht Kreis + alle Inhalte; `a00.r.r="true"` bei Erfolg | ### Self-Like-Restriction Eigene Posts können nicht geliked werden. API antwortet 200, macht aber nichts. diff --git a/README.md b/README.md index 3469327..1b716cd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, tasks, and recipes directly from Claude. -## Features (v0.7.1) +## Features (v0.7.2) ### Read @@ -29,7 +29,8 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your - `create_recipe` -- create a new recipe (name, description, ingredients, instructions, prep/cook time, serves, url); use `\n` to separate ingredient lines - `update_recipe` -- update any field of an existing recipe (partial update — omitted fields unchanged) - `delete_recipe` -- permanently delete a recipe (only own recipes) -- `create_circle` -- create a new Family Wall circle (group); note: circles cannot be deleted via API +- `create_circle` -- create a new Family Wall circle (group) +- `delete_circle` -- permanently delete a circle and all its content (primary circle is protected) - `add_member_to_circle` -- invite a person to a circle by e-mail (for new Family Wall users only; existing accounts require in-app invitation) ## Requirements diff --git a/SPEC.md b/SPEC.md index 2068b2d..166c242 100644 --- a/SPEC.md +++ b/SPEC.md @@ -583,6 +583,31 @@ POST https://api.familywall.com/api/accupdatefamily **Verifiziert am:** 2026-04-16 via FW_DEBUG=1 +### `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) + ## Offene Punkte - Unlike-Endpoint (Service Worker blockiert Analyse) @@ -591,4 +616,3 @@ POST https://api.familywall.com/api/accupdatefamily - Sortierung von Kategorien via API - mpadditemtolist (Zutaten aus Rezept → Einkaufsliste) - Einladung bestehender FamilyWall-Nutzer (accinvite nur für neue Accounts) -- Kreis-Delete-Endpoint (API: "delete not supported" für family-Objekte) diff --git a/pyproject.toml b/pyproject.toml index 1bd33d6..40bdc25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "0.7.1" +version = "0.7.2" description = "MCP server for Family Wall — read your family's lists and tasks via Claude" readme = "README.md" requires-python = ">=3.12" diff --git a/src/mcp_familywall/__init__.py b/src/mcp_familywall/__init__.py index a5f830a..bc8c296 100644 --- a/src/mcp_familywall/__init__.py +++ b/src/mcp_familywall/__init__.py @@ -1 +1 @@ -__version__ = "0.7.1" +__version__ = "0.7.2" diff --git a/src/mcp_familywall/server.py b/src/mcp_familywall/server.py index 548072c..924d4f4 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -1289,6 +1289,95 @@ def add_member_to_circle( ) +# --------------------------------------------------------------------------- +# Tool: delete_circle +# --------------------------------------------------------------------------- + + +@mcp.tool() +def delete_circle(circle_id: str) -> str: + """Permanently delete a Family Wall circle (group) and all its content. + + IMPORTANT: Ask the user for confirmation before calling this tool. + + This action cannot be undone. All lists, tasks, recipes, and wall posts + inside the circle are deleted along with it. The primary (first) circle + is protected and cannot be deleted. + + Args: + circle_id: Circle metaId from ``get_circles`` + (e.g. ``"family/23447378"``). Must not be the primary circle. + + Returns: + JSON success indicator or an error message. + """ + try: + email, password = get_credentials() + except RuntimeError as exc: + return f"Error: {exc}" + + circle_name: str = circle_id + try: + with FamilyWallClient() as client: + client.login(email, password) + + # Fetch the circle list to verify the target exists and is not primary. + circles_data = client.call("famlistfamily") + try: + raw_circles: list[dict[str, Any]] = circles_data["a00"]["r"]["r"] + if not isinstance(raw_circles, list): + raise TypeError("a00.r.r is not a list") + except (KeyError, TypeError): + client.logout() + return "Error: Unexpected famlistfamily response structure." + + target = next((c for c in raw_circles if c.get("metaId") == circle_id), None) + if target is None: + client.logout() + available = [c.get("metaId") for c in raw_circles] + return json.dumps( + { + "error": f"Circle not found: {circle_id!r}", + "available_circles": available, + }, + ensure_ascii=False, + indent=2, + ) + + if target.get("isFirstFamily") == "true": + client.logout() + return json.dumps( + { + "error": "Cannot delete the primary circle.", + "id": circle_id, + "name": target.get("name"), + "hint": ( + "The primary (first) circle cannot be deleted via the API. " + "Use the Family Wall app settings to manage it." + ), + }, + ensure_ascii=False, + indent=2, + ) + + circle_name = target.get("name", circle_id) + + # Verified — delete in the same session. + # adminwipefamily uses scope= and returns a00.r.r="true". + client.call("adminwipefamily", {"scope": circle_id}) + client.logout() + except FamilyWallError as exc: + return f"Error: Family Wall API error: {exc}" + except Exception as exc: + return f"Error: Connection error: {exc}" + + return json.dumps( + {"deleted": True, "id": circle_id, "name": circle_name}, + ensure_ascii=False, + indent=2, + ) + + # --------------------------------------------------------------------------- # Tool: like_post # ---------------------------------------------------------------------------