add_todo
Add a new task to Things 3 with title, notes, schedule, deadline, tags, checklist items, and assign to projects or headings.
Instructions
Create a new todo in Things
Args: title: Title of the todo notes: Notes for the todo when: When to schedule the todo (today, tomorrow, evening, anytime, someday, or YYYY-MM-DD). Use YYYY-MM-DD@HH:MM format to add a reminder (e.g., 2024-01-15@14:30) deadline: Deadline for the todo (YYYY-MM-DD) tags: Tags to apply to the todo checklist_items: Checklist items to add list_id: ID of project/area to add to list_title: Title of project/area to add to heading: Heading title to add under heading_id: Heading ID to add under (takes precedence over heading)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| notes | No | ||
| when | No | ||
| deadline | No | ||
| tags | No | ||
| checklist_items | No | ||
| list_id | No | ||
| list_title | No | ||
| heading | No | ||
| heading_id | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/things_mcp/server.py:348-389 (handler)MCP tool handler for add_todo. Registered with @mcp.tool decorator. Builds a Things URL via url_scheme.add_todo() and executes it via url_scheme.execute_url().
@mcp.tool async def add_todo( title: str, notes: str = None, when: str = None, deadline: str = None, tags: List[str] = None, checklist_items: List[str] = None, list_id: str = None, list_title: str = None, heading: str = None, heading_id: str = None ) -> str: """Create a new todo in Things Args: title: Title of the todo notes: Notes for the todo when: When to schedule the todo (today, tomorrow, evening, anytime, someday, or YYYY-MM-DD). Use YYYY-MM-DD@HH:MM format to add a reminder (e.g., 2024-01-15@14:30) deadline: Deadline for the todo (YYYY-MM-DD) tags: Tags to apply to the todo checklist_items: Checklist items to add list_id: ID of project/area to add to list_title: Title of project/area to add to heading: Heading title to add under heading_id: Heading ID to add under (takes precedence over heading) """ url = url_scheme.add_todo( title=title, notes=notes, when=when, deadline=deadline, tags=tags, checklist_items=checklist_items, list_id=list_id, list_title=list_title, heading=heading, heading_id=heading_id ) url_scheme.execute_url(url) return f"Created new todo: {title}" - src/things_mcp/url_scheme.py:72-112 (schema)URL construction function for add_todo. Defines all parameters and builds a 'things:///add' URL with proper encoding of tags, checklist items, and other params.
def add_todo(title: str, notes: Optional[str] = None, when: Optional[str] = None, deadline: Optional[str] = None, tags: Optional[list[str]] = None, checklist_items: Optional[list[str]] = None, list_id: Optional[str] = None, list_title: Optional[str] = None, heading: Optional[str] = None, heading_id: Optional[str] = None, completed: Optional[bool] = None) -> str: """Construct URL to add a new todo. Args: title: Title of the todo notes: Notes for the todo when: Schedule the todo. Accepts: - Keywords: "today", "tomorrow", "evening", "anytime", "someday" - Date: "yyyy-mm-dd" or natural language ("in 3 days", "next tuesday") - DateTime (adds reminder): "yyyy-mm-dd@HH:MM" (e.g., "2024-01-15@14:30") deadline: Deadline date (yyyy-mm-dd) tags: List of tag names checklist_items: List of checklist item titles list_id: UUID of project/area to add to list_title: Title of project/area to add to heading: Heading title within project heading_id: UUID of heading within project completed: Mark as completed on creation """ params = { 'title': title, 'notes': notes, 'when': when, 'deadline': deadline, 'checklist-items': '\n'.join(checklist_items) if checklist_items else None, 'list-id': list_id, 'list': list_title, 'heading': heading, 'heading-id': heading_id, 'completed': completed } # Handle tags separately since they need to be comma-separated if tags: params['tags'] = ','.join(tags) return construct_url('add', {k: v for k, v in params.items() if v is not None}) - src/things_mcp/url_scheme.py:31-41 (helper)Executes the constructed Things URL via macOS 'open -g' command with security validation to prevent opening arbitrary URLs.
def execute_url(url: str) -> None: """Execute a Things URL without bringing Things to the foreground. Security: validates the URL starts with 'things:///' before execution to prevent opening arbitrary URLs or executing unintended commands. Uses subprocess with argument list (no shell interpolation) to avoid command injection vectors. """ if not url.startswith("things:///"): raise ValueError(f"Invalid Things URL scheme: {url[:50]}") subprocess.run(['open', '-g', url], check=True, capture_output=True) - src/things_mcp/url_scheme.py:43-70 (helper)URL construction helper. Builds 'things:///{command}' with URL-encoded query parameters, handling booleans and lists.
def construct_url(command: str, params: Dict[str, Any]) -> str: """Construct a Things URL from command and parameters.""" # Start with base URL url = f"things:///{command}" # Get authentication token if needed if command in ['update', 'update-project']: token = things.token() if token: params['auth-token'] = token # URL encode parameters if params: encoded_params = [] for key, value in params.items(): if value is None: continue # Handle boolean values if isinstance(value, bool): value = str(value).lower() # Handle lists (for tags, checklist items etc) elif isinstance(value, list): value = ','.join(str(v) for v in value) encoded_params.append(f"{key}={urllib.parse.quote(str(value))}") url += "?" + "&".join(encoded_params) return url - src/things_mcp/server.py:347-349 (registration)Tool registration via @mcp.tool decorator on FastMCP instance. The decorator registers the function as an MCP tool named 'add_todo'.
# Things URL Scheme tools @mcp.tool async def add_todo(