plugin/ holds the plugin manifest, .mcp.json (CLAUDE_PLUGIN_ROOT paths), and README; scripts/build-plugin.sh syncs the core client, esbuild-bundles the server into a single ESM file (the plugin uploader rejects zip entries containing "@", so no node_modules), and zips it as ms-todo.plugin. Same server, two install formats (.mcpb + .plugin). Mirrors claude-msplanner. Verified: the bundled server registers all 17 tools over stdio as ms-todo v1.0.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.4 KiB
Claude-MSTodo
Connect Claude to Microsoft To Do via the Microsoft Graph API, with full read/write (CRUD) over task lists, tasks, and checklist items (subtasks).
This is a sibling to Claude-SharepointLists, built
the same way — a dependency-free core Graph client (graph.mjs) wrapped by a
small CLI and packaged as a Claude Code skill. The one fundamental
difference is authentication:
| SharePoint Lists | Microsoft To Do | |
|---|---|---|
| Auth model | App-only (client credentials) | Delegated (browser, auth-code + PKCE) |
| Why | SharePoint supports application permissions | To Do has no app-only access — /me/todo needs a user token |
| Secrets | tenant + client id + client secret | client id only (public client, no secret) |
| First run | works immediately | login once (opens browser); refresh token cached |
A browser flow is used rather than device-code because device-code is commonly blocked by Conditional Access (AADSTS53003).
Two ways to use it, sharing one core Graph client (graph.mjs):
| Form | Best for | Entry point |
|---|---|---|
| Skill | Claude Code on your own machine | .claude/skills/ms-todo/ |
Desktop extension (.mcpb) |
Claude Desktop, distributed org-wide | mcp-extension/ |
Plugin (.plugin) |
Claude Cowork / Claude Code plugin picker | plugin/ (same server, bundled by scripts/build-plugin.sh) |
Repo layout
.
├── .claude/skills/ms-todo/ # the skill (Claude Code)
│ ├── SKILL.md # how the skill works + usage
│ ├── .env.example # config template (.env is git-ignored)
│ ├── scripts/
│ │ ├── todo.mjs # CLI: login|logout|test|lists|tasks|get|create|update|done|delete|checklist…
│ │ └── lib/graph.mjs # shared, dependency-free Graph client (MSTodoClient) — single source of truth
│ └── references/
│ ├── setup.md # Entra app registration walkthrough (public client + loopback redirect)
│ └── graph-api.md # todoTask field formats + OData query reference
├── mcp-extension/ # Claude Desktop extension (imports the same graph.mjs)
│ ├── manifest.json # .mcpb manifest (baked client/tenant id, user_config)
│ ├── server/index.mjs # MCP server (stdio) exposing todo_* tools
│ ├── scripts/sync-core.mjs # copies the canonical graph.mjs into the bundle
│ └── README.md # build + install + admin setup
├── plugin/ # .plugin (Cowork/Claude Code) packaging metadata
│ ├── .claude-plugin/plugin.json # plugin manifest
│ └── .mcp.json # server launch config (CLAUDE_PLUGIN_ROOT paths)
└── scripts/build-plugin.sh # stages plugin/ + esbuild-bundled server -> ms-todo.plugin
For Claude Desktop, build and distribute the extension — see
mcp-extension/README.md. (Claude Desktop can't run
Claude Code skills; it loads MCP servers, so the .mcpb is the right vehicle.)
For Claude Cowork / Claude Code, run ./scripts/build-plugin.sh and upload the
resulting ms-todo.plugin in the plugin picker (that dialog only accepts
.zip/.plugin — a .mcpb upload is rejected).
Prerequisites (one-time)
An Entra (Azure AD) public client app registration with the delegated
Tasks.ReadWrite permission and a loopback redirect URI (http://localhost +
http://127.0.0.1) under the Mobile-and-desktop platform. The full walkthrough is in
references/setup.md. You end up
needing just one value: TODO_CLIENT_ID (and optionally TODO_TENANT_ID).
There is no client secret — To Do is a personal/delegated resource, so you sign in as yourself once and the refresh token is cached in a git-ignored file.
Quick start
cp .claude/skills/ms-todo/.env.example .claude/skills/ms-todo/.env
# set TODO_CLIENT_ID (and TODO_TENANT_ID if not "common")
cd .claude/skills/ms-todo/scripts
node todo.mjs login # open the printed URL, enter the code, approve
node todo.mjs test # -> { ok: true, signedInAs, listCount, lists: [...] }
node todo.mjs lists
node todo.mjs tasks --list "Work" --filter "status ne 'completed'" --orderby "dueDateTime/dateTime asc"
node todo.mjs create --list "Work" --title "Renew SSL cert" --due 2026-07-01 --importance high
node todo.mjs done --list "Work" --id "<TASK_ID>"
See SKILL.md for the full command set and references/graph-api.md for task field formats and OData query details.
Commands
| Command | Purpose |
|---|---|
login / logout |
Browser sign-in (auth-code+PKCE) / forget the cached sign-in |
test |
Verify auth; show signed-in user and task lists |
lists |
List all task lists |
list-create / list-update / list-delete |
Manage task lists |
tasks |
Query tasks (--filter/--select/--orderby/--top/--all) |
get |
Get one task by id |
create / update / done / delete |
Manage tasks |
checklist / checklist-add / checklist-check / checklist-delete |
Manage subtasks |
Security notes
- Delegated auth = the cached token acts as you. Anyone with read access to
scripts/.token-cache.jsoncan use your To Do. It's written0600and git-ignored — keep it that way;logoutdeletes it. - There is no client secret to leak; the public-client app id is not a secret.
- The skill is for the signed-in user's own tasks. To act as another user you'd sign in as them (or, for an MCP server, run a separate cache per account).
Graduating to an MCP server
The Graph + auth logic lives entirely in
graph.mjs (MSTodoClient,
zero dependencies), so it drops straight into an @modelcontextprotocol/sdk
server — expose listTasks/getTask/createTask/updateTask/deleteTask and
the list/checklist methods as tools. The only extra step vs. an app-only server:
run login once on the host to seed the token cache the server reads. See the
note at the bottom of
references/setup.md.