feat: add OTHER list type + FW_DEBUG task-field logging (v0.8.3)
- create_list now accepts list_type="OTHER" (previously rejected) - get_tasks logs unknown task fields to stderr when FW_DEBUG=1 (preparation for repeating-task field discovery) - README, CLAUDE.md, version bumped to 0.8.3 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
|
## Aktueller Stand
|
||||||
|
|
||||||
### Implementierte Tools (v0.8.2)
|
### Implementierte Tools (v0.8.3)
|
||||||
|
|
||||||
| Kategorie | Tools |
|
| Kategorie | Tools |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -53,7 +53,8 @@ und wird in Claude Desktop eingebunden.
|
|||||||
- v0.7.5: Primärkreis-Schutz in update_circle (isFirstFamily-Check) ✓
|
- 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.0: Rezept-Kategorien (get_recipe_categories, create_recipe + category_ids, update_recipe + category_ids) ✓
|
||||||
- v0.8.1: Bugfixes (recipe categories) ✓
|
- 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.8.x: mpadditemtolist (gestrichen – Family Wall kann das nativ)
|
||||||
- v0.9.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
- v0.9.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
||||||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
- 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 |
|
| `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like |
|
||||||
| `taskcategoryput` | `name`, `emoji` | – |
|
| `taskcategoryput` | `name`, `emoji` | – |
|
||||||
| `taskcategorydelete` | `id` | metaId der Kategorie |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `taskdeletelist` | `id`, `scope` | `scope`: Kreis-metaId für sekundäre Kreise |
|
||||||
|
|||||||
@@ -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.
|
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
|
### Read
|
||||||
|
|
||||||
- `get_circles` -- list all family circles
|
- `get_circles` -- list all family circles
|
||||||
- `get_members` -- list members of a circle (or all 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_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_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)
|
- `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
|
- `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
|
- `toggle_task` -- mark a task complete or reopen it
|
||||||
- `delete_task` -- permanently delete a task
|
- `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)
|
- `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)
|
- `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)
|
- `create_category` -- create a custom category for a shopping list (with optional icon)
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mcp-familywall"
|
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"
|
description = "MCP server for Family Wall — read your family's lists and tasks via Claude"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.8.2"
|
__version__ = "0.8.3"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
@@ -342,7 +344,9 @@ def get_lists(scope: str | None = None) -> str:
|
|||||||
candidate = data["a00"]["r"]["r"]
|
candidate = data["a00"]["r"]["r"]
|
||||||
if isinstance(candidate, list):
|
if isinstance(candidate, list):
|
||||||
raw_lists = candidate
|
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"]
|
raw_lists = candidate["updatedCreated"]
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
pass
|
pass
|
||||||
@@ -392,6 +396,18 @@ def get_tasks(list_id: str, only_open: bool = True):
|
|||||||
|
|
||||||
raw_tasks = _extract_tasks(data)
|
raw_tasks = _extract_tasks(data)
|
||||||
|
|
||||||
|
_fw_debug = os.environ.get("FW_DEBUG") == "1"
|
||||||
|
_known_fields = {
|
||||||
|
"metaId",
|
||||||
|
"text",
|
||||||
|
"description",
|
||||||
|
"complete",
|
||||||
|
"taskCategoryId",
|
||||||
|
"dueDate",
|
||||||
|
"assigneeIds",
|
||||||
|
"taskListId",
|
||||||
|
}
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for task in raw_tasks:
|
for task in raw_tasks:
|
||||||
if task.get("taskListId") != list_id:
|
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"
|
completed = str(task.get("complete", "false")).lower() == "true"
|
||||||
if only_open and completed:
|
if only_open and completed:
|
||||||
continue
|
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(
|
result.append(
|
||||||
{
|
{
|
||||||
"id": task.get("metaId"),
|
"id": task.get("metaId"),
|
||||||
@@ -988,7 +1014,7 @@ def create_list(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: Display name for the new list (max 200 characters).
|
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
|
shared_to_all: When ``True`` (default) the list is shared with all
|
||||||
circle members. When ``False`` it is private to the creator.
|
circle members. When ``False`` it is private to the creator.
|
||||||
color: Optional background colour as a hex string (e.g. ``"#4784EC"``).
|
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
|
Includes ``circle_id`` field showing which circle the list was
|
||||||
created in.
|
created in.
|
||||||
"""
|
"""
|
||||||
if list_type not in ("SHOPPING_LIST", "TODOS"):
|
if list_type not in ("SHOPPING_LIST", "TODOS", "OTHER"):
|
||||||
return "Error: list_type must be 'SHOPPING_LIST' or 'TODOS'."
|
return "Error: list_type must be 'SHOPPING_LIST', 'TODOS', or 'OTHER'."
|
||||||
if len(name) > 200:
|
if len(name) > 200:
|
||||||
return "Error: name must not exceed 200 characters."
|
return "Error: name must not exceed 200 characters."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user