From 94ecf40f9a3a490ce5d4e865b26ca36d6ded419a Mon Sep 17 00:00:00 2001 From: KuriGohan-Kamehameha <16231581+KuriGohan-Kamehameha@users.noreply.github.com> Date: Thu, 7 May 2026 21:50:46 -0400 Subject: [PATCH] feat(calendar): add nc_calendar_complete_todo convenience tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a one-shot "complete this todo" wrapper around update_todo. It sets STATUS=COMPLETED, PERCENT-COMPLETE=100, and the COMPLETED timestamp in a single call. The same operation has always been possible via nc_calendar_update_todo with three explicit fields, but for AI clients the phrasing "complete this task" is much more natural than "set status to COMPLETED, set percent_complete to 100, and set completed to " — the latter forces the client to compute a timestamp and remember three field names. A dedicated tool also pairs naturally with the existing delete_todo, update_todo, and create_todo trio. The implementation is a thin wrapper: it delegates to client.calendar.update_todo with a fixed three-field payload, defaulting completed_at to dt.datetime.now(dt.timezone.utc).isoformat() when the caller doesn't supply one. No client-side or model changes are needed. Verified end-to-end against Nextcloud 33.0.2: create_todo -> complete_todo -> search_todos shows status=COMPLETED, percent_complete=100, completed=. --- nextcloud_mcp_server/server/calendar.py | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/nextcloud_mcp_server/server/calendar.py b/nextcloud_mcp_server/server/calendar.py index 01bde47e4..080c84b26 100644 --- a/nextcloud_mcp_server/server/calendar.py +++ b/nextcloud_mcp_server/server/calendar.py @@ -1104,6 +1104,50 @@ async def nc_calendar_delete_todo( client = await get_client(ctx) return await client.calendar.delete_todo(calendar_name, todo_uid) + @mcp.tool( + title="Complete Todo Task", + annotations=ToolAnnotations(idempotentHint=True, openWorldHint=True), + ) + @require_scopes("todo.write", "calendar.read") + @instrument_tool + async def nc_calendar_complete_todo( + calendar_name: str, + todo_uid: str, + ctx: Context, + completed_at: Optional[str] = None, + ): + """Mark a todo/task as completed. + + Convenience wrapper around nc_calendar_update_todo that sets + STATUS=COMPLETED, PERCENT-COMPLETE=100, and the COMPLETED + timestamp in one call. Equivalent to invoking update_todo with + those three fields populated; useful for AI clients where + "complete this task" is a more natural phrasing than "set status + to COMPLETED, set percent_complete to 100, set completed + timestamp". + + Args: + calendar_name: Name of the calendar containing the todo + todo_uid: UID of the todo to mark complete + ctx: MCP context + completed_at: Optional ISO 8601 completion timestamp. + Defaults to the current UTC time if not provided. + + Returns: + Dict with the update result. + """ + client = await get_client(ctx) + if completed_at is None: + completed_at = dt.datetime.now(dt.timezone.utc).isoformat() + todo_data = { + "status": "COMPLETED", + "percent_complete": 100, + "completed": completed_at, + } + return await client.calendar.update_todo( + calendar_name, todo_uid, todo_data + ) + @mcp.tool( title="Search Todo Tasks", annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),