Files
claude-ms-todo/README.md
Spencer McGuire 8dc821cd07 Add .plugin packaging (Cowork/Claude Code plugin variant)
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>
2026-07-10 08:09:38 -06:00

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.json can use your To Do. It's written 0600 and git-ignored — keep it that way; logout deletes 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.