Microsoft To Do MCP
by Aashish079
README.md
<p align="center">
<img src="assets/guardsix_logo.png" alt="GuardSix" width="320">
</p>
<h1 align="center">Microsoft To Do MCP</h1>
<p align="center">
A hardened, self-contained MCP server for Microsoft To Do — one-click install in Claude Desktop, no technical background required.
</p>
<p align="center">
No <code>CLIENT_SECRET</code> anywhere · No npm-registry dependency · OS-keychain token storage
</p>
---
## Contents
- [What is this?](#what-is-this)
- [Quick start — Claude Desktop](#quick-start--claude-desktop)
- [1. Install](#1-install)
- [2. About the "unverified extension" warning](#2-about-the-unverified-extension-warning)
- [3. Settings](#3-settings)
- [4. Sign in with Microsoft (first time only)](#4-sign-in-with-microsoft-first-time-only)
- [Try it out](#try-it-out)
- [One-time organization setup (Microsoft Entra)](#one-time-organization-setup-microsoft-entra)
- [Advanced setup — IT admins / other MCP clients](#advanced-setup--it-admins--other-mcp-clients)
- [Why a public client + PKCE](#why-a-public-client--pkce)
- [What's hardened here, and why](#whats-hardened-here-and-why)
- [Available tools](#available-tools)
- [Configuration reference](#configuration-reference)
- [Building the extension from source](#building-the-extension-from-source)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Known, unresolved limitations](#known-unresolved-limitations)
## What is this?
This adds a skill to Claude Desktop that lets you manage your Microsoft To Do
tasks just by asking, in plain English — things like *"What's on my to-do
list today?"* or *"Add 'call the dentist' to my list, due Friday."* Claude
reads and updates your actual Microsoft To Do account. No separate app, no
new interface to learn.
Setup has two parts:
| | Who | How long | |
|---|---|---|---|
| **[Quick start](#quick-start--claude-desktop)** | Everyone | ~2 minutes | Install the extension, sign in once |
| **[Org setup](#one-time-organization-setup-microsoft-entra)** | One admin, once | ~15 minutes | Registers the app with Microsoft — skip this if someone at your org already did it |
## Quick start — Claude Desktop
**You'll need:** Claude Desktop installed, and a Microsoft account with
Microsoft To Do — ideally a work/school Microsoft 365 account (see the
[personal accounts note](#faq) in the FAQ if you only have a personal one).
### 1. Install
Get `g6-mstodo-mcp.mcpb` from your team, then either:
- **Double-click** the file, or
- Open **Claude Desktop → Settings → Extensions** and drag the file in.
No Node.js, no terminal, no hand-edited config files — Claude Desktop bundles
its own Node runtime, and this extension ships its dependencies pre-installed.
### 2. About the "unverified extension" warning
You'll likely see:
> *"Installing will grant this extension access to everything on your
> computer. Any developer information shown has not been verified by
> Anthropic."*
This is expected and not specific to this extension — it's Claude Desktop's
standard message for **any** extension outside Anthropic's public directory,
which this one isn't (it's a private tool built for this org). It doesn't
indicate a problem. If the file came from a trusted source on your team,
it's safe to continue.
### 3. Settings
The install screen shows a few settings fields — none are required. Defaults
are pre-filled; click **Install** as-is unless your admin gave you specific
values to enter (see [org setup](#one-time-organization-setup-microsoft-entra)).
### 4. Sign in with Microsoft (first time only)
The first time you ask Claude something involving your tasks:
1. Claude opens a browser window for you.
2. Sign in with your Microsoft account.
3. Review the consent screen (it lists reading/managing your tasks and basic
profile info) and click **Accept**.
4. You'll land on a success page — close the tab and go back to Claude.
5. Ask again — it now works, and you won't need to sign in again unless you
explicitly sign out or switch accounts.
### Try it out
- *"What are my Microsoft To Do lists?"*
- *"What's on my [list name] list?"*
- *"Add a task to my list: pick up dry cleaning, due tomorrow."*
- *"Mark [task] as complete."*
- *"Create a new list called Groceries."*
Deleting something? Claude will describe what it's about to delete and ask
you to confirm first — nothing is removed silently.
## One-time organization setup (Microsoft Entra)
<details>
<summary><strong>Expand if your organization hasn't registered this app with Microsoft yet</strong> (one person does this once — most readers can skip straight to <a href="#quick-start--claude-desktop">Quick start</a>)</summary>
This registers the app as a known, trusted app with Microsoft so it's allowed
to ask people to sign in.
**1. Open the Microsoft Entra admin center**
Go to [entra.microsoft.com](https://entra.microsoft.com) and sign in with an
account that can manage app registrations for your organization.
**2. Start a new app registration**
1. Go to **Identity → Applications → App registrations → New registration**.
2. Under **Name**, enter something recognizable, e.g. `Microsoft To Do MCP`.
3. Under **Supported account types**, choose **Accounts in any organizational
directory (Any Microsoft Entra ID tenant)** — matches this extension's
default `organizations` tenant setting. (Restricting to your own tenant
only is fine too — just note the Tenant ID from the Overview page for
step 6.)
4. Leave **Redirect URI** blank for now, then click **Register**.
**3. Copy the Application (client) ID**
On the app's **Overview** page, copy the **Application (client) ID** (looks
like `4fdb1079-ff55-434a-bec9-c1f31d9daad4`) — you'll need it in step 6.
**4. Allow sign-in from a desktop app**
1. Under **Manage**, click **Authentication → Add a platform**.
2. Choose **Mobile and desktop applications**.
3. Under **Custom redirect URIs**, add both of these (one for the Claude
Desktop flow, one for the advanced/manual flow below):
```
http://localhost:51823/callback
http://localhost:3000/callback
```
4. Click **Configure**.
> **No client secret to create.** There's intentionally no "Certificates &
> secrets" step — see [Why a public client + PKCE](#why-a-public-client--pkce).
**5. Grant the permissions it needs**
1. Click **API permissions → Add a permission → Microsoft Graph → Delegated permissions**.
2. Search for and check **Tasks.Read** and **Tasks.ReadWrite**, then **Add permissions**.
3. If a **Grant admin consent for [org]** button appears, click it — optional,
but it saves each person one extra click on their first sign-in.
**6. Hand out what you've got**
| What | Where | Needed if |
|---|---|---|
| **Application (client) ID** | Overview page | Always — share with whoever installs the extension |
| **Directory (tenant) ID** | Overview page | Only if you chose "this organizational directory only" above |
If your build of `g6-mstodo-mcp.mcpb` already has your Application ID baked
in as the default, you can skip handing this out entirely.
</details>
## Advanced setup — IT admins / other MCP clients
The original manual flow still works for anything that isn't Claude Desktop
(e.g. Cursor), or if you'd rather manage configuration by hand.
```bash
npm install
npm run build
```
**1. Configure Azure** — create a `.env` file:
```
CLIENT_ID=your_client_id
TENANT_ID=your_tenant_id_or_organizations_or_consumers_or_common
```
No `CLIENT_SECRET` — see [Why a public client + PKCE](#why-a-public-client--pkce).
**2. Authenticate:**
```bash
npm run auth
```
Opens a browser to `http://localhost:3000`. Sign in, grant access, done — the
server saves your session and shuts itself down automatically a few seconds
later (or after 10 minutes of inactivity if you don't finish).
**3. Point your MCP client at it:**
```bash
npm run create-config
```
Writes `mcp.json` with a `g6-mstodo` entry pointing at `build/cli.js` — merge
into your client's MCP config. It never embeds live tokens; `build/cli.js`
reads them from the same secure storage `npm run auth` just wrote to.
## Why a public client + PKCE
Earlier versions of this project used a confidential OAuth client — a
`CLIENT_SECRET` typed into a `.env` file or settings field. For a tool
installed on many machines, a "confidential" secret that ends up on every
installation isn't really confidential. This build uses a **public client
with PKCE** instead (`PublicClientApplication` in `@azure/msal-node`; a fresh
`code_verifier`/`code_challenge` pair generated per sign-in — no secret,
ever).
| Flow | Redirect URI | Trigger |
|---|---|---|
| Embedded (Claude Desktop / MCPB) | `http://localhost:51823/callback` | Automatic, on first tool call that needs a token |
| Standalone / advanced | `http://localhost:3000/callback` | Manual, via `npm run auth` |
(Embedded port configurable via the `oauth_port` setting in Claude Desktop, or `MSTODO_OAUTH_PORT` in the manual flow.)
## What's hardened here, and why
This build was hardened following a security review of the original codebase.
| Hardening | Detail |
|---|---|
| **Token storage** | OS keychain (macOS Keychain / Windows DPAPI / Linux `secret-tool`), falling back to a `chmod 600` file. Stored per-user (`~/Library/Application Support/g6-mstodo-mcp/` on macOS, `%APPDATA%\g6-mstodo-mcp\` on Windows, `~/.config/g6-mstodo-mcp/` on Linux) — not next to the installed extension's files, since an extension directory isn't guaranteed writable or to survive an update. |
| **No client secret** | Public client + PKCE — see above. |
| **Narrower scopes by default** | Only `Tasks.Read` + `Tasks.ReadWrite` — not `Tasks.*.Shared`, which would let the LLM touch tasks others shared with you. Opt in via the `include_shared` setting / `MSTODO_INCLUDE_SHARED=true`. |
| **Confirmation on deletes** | `delete-task`, `delete-task-list`, `delete-checklist-item` require `confirm: true`, or they describe what *would* be deleted instead of deleting it. All tools carry `destructiveHint`/`readOnlyHint` annotations for clients with their own approval UI. |
| **Prompt-injection framing** | Content read back from Graph (list names, task titles/bodies, checklist items) is explicitly framed as untrusted reference data before reaching the LLM — a mitigation, not a guarantee. |
| **Lazy, singleton sign-in** | The embedded flow starts only when a tool call needs a token, and is safe against Claude Desktop spawning multiple copies of the server: whichever process binds the sign-in port first serves it, the rest wait, since every process reads the same shared token storage. |
| **Quieter logging** | Full response bodies and account email are off by default. Enable via `debug_logging` / `MSTODO_DEBUG=true` only when troubleshooting. |
| **Local audit trail** | Every tool call appended to `audit.log` — tool name, timestamp, ID fields only, never task content. Disable via `audit_log` / `MSTODO_AUDIT_LOG=false`. |
| **Sign-in listener lockdown** | Binds `127.0.0.1` only; `state` + PKCE `code_verifier` verified on callback (CSRF); a random per-run token gates the `/refresh` route; auto-shuts-down after success or idle timeout. |
## Available tools
| Tool | Description |
|---|---|
| `auth-status` | Check sign-in status and token expiry |
| `get-task-lists` | List all task lists |
| `create-task-list` | Create a task list |
| `update-task-list` | Rename a task list |
| `delete-task-list` | Delete a task list and everything in it — requires `confirm: true` |
| `get-tasks` | List tasks in a list (supports OData filter/sort/paging) |
| `create-task` | Create a task |
| `update-task` | Update a task's fields |
| `delete-task` | Delete a task — requires `confirm: true` |
| `get-checklist-items` | List a task's checklist items (subtasks) |
| `create-checklist-item` | Add a checklist item |
| `update-checklist-item` | Edit or check off a checklist item |
| `delete-checklist-item` | Delete a checklist item — requires `confirm: true` |
## Configuration reference
| Manual-flow env var | Claude Desktop setting | Default | Purpose |
|---|---|---|---|
| `CLIENT_ID` | Azure App (client) ID | GuardSix's app | Public-client Azure App Registration ID |
| `TENANT_ID` | Microsoft tenant | `organizations` | Which accounts can sign in |
| `MSTODO_INCLUDE_SHARED` | Include lists shared with me | `false` | Request `Tasks.*.Shared` scopes too |
| `MSTODO_DEBUG` | Verbose debug logging | `false` | May include account email / response content |
| `MSTODO_AUDIT_LOG` | Keep a local audit log | `true` | Local content-free audit trail of tool calls |
| `MSTODO_OAUTH_PORT` | Sign-in listener port (advanced) | `51823` | Embedded flow's local sign-in port |
| `MSTODO_TOKEN_FILE` | — | per-user app-data dir | Override the token storage path |
| `MSTODO_ACCESS_TOKEN` / `MSTODO_REFRESH_TOKEN` | — | — | Inject tokens directly, bypassing storage |
| `MSTODO_AUTH_IDLE_TIMEOUT_MS` | — | `600000` (10 min) | How long standalone `npm run auth` waits before auto-exiting |
## Building the extension from source
```bash
npm install -g @anthropic-ai/mcpb # one-time
npm run package:mcpb
```
Produces `g6-mstodo-mcp.mcpb` at the project root:
| File | Purpose |
|---|---|
| `mcpb/manifest.json` | Extension metadata and the settings shown during install |
| `mcpb/icon.png` | Extension icon (the GuardSix "g6" mark) |
| `scripts/build-mcpb.sh` | Builds, stages, and packs the bundle — exactly what's included |
## Troubleshooting
**Claude said it opened a browser, but nothing seems to have happened.**
Check for a new tab or window (sometimes behind existing ones). If you don't
see one, go to `http://localhost:51823/` directly.
**Claude says it's "still waiting" for me to sign in.**
The sign-in window is open somewhere — finish there, then ask again. If you
truly don't see one, use the direct link above.
**I got an error during Microsoft sign-in about the app or redirect URI.**
The [one-time org setup](#one-time-organization-setup-microsoft-entra) likely
hasn't been completed, or a redirect URI doesn't match exactly — check with
your admin.
## FAQ
**Is this safe to install?**
Yes, when the file comes from a trusted source on your team. The "unverified
extension" warning is generic to any extension outside Anthropic's public
directory — see [step 2 of Quick start](#2-about-the-unverified-extension-warning).
**Does this cost anything?**
No. Using the extension doesn't add any cost beyond whatever Claude and
Microsoft 365 plans you already have. The Azure App Registration and
Microsoft Graph API calls it makes are free. (Code-signing this extension
would cost a nominal yearly fee — that's a deliberate, deferred decision, not
something you're paying for today.)
**I only have a personal Microsoft account (outlook.com, hotmail.com, etc.) — will this work?**
Possibly not. You may see an error mentioning `MailboxNotEnabledForRESTAPI`
— a Microsoft limitation, not a bug here. Microsoft only fully supports the
To Do API for Microsoft 365 (work/school) accounts.
## Known, unresolved limitations
- **Prompt injection** can't be fully solved at the tool layer — the framing in the hardening table above is a mitigation, not a guarantee.
- **DLP/CASB bypass** is inherent to any tool that pulls Microsoft 365 data into a local LLM's context. The audit log is a compensating control (visibility after the fact), not a preventive one.
- **Unsigned extension.** `g6-mstodo-mcp.mcpb` isn't code-signed, so Claude Desktop shows an "installing unsigned extension" warning (not a hard block). Revisit signing (`mcpb sign`) if usage grows enough to justify the Apple/Windows signing costs.
---
<p align="center"><sub>GuardSix</sub></p>
TDQS
A4.1/5.0
Scored across 13 tools
Disambiguation5/5
Each tool targets a distinct combination of resource (task list, task, checklist item) and action (get, create, update, delete, auth), with no overlapping purposes.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern using lowercase and hyphens, with clear pluralization for retrieval and singular for mutations.
Tool Count5/5
13 tools cover CRUD for three entities plus authentication, which is well-scoped for a task management server without being excessive or too lean.
Completeness4/5
The set covers most CRUD operations but lacks a dedicated tool to retrieve a single task or task list by ID, which is a minor gap.
Maintenance
ActivitySlowing
ResponsivenessNo issues