feat(circles): add update_circle tool (v0.7.4)
Implements accupdatefamily endpoint (verified via FW_DEBUG=1): - Parameter 'scope' targets any circle (primary or secondary) - Without scope: renames the primary circle (API default) - Server always capitalises the first letter of the new name - Verifies circle existence via famlistfamily in same session - Response a00.r.r = full circle object with updated name Also corrects SPEC.md: accupdatefamily with scope= works for any circle, not just the primary (previous note was incomplete). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ und wird in Claude Desktop eingebunden.
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
### Implementierte Tools (v0.7.3)
|
||||
### Implementierte Tools (v0.7.4)
|
||||
|
||||
| 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`, `delete_circle`, `add_member_to_circle` |
|
||||
| Kreise | `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` |
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -48,8 +48,9 @@ und wird in Claude Desktop eingebunden.
|
||||
- v0.7.0: create_circle + add_member_to_circle ✓
|
||||
- v0.7.1: get_lists scope fix + create_list circle_id + delete_list scope ✓
|
||||
- v0.7.2: delete_circle ✓
|
||||
- v0.7.3: update_list (Umbenennen, emoji/color ändern) ✓ ← aktuell
|
||||
- v0.7.4: mpadditemtolist (Zutaten → Einkaufsliste)
|
||||
- v0.7.3: update_list (Umbenennen, emoji/color ändern) ✓
|
||||
- v0.7.4: update_circle (Kreis umbenennen) ✓ ← aktuell
|
||||
- v0.7.5: mpadditemtolist (Zutaten → Einkaufsliste)
|
||||
- v0.8.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
||||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||||
|
||||
@@ -148,7 +149,7 @@ Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||||
| `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`) |
|
||||
| `accupdatefamily` | `name`, `scope` | `scope`: Kreis-metaId; ohne scope → PRIMARY Kreis; erster Buchstabe wird kapitalisiert |
|
||||
| `adminwipefamily` | `scope` | Kreis-metaId; löscht Kreis + alle Inhalte; `a00.r.r="true"` bei Erfolg |
|
||||
|
||||
### Self-Like-Restriction
|
||||
|
||||
@@ -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.3)
|
||||
## Features (v0.7.4)
|
||||
|
||||
### Read
|
||||
|
||||
@@ -31,6 +31,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your
|
||||
- `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)
|
||||
- `update_circle` -- rename a circle (server capitalises first letter; only name is changeable via API)
|
||||
- `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)
|
||||
|
||||
|
||||
@@ -611,10 +611,28 @@ POST https://api.familywall.com/api/accupdatefamily
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `name` | ja | Neuer Kreis-Name |
|
||||
| `scope` | nein | Kreis-metaId z.B. `"family/23449644"` (ohne scope → primärer Kreis) |
|
||||
|
||||
**Hinweis:** Aktualisiert immer den PRIMARY Kreis des Accounts (ignoriert `id`/`familyId` Parameter).
|
||||
**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
|
||||
```
|
||||
|
||||
**Verifiziert am:** 2026-04-16 via FW_DEBUG=1
|
||||
**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
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mcp-familywall"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
description = "MCP server for Family Wall — read your family's lists and tasks via Claude"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.7.3"
|
||||
__version__ = "0.7.4"
|
||||
|
||||
@@ -1342,6 +1342,96 @@ def create_circle(name: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: update_circle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def update_circle(circle_id: str, name: str) -> str:
|
||||
"""Rename a Family Wall circle.
|
||||
|
||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||
|
||||
Only the circle name can be changed via the API. Note that the server
|
||||
always capitalises the first letter of the new name.
|
||||
|
||||
Args:
|
||||
circle_id: Circle metaId from ``get_circles``
|
||||
(e.g. ``"family/23431854"``).
|
||||
name: New display name for the circle.
|
||||
|
||||
Returns:
|
||||
JSON with ``{updated, id, name}`` on success, or an error message.
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
return "Error: 'name' must not be empty."
|
||||
|
||||
try:
|
||||
email, password = get_credentials()
|
||||
except RuntimeError as exc:
|
||||
return f"Error: {exc}"
|
||||
|
||||
try:
|
||||
with FamilyWallClient() as client:
|
||||
client.login(email, password)
|
||||
|
||||
# Verify the circle exists before attempting the update.
|
||||
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,
|
||||
)
|
||||
|
||||
# Rename the circle.
|
||||
# Verified: accupdatefamily with scope=<circle_metaId> targets any circle,
|
||||
# both primary and secondary. The server capitalises the first letter.
|
||||
resp = client.call("accupdatefamily", {"scope": circle_id, "name": name})
|
||||
client.logout()
|
||||
except FamilyWallError as exc:
|
||||
return f"Error: Family Wall API error: {exc}"
|
||||
except Exception as exc:
|
||||
return f"Error: Connection error: {exc}"
|
||||
|
||||
# Response: a00.r.r = full circle object
|
||||
try:
|
||||
circle_obj: dict[str, Any] = resp["a00"]["r"]["r"]
|
||||
if not isinstance(circle_obj, dict) or "metaId" not in circle_obj:
|
||||
raise TypeError("unexpected shape")
|
||||
except (KeyError, TypeError):
|
||||
return json.dumps(
|
||||
{"warning": "Unexpected accupdatefamily response structure", "raw": resp},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
"updated": True,
|
||||
"id": circle_obj.get("metaId"),
|
||||
"name": circle_obj.get("name"),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: add_member_to_circle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user