add_task
Create new tasks with optional Smart Add syntax for due dates, priorities, tags, locations, time estimates, and repetition. Parse natural language to set task details.
Instructions
Add a new task.
Supports Smart Add syntax when parse=True: - ^date for due date (^tomorrow, ^next friday) - !priority (!1, !2, !3) - #tag for tags (#work, #urgent) - @location - =time estimate (=30min, =1h) - *repeat pattern (*daily, *weekly)
Args: name: Task name (with optional Smart Add syntax) list_name: List to add to (uses default list if not specified) parse: Parse Smart Add syntax (default: True)
Returns: Created task details with transaction ID for undo
Examples: - add_task("Buy groceries") - add_task("Call mom ^tomorrow !1 #family") - add_task("Weekly review *weekly ^monday", list_name="Work")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| list_name | No | ||
| parse | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/rtm_mcp/tools/tasks.py:106-167 (handler)The add_task tool handler function. Decorated with @mcp.tool() inside register_task_tools. Accepts name, optional list_name, and parse flag. Makes rtm.tasks.add API call with Smart Add syntax support and returns formatted task response with transaction ID for undo.
@mcp.tool() async def add_task( ctx: Context, name: str, list_name: str | None = None, parse: bool = True, ) -> dict[str, Any]: """Add a new task. Supports Smart Add syntax when parse=True: - ^date for due date (^tomorrow, ^next friday) - !priority (!1, !2, !3) - #tag for tags (#work, #urgent) - @location - =time estimate (=30min, =1h) - *repeat pattern (*daily, *weekly) Args: name: Task name (with optional Smart Add syntax) list_name: List to add to (uses default list if not specified) parse: Parse Smart Add syntax (default: True) Returns: Created task details with transaction ID for undo Examples: - add_task("Buy groceries") - add_task("Call mom ^tomorrow !1 #family") - add_task("Weekly review *weekly ^monday", list_name="Work") """ client: RTMClient = await get_client() params: dict[str, Any] = { "name": name, "parse": "1" if parse else "0", } if list_name: lists_result = await client.call("rtm.lists.getList") from ..response_builder import parse_lists_response lists = parse_lists_response(lists_result) for lst in lists: if lst["name"].lower() == list_name.lower(): params["list_id"] = lst["id"] break result = await client.call("rtm.tasks.add", require_timeline=True, **params) # Parse the created task tasks = parse_tasks_response(result) task = tasks[0] if tasks else {} transaction_id = get_transaction_id(result) timezone = await _get_user_timezone(client) return build_response( data={ "task": format_task(task, timezone=timezone), "message": f"Created task: {task.get('name', name)}", }, transaction_id=transaction_id, ) - src/rtm_mcp/tools/tasks.py:17-17 (registration)register_task_tools function that registers all task tools including add_task via @mcp.tool() decorator.
def register_task_tools(mcp: Any, get_client: Any) -> None: - src/rtm_mcp/server.py:102-106 (registration)Server entry point calling register_task_tools(mcp, get_client) to register all task tools including add_task.
# Register all tools register_task_tools(mcp, get_client) register_list_tools(mcp, get_client) register_note_tools(mcp, get_client) register_utility_tools(mcp, get_client) - Helper functions used by add_task: build_response for consistent response format, format_task for task display, parse_tasks_response for parsing RTM API response, get_transaction_id for undo support.
def build_response( data: dict[str, Any] | list[Any], analysis: dict[str, Any] | None = None, transaction_id: str | None = None, ) -> dict[str, Any]: """Build a consistent response structure. Args: data: The main response data analysis: Optional analysis/insights transaction_id: Optional transaction ID for undo support Returns: Structured response dict """ response = { "data": data, "metadata": { "fetched_at": datetime.now().isoformat(), }, } if analysis: response["analysis"] = analysis if transaction_id: response["metadata"]["transaction_id"] = transaction_id return response def _convert_rtm_date(due: str, timezone: str | None) -> str: """Convert RTM date (UTC) to user's timezone. Args: due: Date string from RTM (ISO 8601 format, typically with Z suffix) timezone: User's IANA timezone (e.g., 'Europe/Warsaw') Returns: ISO 8601 date string in user's timezone, or original if conversion fails """ if not timezone: return due try: from zoneinfo import ZoneInfo # Parse the UTC date from RTM due_dt = datetime.fromisoformat(due.replace("Z", "+00:00")) # Convert to user's timezone user_tz = ZoneInfo(timezone) due_local = due_dt.astimezone(user_tz) # Return ISO format in user's timezone return due_local.isoformat() except Exception: # If conversion fails, return original return due def format_task( task: dict[str, Any], include_ids: bool = True, timezone: str | None = None ) -> dict[str, Any]: """Format a task for response. Args: task: Raw task data from RTM include_ids: Whether to include task IDs timezone: User's IANA timezone for date conversion (e.g., 'Europe/Warsaw') Returns: Formatted task dict """ # Convert dates to user's timezone due_display = None due_raw = task.get("due") if due_raw: due_display = _convert_rtm_date(due_raw, timezone) start_display = None start_raw = task.get("start") if start_raw: start_display = _convert_rtm_date(start_raw, timezone) formatted = { "name": task.get("name", ""), "priority": _priority_label(task.get("priority", "N")), "due": due_display, "start": start_display, "completed": task.get("completed") or None, "tags": task.get("tags", []), "url": task.get("url") or None, "notes_count": len(task.get("notes", [])), "estimate": task.get("estimate") or None, "modified": task.get("modified") or None, } if include_ids: formatted["id"] = task.get("id") formatted["taskseries_id"] = task.get("taskseries_id") formatted["list_id"] = task.get("list_id") return formatted def format_list(lst: dict[str, Any]) -> dict[str, Any]: """Format a list for response.""" return { "id": lst.get("id"), "name": lst.get("name"), "smart": lst.get("smart") == "1", "archived": lst.get("archived") == "1", "locked": lst.get("locked") == "1", } def _priority_label(priority: str) -> str: """Convert priority code to label.""" labels = { "1": "high", "2": "medium", "3": "low", "N": "none", } return labels.get(priority, "none") def priority_to_code(priority: str | int | None) -> str: """Convert priority label/number to RTM code.""" if priority is None: return "N" priority_str = str(priority).lower() mapping = { "high": "1", "1": "1", "medium": "2", "2": "2", "low": "3", "3": "3", "none": "N", "0": "N", "n": "N", } return mapping.get(priority_str, "N") def parse_tasks_response(result: dict[str, Any]) -> list[dict[str, Any]]: """Parse RTM tasks response into flat task list. RTM returns nested structure: tasks.list[].taskseries[].task[] We flatten this to a simple list with all IDs attached. """ tasks = [] task_lists = result.get("tasks", {}).get("list", []) if isinstance(task_lists, dict): task_lists = [task_lists] for tl in task_lists: list_id = tl.get("id") taskseries_list = tl.get("taskseries", []) if isinstance(taskseries_list, dict): taskseries_list = [taskseries_list] for ts in taskseries_list: task_data = ts.get("task", []) if isinstance(task_data, dict): task_data = [task_data] # Parse tags tags_data = ts.get("tags", []) if isinstance(tags_data, dict): tags = tags_data.get("tag", []) if isinstance(tags, str): tags = [tags] else: tags = [] # Parse notes notes_data = ts.get("notes", []) notes = [] if isinstance(notes_data, dict): notes = notes_data.get("note", []) if isinstance(notes, dict): notes = [notes] for t in task_data: tasks.append( { "id": t.get("id"), "taskseries_id": ts.get("id"), "list_id": list_id, "name": ts.get("name"), "due": t.get("due") or None, "has_due_time": t.get("has_due_time") == "1", "start": t.get("start") or None, "has_start_time": t.get("has_start_time") == "1", "completed": t.get("completed") or None, "deleted": t.get("deleted") or None, "priority": t.get("priority", "N"), "postponed": int(t.get("postponed", 0)), "estimate": t.get("estimate") or None, "tags": tags if tags else [], "notes": notes, "url": ts.get("url") or None, "location_id": ts.get("location_id") or None, "created": ts.get("created") or None, "modified": ts.get("modified") or None, } ) return tasks def parse_lists_response(result: dict[str, Any]) -> list[dict[str, Any]]: """Parse RTM lists response.""" lists = result.get("lists", {}).get("list", []) if isinstance(lists, dict): lists = [lists] return [ { "id": lst.get("id"), "name": lst.get("name"), "deleted": lst.get("deleted") == "1", "locked": lst.get("locked") == "1", "archived": lst.get("archived") == "1", "position": int(lst.get("position", -1)), "smart": lst.get("smart") == "1", "filter": lst.get("filter"), "sort_order": lst.get("sort_order"), } for lst in lists ] def get_transaction_id(result: dict[str, Any]) -> str | None: """Extract transaction ID from response for undo support.""" transaction = result.get("transaction", {}) return transaction.get("id") - src/rtm_mcp/tools/tasks.py:107-135 (schema)Input schema for add_task: name (str, required), list_name (str | None, optional), parse (bool, default True). Returns dict with task details and transaction_id.
async def add_task( ctx: Context, name: str, list_name: str | None = None, parse: bool = True, ) -> dict[str, Any]: """Add a new task. Supports Smart Add syntax when parse=True: - ^date for due date (^tomorrow, ^next friday) - !priority (!1, !2, !3) - #tag for tags (#work, #urgent) - @location - =time estimate (=30min, =1h) - *repeat pattern (*daily, *weekly) Args: name: Task name (with optional Smart Add syntax) list_name: List to add to (uses default list if not specified) parse: Parse Smart Add syntax (default: True) Returns: Created task details with transaction ID for undo Examples: - add_task("Buy groceries") - add_task("Call mom ^tomorrow !1 #family") - add_task("Weekly review *weekly ^monday", list_name="Work") """