feat(lists): add update_list tool (v0.7.3)
Implements taskupdatelist endpoint (verified via FW_DEBUG=1): - Parameter 'metaId' (not 'id') identifies the list - Partial update: only provided fields (name/color/emoji) are changed - Reads rights.canUpdate before calling the endpoint (single session) - System lists (canUpdate != 'true') are rejected with a clear error - Scope derived from list metaId for secondary-circle support Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,13 +24,13 @@ und wird in Claude Desktop eingebunden.
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
### Implementierte Tools (v0.7.2)
|
||||
### Implementierte Tools (v0.7.3)
|
||||
|
||||
| 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` |
|
||||
| Listen | `create_list`, `update_list`, `delete_list` |
|
||||
| Kategorien | `create_category`, `delete_category` |
|
||||
| Aktivitäten | `like_post` |
|
||||
| Rezepte | `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe` |
|
||||
@@ -47,9 +47,9 @@ und wird in Claude Desktop eingebunden.
|
||||
- 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 ✓
|
||||
- v0.7.2: delete_circle ✓ ← aktuell
|
||||
- v0.7.3: mpadditemtolist (Zutaten → Einkaufsliste)
|
||||
- v0.5.3: update_list (Umbenennen, emoji/color ändern), Sharing-Verwaltung
|
||||
- v0.7.2: delete_circle ✓
|
||||
- v0.7.3: update_list (Umbenennen, emoji/color ändern) ✓ ← aktuell
|
||||
- v0.7.4: mpadditemtolist (Zutaten → Einkaufsliste)
|
||||
- v0.8.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
||||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||||
|
||||
@@ -141,6 +141,7 @@ Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||||
| `taskcategorydelete` | `id` | metaId der Kategorie |
|
||||
| `taskcreatelist` | `name`, `taskListType`, `sharedToAll`, `color`, `emoji`, `scope` | `scope`: Kreis-metaId für nicht-primäre Kreise |
|
||||
| `taskgettasklists` | `scope` | Kreis-metaId; ohne scope → primärer Kreis |
|
||||
| `taskupdatelist` | `metaId`, `name`, `color`, `emoji` | `metaId` ⚠️ nicht `id`!; Partial Update |
|
||||
| `taskdeletelist` | `id`, `scope` | `scope`: Kreis-metaId für sekundäre Kreise |
|
||||
| `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 |
|
||||
|
||||
@@ -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.2)
|
||||
## Features (v0.7.3)
|
||||
|
||||
### Read
|
||||
|
||||
@@ -22,6 +22,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your
|
||||
- `toggle_task` -- mark a task complete or reopen it
|
||||
- `delete_task` -- permanently delete a task
|
||||
- `create_list` -- create a new task list (SHOPPING_LIST or TODOS; optional `emoji`, `color`, and `circle_id` to target a specific circle)
|
||||
- `update_list` -- rename a list or change its emoji/color (partial update — omitted fields unchanged; system lists are protected)
|
||||
- `delete_list` -- permanently delete a list and all its tasks (system lists are protected)
|
||||
- `create_category` -- create a custom category for a shopping list (with optional icon)
|
||||
- `delete_category` -- delete a custom category (system categories are protected)
|
||||
|
||||
@@ -315,6 +315,39 @@ a00.r.r → vollständiges Listen-Objekt
|
||||
|
||||
**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. `"🧪"` |
|
||||
|
||||
**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
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mcp-familywall"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
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.2"
|
||||
__version__ = "0.7.3"
|
||||
|
||||
@@ -1140,6 +1140,132 @@ def delete_list(list_id: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: update_list
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def update_list(
|
||||
list_id: str,
|
||||
name: str | None = None,
|
||||
color: str | None = None,
|
||||
emoji: str | None = None,
|
||||
) -> str:
|
||||
"""Update a task list's name, color, or emoji.
|
||||
|
||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||
|
||||
Performs a partial update — only the fields you provide are changed.
|
||||
The current values for any omitted fields are preserved on the server
|
||||
(the server keeps them; no need to fetch and re-send them).
|
||||
|
||||
Only user-created lists with ``rights.canUpdate="true"`` can be updated.
|
||||
System lists are protected and this tool will refuse to update them.
|
||||
|
||||
Args:
|
||||
list_id: List metaId from get_lists
|
||||
(e.g. ``"taskList/23431854_29759623"``).
|
||||
name: New display name (omit to keep existing).
|
||||
color: New background colour as a hex string (e.g. ``"#E53935"``).
|
||||
Omit to keep existing.
|
||||
emoji: New Unicode emoji icon (e.g. ``"🧪"``).
|
||||
Omit to keep existing.
|
||||
|
||||
Returns:
|
||||
JSON with the updated list object on success, or an error message.
|
||||
"""
|
||||
if name is None and color is None and emoji is None:
|
||||
return "Error: At least one of 'name', 'color', or 'emoji' must be provided."
|
||||
|
||||
try:
|
||||
email, password = get_credentials()
|
||||
except RuntimeError as exc:
|
||||
return f"Error: {exc}"
|
||||
|
||||
# Derive the owning circle from the list metaId (same as delete_list).
|
||||
circle_scope = _circle_id_from_list_id(list_id)
|
||||
|
||||
list_obj: dict[str, Any] | None = None
|
||||
try:
|
||||
with FamilyWallClient() as client:
|
||||
client.login(email, password)
|
||||
|
||||
# Fetch list to verify rights.canUpdate before updating.
|
||||
get_params: dict[str, Any] = {}
|
||||
if circle_scope:
|
||||
get_params["scope"] = circle_scope
|
||||
raw = client.call("taskgettasklists", get_params)
|
||||
try:
|
||||
raw_lists: list[dict[str, Any]] = raw["a00"]["r"]["r"]
|
||||
if not isinstance(raw_lists, list):
|
||||
raw_lists = []
|
||||
except (KeyError, TypeError):
|
||||
raw_lists = []
|
||||
|
||||
list_obj = next((lst for lst in raw_lists if lst.get("metaId") == list_id), None)
|
||||
if list_obj is None:
|
||||
client.logout()
|
||||
return f"Error: List '{list_id}' not found."
|
||||
|
||||
can_update: str | None = (list_obj.get("rights") or {}).get("canUpdate")
|
||||
if can_update != "true":
|
||||
client.logout()
|
||||
return json.dumps(
|
||||
{
|
||||
"error": "System lists cannot be updated.",
|
||||
"id": list_id,
|
||||
"name": list_obj.get("name"),
|
||||
"hint": "Only user-created lists (rights.canUpdate=true) can be updated.",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
# Build update params — only include provided fields.
|
||||
# Verified: taskupdatelist uses 'metaId' (not 'id') as the list identifier.
|
||||
upd_params: dict[str, Any] = {"metaId": list_id}
|
||||
if name is not None:
|
||||
upd_params["name"] = name
|
||||
if color is not None:
|
||||
upd_params["color"] = color
|
||||
if emoji is not None:
|
||||
upd_params["emoji"] = emoji
|
||||
|
||||
resp = client.call("taskupdatelist", upd_params)
|
||||
client.logout()
|
||||
except FamilyWallError as exc:
|
||||
return f"Error: Family Wall API error: {exc}"
|
||||
except Exception as exc:
|
||||
return f"Error: Connection error: {exc}"
|
||||
|
||||
try:
|
||||
updated_obj: dict[str, Any] = resp["a00"]["r"]["r"]
|
||||
if not isinstance(updated_obj, dict) or "metaId" not in updated_obj:
|
||||
raise TypeError("unexpected shape")
|
||||
except (KeyError, TypeError):
|
||||
return json.dumps(
|
||||
{"warning": "Unexpected taskupdatelist response structure", "raw": resp},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
raw_emoji: str = updated_obj.get("emoji", "")
|
||||
return json.dumps(
|
||||
{
|
||||
"updated": True,
|
||||
"id": updated_obj.get("metaId"),
|
||||
"name": translate_name(updated_obj.get("name", "")),
|
||||
"type": updated_obj.get("taskListType"),
|
||||
"emoji": raw_emoji if raw_emoji else None,
|
||||
"color": updated_obj.get("color") or None,
|
||||
"circle_id": updated_obj.get("familyId"),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: create_circle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user