--- name: ms-todo description: >- Read and write Microsoft To Do tasks via the Microsoft Graph API using delegated (browser, auth-code + PKCE) authentication. Use this skill whenever the user wants to view, list, search, add, create, update, complete, check off, or delete their Microsoft To Do tasks, task lists, or subtasks/checklist items — even if they just say "my to-dos", "my tasks", "my task list", "add a reminder", "what's on my list", or name a specific To Do list rather than saying "Microsoft To Do" explicitly. Also use it for bulk operations over tasks (export, reporting, batch complete/clean-up) and for sign-in/connectivity troubleshooting against To Do. This is for Microsoft *To Do* (personal tasks in a user's mailbox), not Planner, Project, or SharePoint task lists. --- # Microsoft To Do Connect to Microsoft To Do and perform full CRUD (create, read, update, delete) on task lists, tasks, and checklist items (subtasks) through the Microsoft Graph API. **Authentication is delegated, not app-only.** Microsoft To Do has *no* application permission — the Graph `/me/todo` endpoints only accept a signed-in user's token. This skill uses the OAuth2 **authorization-code + PKCE** flow: `login` opens your browser, you sign in, and the refresh token is cached locally so later runs refresh silently. This is the key difference from an app-only integration like SharePoint Lists — there are no `*_CLIENT_SECRET` credentials and a human must sign in the first time. (A browser flow is used rather than device-code because device-code is often blocked by Conditional Access — AADSTS53003.) All operations go through one CLI, `scripts/todo.mjs`, which wraps the reusable core client in `scripts/lib/graph.mjs`. Run it with Node (already available): ```bash node scripts/todo.mjs [flags] ``` > Paths above are relative to this skill's directory. From elsewhere, use the > absolute path, e.g. `node "/scripts/todo.mjs" ...`. ## Prerequisites (one-time) You need an Entra (Azure AD) **public client** app registration with the delegated `Tasks.ReadWrite` permission. It requires only one value: - `TODO_CLIENT_ID` — Application (client) ID - `TODO_TENANT_ID` — optional; defaults to `common` (use a tenant id for work/school-only apps, or `consumers` for personal Microsoft accounts only) Set them as environment variables, or copy `.env.example` to `.env` in this skill's directory and fill it in (the CLI loads `.env` automatically; `.env` is git-ignored). **If the app registration is not yet set up**, do not guess — point the user to [references/setup.md](references/setup.md), a step-by-step walkthrough of creating the public-client app, adding the loopback redirect URI, adding the delegated `Tasks.ReadWrite` permission, and signing in. ## First step in any task: sign in, then verify Sign in once (interactive — the user must complete it in a browser): ```bash node scripts/todo.mjs login ``` This opens the user's browser to the Microsoft sign-in page (and prints the URL as a fallback). The user signs in and approves; the browser redirects back to a temporary local server and the refresh token is cached to `scripts/.token-cache.json` (git-ignored). Run `login` on the same machine as the browser. After that, confirm everything works: ```bash node scripts/todo.mjs test ``` A successful response shows who is signed in and the names of their task lists. If any command returns `"needsLogin": true`, the cached sign-in is missing or expired — run `login` again. ## Identifying a list - A **task list** can be given by display name (e.g. `"Groceries"`) or by its opaque list id. The client resolves names to ids and, on a typo, lists the available names so you can correct it. - To Do list ids are long opaque strings (not GUIDs), so prefer the display name unless you already have the id from a previous call. When unsure what lists exist, discover first: ```bash node scripts/todo.mjs lists ``` ## Reading tasks ```bash # Everything in a list (follows pagination): node scripts/todo.mjs tasks --list "Groceries" --all # Open tasks only, soonest due first, projected: node scripts/todo.mjs tasks --list "Work" \ --filter "status ne 'completed'" \ --select "title,status,dueDateTime,importance" \ --orderby "dueDateTime/dateTime asc" --top 50 # One task by id: node scripts/todo.mjs get --list "Work" --id "" ``` Tasks come back simplified (id, title, status, importance, due/start/reminder date-times, body, categories, timestamps). See [references/graph-api.md](references/graph-api.md) for the full property set and OData query details. ## Writing tasks Convenience flags cover the common properties; `--fields` (inline JSON, `@file.json`, or `-` for stdin) merges on top for anything else. ```bash # Create node scripts/todo.mjs create --list "Work" \ --title "Renew SSL cert" --due 2026-07-01 --importance high \ --body "Use the prod ACME account" # Update (partial — only what you pass changes) node scripts/todo.mjs update --list "Work" --id "" --status inProgress # Complete (shorthand for --status completed) node scripts/todo.mjs done --list "Work" --id "" # Delete node scripts/todo.mjs delete --list "Work" --id "" ``` `--due`, `--start`, and `--reminder` accept `2026-07-01` or a full date-time and are converted to Graph's `dateTimeTimeZone` shape using `TODO_TIMEZONE` (default `UTC`). Setting `--reminder` also turns the reminder on. **Deletes are irreversible.** Confirm the task id with the user before deleting, and prefer showing them the task via `get` first. For bulk deletes, confirm the full set once, then proceed. ## Task lists and checklist items ```bash # Lists node scripts/todo.mjs list-create --name "Trip planning" node scripts/todo.mjs list-update --list "Trip planning" --name "Italy trip" node scripts/todo.mjs list-delete --list "Italy trip" # deletes its tasks too — confirm first # Checklist items (subtasks) on a task node scripts/todo.mjs checklist --list "Work" --id "" node scripts/todo.mjs checklist-add --list "Work" --id "" --name "Draft email" node scripts/todo.mjs checklist-check --list "Work" --id "" --item "" # --uncheck to undo node scripts/todo.mjs checklist-delete --list "Work" --id "" --item "" ``` ## Bulk operations For exports or batch edits, fetch with `--all`, transform the JSON however the task needs, then loop the relevant `create`/`update`/`done`/`delete` calls. Keep concurrency modest — Graph throttles with HTTP 429; the client already retries with backoff, but don't fan out hundreds of parallel writes. ## Handling errors The CLI prints a JSON error to stderr and exits non-zero. Common cases: - `"needsLogin": true` → no cached sign-in or the refresh token expired/was revoked. Run `node scripts/todo.mjs login`. - `401`/`403` right after login → the app is missing the delegated `Tasks.ReadWrite` permission, or consent wasn't granted — see [references/setup.md](references/setup.md). - `AADSTS53003` during sign-in → a Conditional Access policy blocked it. This browser flow avoids device-code-specific blocks; if the policy requires a compliant/managed device, the device must be enrolled first. See setup. - `AADSTS50011` (redirect mismatch) → register `http://localhost` **and** `http://127.0.0.1` under the app's Mobile-and-desktop platform. See setup. - `400` on a write → usually a bad property name or value format (e.g. a date not in `dateTimeTimeZone` shape, or an invalid `status`/`importance` enum). Check [references/graph-api.md](references/graph-api.md). - `404` on a list → wrong name; run `lists` to see valid names. ## Note on graduating to an MCP server The core logic is isolated in `scripts/lib/graph.mjs` (the `MSTodoClient` class, zero dependencies). To expose this as a standalone MCP server later, import that class and expose its methods (`listTasks`, `getTask`, `createTask`, `updateTask`, `deleteTask`, list/checklist methods) as MCP tools — no rewrite of the Graph or auth logic needed. The only wrinkle vs. an app-only server: the browser `login` must be run once on the host to seed the token cache the server reads.