feat: category assignment in create_task / update_task (v0.4.10)
Verified via FW_DEBUG=1 + systematic param-name probing that the correct parameter is `taskCategoryId` with value = full metaId from get_categories (e.g. taskCategory/23431854_200). Numeric systemCategoryId alone causes API error; full metaId is accepted and stored. Changes: - create_task: add optional category_id parameter → sent as taskCategoryId - update_task: add optional category_id parameter → sent as taskCategoryId; guard now accepts category_id-only updates - get_tasks: expose category_id field in returned task objects - get_categories: update docstring (param name now known) - SPEC.md: document verified taskCategoryId param + clarify categories[] vs taskCategoryId field distinction - scripts/find_category_param.py: discovery script used to find param name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,21 +23,21 @@ eingebunden.
|
|||||||
|
|
||||||
## Aktueller Stand
|
## Aktueller Stand
|
||||||
|
|
||||||
### Implementierte Tools (v0.4.8)
|
### Implementierte Tools (v0.4.10)
|
||||||
|
|
||||||
| Kategorie | Tools |
|
| Kategorie | Tools |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Kreise | `get_circles`, `get_members` |
|
| Kreise | `get_circles`, `get_members` |
|
||||||
| Listen | `get_lists` |
|
| Listen | `get_lists` |
|
||||||
| Tasks (Lesen) | `get_tasks`, `get_categories` |
|
| Tasks (Lesen) | `get_tasks` (inkl. `category_id`), `get_categories` |
|
||||||
| Wall | `get_activities`, `like_post` |
|
| Wall | `get_activities`, `like_post` |
|
||||||
| Tasks (Schreiben) | `create_task`, `update_task`, `toggle_task`, `delete_task` |
|
| Tasks (Schreiben) | `create_task` (inkl. `category_id`), `update_task` (inkl. `category_id`), `toggle_task`, `delete_task` |
|
||||||
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- v0.x: Erweiterter Lese- + Schreibzugriff ← aktuell
|
- v0.x: Erweiterter Lese- + Schreibzugriff ← aktuell
|
||||||
- Offen: Unlike (`like_post(like=False)`), Task-Kategorie-Zuweisung beim Erstellen/Aktualisieren
|
- Offen: Unlike (`like_post(like=False)`)
|
||||||
|
|
||||||
|
|
||||||
## Referenzprojekt
|
## Referenzprojekt
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
|
|
||||||
MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, and tasks directly from Claude.
|
MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, and tasks directly from Claude.
|
||||||
|
|
||||||
## Features (v0.4.9)
|
## Features (v0.4.10)
|
||||||
|
|
||||||
### 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 (optionally filtered by circle)
|
- `get_lists` -- list all task lists (optionally filtered by circle)
|
||||||
- `get_tasks` -- list tasks in a specific list
|
- `get_tasks` -- list tasks in a specific list (includes `category_id` field)
|
||||||
- `get_categories` -- list categories available for a list (locale-filtered, default: German)
|
- `get_categories` -- list categories available for a list (locale-filtered, default: German)
|
||||||
- `get_activities` -- list recent wall activities (author resolved to display name)
|
- `get_activities` -- list recent wall activities (author resolved to display name)
|
||||||
|
|
||||||
### Write (with confirmation prompt)
|
### Write (with confirmation prompt)
|
||||||
|
|
||||||
- `create_task` -- create a new task in a list
|
- `create_task` -- create a new task in a list (supports `category_id` for shopping lists)
|
||||||
- `update_task` -- update the text/description of an existing task
|
- `update_task` -- update text, description, and/or category of an existing task
|
||||||
- `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
|
||||||
- `like_post` -- like or unlike a wall post/activity
|
- `like_post` -- like or unlike a wall post/activity
|
||||||
|
|||||||
@@ -158,19 +158,34 @@ a02.r.r.updatedCreated[] → tasksync (Tasks)
|
|||||||
.description → optionale Beschreibung
|
.description → optionale Beschreibung
|
||||||
.taskListId → Zugehörigkeit zur Liste
|
.taskListId → Zugehörigkeit zur Liste
|
||||||
.complete → "true" / "false" (String, nicht Boolean!)
|
.complete → "true" / "false" (String, nicht Boolean!)
|
||||||
.categories[] → zugewiesene Kategorien des Tasks
|
.categories[] → Listen-Level-Systemkategorie (z.B. SYS-CAT-SHOPPINGLIST);
|
||||||
.system → "true" wenn System-Kategorie, "false" wenn custom
|
NICHT die spezifische Task-Kategorie — immer identisch
|
||||||
.name → Kategoriename (z.B. "SYS-CAT-TODOS", "Beverages")
|
für alle Tasks einer Liste
|
||||||
|
.system → "true" (immer System-Kategorie)
|
||||||
|
.name → Listen-Systemkategorien (z.B. "SYS-CAT-SHOPPINGLIST", "SYS-CAT-TODOS")
|
||||||
|
.taskCategoryId → spezifische Task-Kategorie (verifiziert): metaId-Format
|
||||||
|
(z.B. "taskCategory/23431854_200"), null wenn nicht gesetzt
|
||||||
.assignee / .assigneeIds → zugewiesene Mitglieder
|
.assignee / .assigneeIds → zugewiesene Mitglieder
|
||||||
.reminder → Erinnerungsdatum (ISO 8601, optional)
|
.reminder → Erinnerungsdatum (ISO 8601, optional)
|
||||||
.recurrency → Wiederholungsregel (optional)
|
.recurrency → Wiederholungsregel (optional)
|
||||||
.sortingIndex → Anzeigereihenfolge
|
.sortingIndex → Anzeigereihenfolge
|
||||||
```
|
```
|
||||||
|
|
||||||
**Kategorie-Zuweisung bei taskcreate2 / taskupdate2:** Parameter-Name noch unbekannt.
|
**Kategorie-Zuweisung bei taskcreate2 / taskupdate2 (verifiziert):**
|
||||||
Getestete Varianten (`taskCategoryId`, `categoryId`, `category`, `categoryMetaId`) werden
|
|
||||||
serverseitig ignoriert — die Task bekommt stets die Default-Systemkategorie der Liste.
|
| Parameter | Pflicht | Wert |
|
||||||
Service Worker in der Web-App verhindert Inspektion des echten Requests. Noch zu verifizieren.
|
|---|---|---|
|
||||||
|
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||||
|
|
||||||
|
Hinweise:
|
||||||
|
- Wert muss das vollständige metaId-Format `taskCategory/<familyId>_<systemCategoryId>` sein.
|
||||||
|
Nur der numerische `systemCategoryId`-Teil (z.B. `200`) führt zu API-Fehler
|
||||||
|
`"cannot find task category id=200"`.
|
||||||
|
- Das `categories[]`-Feld in der Response zeigt immer `SYS-CAT-SHOPPINGLIST`
|
||||||
|
(Listen-Level-Systemkategorie, unabhängig vom gesetzten `taskCategoryId`).
|
||||||
|
Die tatsächliche Task-Kategorie ist im Feld `taskCategoryId` der Task gespeichert.
|
||||||
|
- Nur für Einkaufslisten (`taskListType=SHOPPING_LIST`) relevant;
|
||||||
|
TODO-Listen haben keine Kategorien.
|
||||||
|
|
||||||
## Systembezeichnungen für Listen-Namen
|
## Systembezeichnungen für Listen-Namen
|
||||||
|
|
||||||
@@ -244,6 +259,7 @@ Content-Type: application/x-www-form-urlencoded
|
|||||||
| `taskListId` | ja | Listen-ID aus `get_lists` (z.B. `taskList/123_456`) |
|
| `taskListId` | ja | Listen-ID aus `get_lists` (z.B. `taskList/123_456`) |
|
||||||
| `text` | ja | Aufgabentitel |
|
| `text` | ja | Aufgabentitel |
|
||||||
| `description` | nein | Optionale Beschreibung |
|
| `description` | nein | Optionale Beschreibung |
|
||||||
|
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||||
|
|
||||||
**Response-Struktur (verifiziert):**
|
**Response-Struktur (verifiziert):**
|
||||||
```
|
```
|
||||||
@@ -263,8 +279,9 @@ Content-Type: application/x-www-form-urlencoded
|
|||||||
| Parameter | Pflicht | Wert |
|
| Parameter | Pflicht | Wert |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `metaId` | ja | Task-ID aus `get_tasks` |
|
| `metaId` | ja | Task-ID aus `get_tasks` |
|
||||||
| `text` | nein | Neuer Titel (mindestens `text` oder `description` erforderlich) |
|
| `text` | nein | Neuer Titel (mindestens eines der optionalen Felder erforderlich) |
|
||||||
| `description` | nein | Neue Beschreibung |
|
| `description` | nein | Neue Beschreibung |
|
||||||
|
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||||
|
|
||||||
Hinweis: `taskListId` ist **nicht** erforderlich (verifiziert – Update ohne `taskListId` funktioniert).
|
Hinweis: `taskListId` ist **nicht** erforderlich (verifiziert – Update ohne `taskListId` funktioniert).
|
||||||
|
|
||||||
@@ -409,3 +426,4 @@ AND `moodStarShortcut: false` AND `moodMap: {}`.
|
|||||||
- ~~`wallmood`: Parameter-Name `wallId`~~ → **`wall_message_id`** (verifiziert via API-Fehlermeldung)
|
- ~~`wallmood`: Parameter-Name `wallId`~~ → **`wall_message_id`** (verifiziert via API-Fehlermeldung)
|
||||||
- ~~`wallmood`: `moodType`-Werte, Toggle vs. explizit, Response-Struktur~~ → verifiziert: idempotentes SET mit `"STAR"`, kein Toggle (siehe oben)
|
- ~~`wallmood`: `moodType`-Werte, Toggle vs. explizit, Response-Struktur~~ → verifiziert: idempotentes SET mit `"STAR"`, kein Toggle (siehe oben)
|
||||||
- `wallmood` Unlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben)
|
- `wallmood` Unlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben)
|
||||||
|
- ~~`taskcreate2` / `taskupdate2`: Kategorie-Paramter-Name~~ → **`taskCategoryId`**, Wert = vollständige metaId (verifiziert)
|
||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mcp-familywall"
|
name = "mcp-familywall"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
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"
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
"""Discover the category assignment parameter name for taskcreate2 / taskupdate2.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
FW_DEBUG=1 python scripts/find_category_param.py
|
||||||
|
|
||||||
|
The script:
|
||||||
|
1. Fetches the first available shopping-list category (German locale).
|
||||||
|
2. For each candidate parameter name, creates a test task with that parameter set,
|
||||||
|
checks whether the category appears in the response, then deletes the task.
|
||||||
|
3. Reports which parameter name (if any) caused the category to be applied.
|
||||||
|
|
||||||
|
Prerequisites: credentials stored in OS keyring or FW_EMAIL / FW_PASSWORD set.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Allow running from repo root without installing the package.
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||||
|
|
||||||
|
from mcp_familywall.auth import get_credentials
|
||||||
|
from mcp_familywall.fw_client import FamilyWallClient, FamilyWallError
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Config
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Shopping list to use for the test (adjust if needed).
|
||||||
|
TEST_LIST_ID = "taskList/23431854_29740941"
|
||||||
|
|
||||||
|
# Category to assign — will be resolved from the first taskcategorysync entry
|
||||||
|
# for this list in German locale; override here if desired.
|
||||||
|
FORCED_CATEGORY_META_ID: str | None = None
|
||||||
|
|
||||||
|
# Parameter-name candidates not yet tested (already ruled out are commented out):
|
||||||
|
CANDIDATES: list[dict[str, str | None]] = [
|
||||||
|
# key → value format description
|
||||||
|
# Full metaId variants
|
||||||
|
{"key": "systemCategoryId", "fmt": "numeric"}, # just the number, e.g. "200"
|
||||||
|
{"key": "taskCategorySystemId", "fmt": "numeric"},
|
||||||
|
{"key": "categories", "fmt": "meta_id"}, # full taskCategory/… metaId
|
||||||
|
{"key": "categoryIds", "fmt": "meta_id"},
|
||||||
|
{"key": "taskCategoryName", "fmt": "name"}, # category name as string
|
||||||
|
{"key": "categoryName", "fmt": "name"},
|
||||||
|
{"key": "taskCategoryId", "fmt": "meta_id"}, # already tried but try numeric too
|
||||||
|
# Numeric variants of already-tried names
|
||||||
|
{"key": "taskCategoryId", "fmt": "numeric"},
|
||||||
|
{"key": "categoryId", "fmt": "numeric"},
|
||||||
|
{"key": "category", "fmt": "numeric"},
|
||||||
|
{"key": "categoryMetaId", "fmt": "numeric"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_category(
|
||||||
|
client: FamilyWallClient, list_id: str
|
||||||
|
) -> tuple[str, str, str]:
|
||||||
|
"""Return (meta_id, system_category_id_str, name) for the first German category."""
|
||||||
|
data = client.call(
|
||||||
|
"accgetallfamily",
|
||||||
|
{"a01call": "taskcategorysync", "a02call": "tasksync"},
|
||||||
|
)
|
||||||
|
cats = data["a01"]["r"]["r"]["updatedCreated"]
|
||||||
|
|
||||||
|
# Also resolve list type for filtering.
|
||||||
|
list_type: str | None = None
|
||||||
|
try:
|
||||||
|
list_data = client.call("taskgettasklists", {})
|
||||||
|
for lst in list_data.get("a00", {}).get("r", {}).get("r", []) or []:
|
||||||
|
if lst.get("metaId") == list_id:
|
||||||
|
list_type = lst.get("taskListType")
|
||||||
|
break
|
||||||
|
except FamilyWallError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for cat in cats:
|
||||||
|
if cat.get("locale") != "de":
|
||||||
|
continue
|
||||||
|
if list_type and cat.get("taskListType") != list_type:
|
||||||
|
continue
|
||||||
|
meta_id: str = cat["metaId"]
|
||||||
|
sys_id: str = str(cat.get("systemCategoryId", ""))
|
||||||
|
name: str = cat.get("name", "")
|
||||||
|
print(f" Using category: metaId={meta_id!r}, systemCategoryId={sys_id!r}, name={name!r}")
|
||||||
|
return meta_id, sys_id, name
|
||||||
|
|
||||||
|
raise RuntimeError("No German category found for list; adjust TEST_LIST_ID.")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_task_categories(task_obj: dict) -> list[str]:
|
||||||
|
"""Extract category names from a task API object."""
|
||||||
|
cats = task_obj.get("categories") or []
|
||||||
|
return [c.get("name", "") for c in cats if isinstance(c, dict)]
|
||||||
|
|
||||||
|
|
||||||
|
def _delete_task(client: FamilyWallClient, task_id: str) -> None:
|
||||||
|
try:
|
||||||
|
client.call("metadelete", {"id": task_id})
|
||||||
|
except FamilyWallError as exc:
|
||||||
|
print(f" [warn] Could not delete task {task_id}: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
email, password = get_credentials()
|
||||||
|
|
||||||
|
with FamilyWallClient() as client:
|
||||||
|
client.login(email, password)
|
||||||
|
|
||||||
|
print(f"\nResolving category for list {TEST_LIST_ID!r} …")
|
||||||
|
if FORCED_CATEGORY_META_ID:
|
||||||
|
# Can't resolve name/numeric from meta_id easily; just use the meta_id.
|
||||||
|
cat_meta = FORCED_CATEGORY_META_ID
|
||||||
|
cat_numeric = cat_meta.split("_")[-1] if "_" in cat_meta else cat_meta
|
||||||
|
cat_name = cat_meta
|
||||||
|
else:
|
||||||
|
cat_meta, cat_numeric, cat_name = _resolve_category(client, TEST_LIST_ID)
|
||||||
|
|
||||||
|
value_map = {
|
||||||
|
"meta_id": cat_meta,
|
||||||
|
"numeric": cat_numeric,
|
||||||
|
"name": cat_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"\nTesting {len(CANDIDATES)} candidate parameter names …\n")
|
||||||
|
results: list[tuple[str, str, bool]] = []
|
||||||
|
|
||||||
|
seen: set[tuple[str, str]] = set() # avoid duplicate tests
|
||||||
|
|
||||||
|
for cand in CANDIDATES:
|
||||||
|
key: str = cand["key"] # type: ignore[assignment]
|
||||||
|
fmt: str = cand["fmt"] # type: ignore[assignment]
|
||||||
|
value: str = value_map[fmt]
|
||||||
|
pair = (key, value)
|
||||||
|
if pair in seen:
|
||||||
|
continue
|
||||||
|
seen.add(pair)
|
||||||
|
|
||||||
|
params: dict = {
|
||||||
|
"taskListId": TEST_LIST_ID,
|
||||||
|
"text": f"[TEST] cat-param-probe {key}={fmt}",
|
||||||
|
key: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f" Testing {key!r} = {value!r} …", end=" ", flush=True)
|
||||||
|
try:
|
||||||
|
data = client.call("taskcreate2", params)
|
||||||
|
except FamilyWallError as exc:
|
||||||
|
print(f"API ERROR: {exc}")
|
||||||
|
results.append((key, value, False))
|
||||||
|
continue
|
||||||
|
|
||||||
|
task_obj = data.get("a00", {}).get("r", {}).get("r", {})
|
||||||
|
task_id: str = task_obj.get("metaId", "")
|
||||||
|
applied_cats = _get_task_categories(task_obj)
|
||||||
|
|
||||||
|
# A non-default category was applied when the name matches our target.
|
||||||
|
success = any(cat_name in c or c == cat_name for c in applied_cats)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print(f"SUCCESS -> categories={applied_cats}")
|
||||||
|
else:
|
||||||
|
print(f"no effect -> categories={applied_cats}")
|
||||||
|
|
||||||
|
results.append((key, value, success))
|
||||||
|
|
||||||
|
if task_id:
|
||||||
|
_delete_task(client, task_id)
|
||||||
|
|
||||||
|
client.logout()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("RESULTS")
|
||||||
|
print("=" * 60)
|
||||||
|
hits = [(k, v) for k, v, ok in results if ok]
|
||||||
|
misses = [(k, v) for k, v, ok in results if not ok]
|
||||||
|
|
||||||
|
if hits:
|
||||||
|
print(f"\nWORKING parameter(s) found ({len(hits)}):")
|
||||||
|
for k, v in hits:
|
||||||
|
print(f" {k!r} = {v!r}")
|
||||||
|
else:
|
||||||
|
print("\nNo working parameter found.")
|
||||||
|
|
||||||
|
print(f"\nDid NOT work ({len(misses)}):")
|
||||||
|
for k, v in misses:
|
||||||
|
print(f" {k!r} = {v!r}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -325,6 +325,7 @@ def get_tasks(list_id: str, only_open: bool = True):
|
|||||||
"text": task.get("text"),
|
"text": task.get("text"),
|
||||||
"description": task.get("description"),
|
"description": task.get("description"),
|
||||||
"completed": completed,
|
"completed": completed,
|
||||||
|
"category_id": task.get("taskCategoryId"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -344,8 +345,8 @@ def get_categories(list_id: str, locale: str = "de") -> str:
|
|||||||
lists return an empty list. Categories are filtered by locale so only
|
lists return an empty list. Categories are filtered by locale so only
|
||||||
the language-appropriate names are returned (default: German).
|
the language-appropriate names are returned (default: German).
|
||||||
|
|
||||||
Note: the parameter name for assigning a category when creating or
|
Use the returned ``id`` values as the ``category_id`` parameter in
|
||||||
updating a task is not yet verified via FW_DEBUG=1. See SPEC.md.
|
``create_task`` and ``update_task``.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
list_id: List ID from get_lists (e.g. ``taskList/23431854_29740942``).
|
list_id: List ID from get_lists (e.g. ``taskList/23431854_29740942``).
|
||||||
@@ -517,7 +518,12 @@ def _authenticated_call(endpoint: str, params: dict[str, Any]) -> dict[str, Any]
|
|||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def create_task(list_id: str, text: str, description: str | None = None) -> str:
|
def create_task(
|
||||||
|
list_id: str,
|
||||||
|
text: str,
|
||||||
|
description: str | None = None,
|
||||||
|
category_id: str | None = None,
|
||||||
|
) -> str:
|
||||||
"""Create a new task in the given list.
|
"""Create a new task in the given list.
|
||||||
|
|
||||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||||
@@ -526,6 +532,9 @@ def create_task(list_id: str, text: str, description: str | None = None) -> str:
|
|||||||
list_id: Target list ID from get_lists (e.g. ``taskList/123_456``).
|
list_id: Target list ID from get_lists (e.g. ``taskList/123_456``).
|
||||||
text: Task title / main text.
|
text: Task title / main text.
|
||||||
description: Optional longer description.
|
description: Optional longer description.
|
||||||
|
category_id: Optional category metaId from get_categories
|
||||||
|
(e.g. ``taskCategory/23431854_200``). Only meaningful for
|
||||||
|
shopping lists; ignored for TODO lists.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JSON with the new task's metaId on success, or an error message.
|
JSON with the new task's metaId on success, or an error message.
|
||||||
@@ -533,6 +542,8 @@ def create_task(list_id: str, text: str, description: str | None = None) -> str:
|
|||||||
params: dict[str, Any] = {"taskListId": list_id, "text": text}
|
params: dict[str, Any] = {"taskListId": list_id, "text": text}
|
||||||
if description:
|
if description:
|
||||||
params["description"] = description
|
params["description"] = description
|
||||||
|
if category_id:
|
||||||
|
params["taskCategoryId"] = category_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = _authenticated_call("taskcreate2", params)
|
data = _authenticated_call("taskcreate2", params)
|
||||||
@@ -559,28 +570,38 @@ def create_task(list_id: str, text: str, description: str | None = None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def update_task(task_id: str, text: str | None = None, description: str | None = None) -> str:
|
def update_task(
|
||||||
"""Update the text and/or description of an existing task.
|
task_id: str,
|
||||||
|
text: str | None = None,
|
||||||
|
description: str | None = None,
|
||||||
|
category_id: str | None = None,
|
||||||
|
) -> str:
|
||||||
|
"""Update the text, description, and/or category of an existing task.
|
||||||
|
|
||||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||||
At least one of *text* or *description* must be provided.
|
At least one of *text*, *description*, or *category_id* must be provided.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_id: Task metaId from get_tasks.
|
task_id: Task metaId from get_tasks.
|
||||||
text: New title text (omit to leave unchanged).
|
text: New title text (omit to leave unchanged).
|
||||||
description: New description (omit to leave unchanged).
|
description: New description (omit to leave unchanged).
|
||||||
|
category_id: New category metaId from get_categories
|
||||||
|
(e.g. ``taskCategory/23431854_200``). Only meaningful for
|
||||||
|
shopping lists; ignored for TODO lists.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JSON success indicator or an error message.
|
JSON success indicator or an error message.
|
||||||
"""
|
"""
|
||||||
if text is None and description is None:
|
if text is None and description is None and category_id is None:
|
||||||
return "Error: At least one of 'text' or 'description' must be provided."
|
return "Error: At least one of 'text', 'description', or 'category_id' must be provided."
|
||||||
|
|
||||||
params: dict[str, Any] = {"metaId": task_id}
|
params: dict[str, Any] = {"metaId": task_id}
|
||||||
if text is not None:
|
if text is not None:
|
||||||
params["text"] = text
|
params["text"] = text
|
||||||
if description is not None:
|
if description is not None:
|
||||||
params["description"] = description
|
params["description"] = description
|
||||||
|
if category_id is not None:
|
||||||
|
params["taskCategoryId"] = category_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_authenticated_call("taskupdate2", params)
|
_authenticated_call("taskupdate2", params)
|
||||||
|
|||||||
Reference in New Issue
Block a user