diff --git a/CLAUDE.md b/CLAUDE.md index cce1cc6..d63f8a6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,7 +24,7 @@ und wird in Claude Desktop eingebunden. ## Aktueller Stand -### Implementierte Tools (v0.8.2) +### Implementierte Tools (v0.8.3) | Kategorie | Tools | |---|---| @@ -53,7 +53,8 @@ und wird in Claude Desktop eingebunden. - v0.7.5: Primärkreis-Schutz in update_circle (isFirstFamily-Check) ✓ - v0.8.0: Rezept-Kategorien (get_recipe_categories, create_recipe + category_ids, update_recipe + category_ids) ✓ - v0.8.1: Bugfixes (recipe categories) ✓ -- v0.8.2: get_lists() ohne scope → alle Kreise ✓ ← aktuell +- v0.8.2: get_lists() ohne scope → alle Kreise ✓ +- v0.8.3: OTHER-Listentyp dokumentiert + create_list unterstützt ihn; FW_DEBUG=1 loggt unbekannte Task-Felder (Vorbereitung Wiederholungen) ✓ ← aktuell - v0.8.x: mpadditemtolist (gestrichen – Family Wall kann das nativ) - v0.9.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich) - v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren) @@ -144,7 +145,7 @@ Fehler bei falschen Parametern kommen nicht immer auf Top-Level: | `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like | | `taskcategoryput` | `name`, `emoji` | – | | `taskcategorydelete` | `id` | metaId der Kategorie | -| `taskcreatelist` | `name`, `taskListType`, `sharedToAll`, `color`, `emoji`, `scope` | `scope`: Kreis-metaId für nicht-primäre Kreise | +| `taskcreatelist` | `name`, `taskListType`, `sharedToAll`, `color`, `emoji`, `scope` | `taskListType`: `SHOPPING_LIST`, `TODOS`, `OTHER`; `scope`: Kreis-metaId für nicht-primäre Kreise | | `taskgettasklists` | `scope` | Kreis-metaId; ohne scope → primärer Kreis | | `taskupdatelist` | `metaId`, `name`, `color`, `emoji`, `scope` | `metaId` ⚠️ nicht `id`!; `scope`: Kreis-metaId für sekundäre Kreise; Partial Update | | `taskdeletelist` | `id`, `scope` | `scope`: Kreis-metaId für sekundäre Kreise | diff --git a/README.md b/README.md index e353115..008b67e 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ 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.8.2) +## Features (v0.8.3) ### Read - `get_circles` -- list all family circles - `get_members` -- list members of a circle (or all circles) -- `get_lists` -- list all task lists (includes `emoji`, `color`, `circle_id`; `null` when unset); without scope parameter returns lists from **all circles**; optional `scope` parameter filters by circle metaId or circle name +- `get_lists` -- list all task lists (includes `emoji`, `color`, `circle_id`; `null` when unset; list types: `SHOPPING_LIST`, `TODOS`, `OTHER`); without scope parameter returns lists from **all circles**; optional `scope` parameter filters by circle metaId or circle name - `get_tasks` -- list tasks in a specific list (includes `category_id`, `due_date`, `assignee_ids`) - `get_categories` -- list categories for a list (locale-filtered; custom categories always included; `custom` flag marks user-created ones) - `get_activities` -- list recent wall activities (author resolved to display name) @@ -22,7 +22,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your - `update_task` -- update text, description, category, due date, assignees, or move to a different list; supports `clear_due_date=True` to remove a due date - `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) +- `create_list` -- create a new task list (`SHOPPING_LIST`, `TODOS`, or `OTHER`; 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) diff --git a/pyproject.toml b/pyproject.toml index 01cc8ed..deb89cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "0.8.2" +version = "0.8.3" 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 deded32..732155f 100644 --- a/src/mcp_familywall/__init__.py +++ b/src/mcp_familywall/__init__.py @@ -1 +1 @@ -__version__ = "0.8.2" +__version__ = "0.8.3" diff --git a/src/mcp_familywall/server.py b/src/mcp_familywall/server.py index 1243723..a61bcac 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -4,6 +4,8 @@ from __future__ import annotations import json import logging +import os +import sys from typing import Any from mcp.server.fastmcp import FastMCP @@ -342,7 +344,9 @@ def get_lists(scope: str | None = None) -> str: candidate = data["a00"]["r"]["r"] if isinstance(candidate, list): raw_lists = candidate - elif isinstance(candidate, dict) and isinstance(candidate.get("updatedCreated"), list): + elif isinstance(candidate, dict) and isinstance( + candidate.get("updatedCreated"), list + ): raw_lists = candidate["updatedCreated"] except (KeyError, TypeError): pass @@ -392,6 +396,18 @@ def get_tasks(list_id: str, only_open: bool = True): raw_tasks = _extract_tasks(data) + _fw_debug = os.environ.get("FW_DEBUG") == "1" + _known_fields = { + "metaId", + "text", + "description", + "complete", + "taskCategoryId", + "dueDate", + "assigneeIds", + "taskListId", + } + result = [] for task in raw_tasks: if task.get("taskListId") != list_id: @@ -399,6 +415,16 @@ def get_tasks(list_id: str, only_open: bool = True): completed = str(task.get("complete", "false")).lower() == "true" if only_open and completed: continue + + if _fw_debug: + unknown = {k: v for k, v in task.items() if k not in _known_fields} + if unknown: + print( + f"[FW_DEBUG] task {task.get('metaId')} unknown fields: " + + json.dumps(unknown, ensure_ascii=False), + file=sys.stderr, + ) + result.append( { "id": task.get("metaId"), @@ -988,7 +1014,7 @@ def create_list( Args: name: Display name for the new list (max 200 characters). - list_type: List type — either ``"SHOPPING_LIST"`` or ``"TODOS"``. + list_type: List type — ``"SHOPPING_LIST"``, ``"TODOS"``, or ``"OTHER"``. shared_to_all: When ``True`` (default) the list is shared with all circle members. When ``False`` it is private to the creator. color: Optional background colour as a hex string (e.g. ``"#4784EC"``). @@ -1003,8 +1029,8 @@ def create_list( Includes ``circle_id`` field showing which circle the list was created in. """ - if list_type not in ("SHOPPING_LIST", "TODOS"): - return "Error: list_type must be 'SHOPPING_LIST' or 'TODOS'." + if list_type not in ("SHOPPING_LIST", "TODOS", "OTHER"): + return "Error: list_type must be 'SHOPPING_LIST', 'TODOS', or 'OTHER'." if len(name) > 200: return "Error: name must not exceed 200 characters."