195 lines
8.1 KiB
Markdown
195 lines
8.1 KiB
Markdown
|
|
---
|
||
|
|
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 <command> [flags]
|
||
|
|
```
|
||
|
|
|
||
|
|
> Paths above are relative to this skill's directory. From elsewhere, use the
|
||
|
|
> absolute path, e.g. `node "<skill-dir>/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 "<TASK_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 "<TASK_ID>" --status inProgress
|
||
|
|
|
||
|
|
# Complete (shorthand for --status completed)
|
||
|
|
node scripts/todo.mjs done --list "Work" --id "<TASK_ID>"
|
||
|
|
|
||
|
|
# Delete
|
||
|
|
node scripts/todo.mjs delete --list "Work" --id "<TASK_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 "<TASK_ID>"
|
||
|
|
node scripts/todo.mjs checklist-add --list "Work" --id "<TASK_ID>" --name "Draft email"
|
||
|
|
node scripts/todo.mjs checklist-check --list "Work" --id "<TASK_ID>" --item "<ITEM_ID>" # --uncheck to undo
|
||
|
|
node scripts/todo.mjs checklist-delete --list "Work" --id "<TASK_ID>" --item "<ITEM_ID>"
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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.
|