Install
openclaw skills install microsoft-graph-todoMicrosoft To Do via Microsoft Graph. List task lists, read tasks, create tasks, update tasks, and mark tasks complete.
openclaw skills install microsoft-graph-todoUse Microsoft Graph To Do endpoints for personal task lists and tasks.
Use this skill when the user wants to:
Do not use this skill for:
client_id from an Entra app registrationtenant_id for work/school accounts, or consumers for personal Outlook/Hotmail/Live accountsTasks.ReadWriteUse the OS-native user config directory as the default. That matches common cross-platform practice and keeps secrets out of shell startup files.
Default config locations:
$XDG_CONFIG_HOME/microsoft-todo or ~/.config/microsoft-todo~/Library/Application Support/microsoft-todo%APPDATA%\\microsoft-todoUse that config directory for:
client_idtenant_idtoken.jsondevice_code.jsonAvoid putting bearer tokens or refresh tokens in:
~/.bashrc~/.zshrc.env filesFor quick local-only usage, the bundled Python helper also loads scripts/.env if it exists. That is an override path, not the primary default for published usage.
Supported environment overrides:
MS_TODO_TENANT_IDMS_TODO_CLIENT_IDMS_TODO_CONFIG_DIRMS_TODO_TOKEN_FILEMS_TODO_DEVICE_FILEMicrosoft To Do on Graph uses delegated permissions. Authenticate as a signed-in user with device-code flow.
Required delegated permissions:
Tasks.ReadTasks.ReadWriteoffline_accessCreate a Microsoft Entra app registration, then store its IDs locally:
mkdir -p ~/.config/microsoft-todo
echo "YOUR_TENANT_ID_OR_consumers" > ~/.config/microsoft-todo/tenant_id
echo "YOUR_CLIENT_ID" > ~/.config/microsoft-todo/client_id
Where to get them:
tenant_id: Azure Portal -> Microsoft Entra ID -> Overview -> Tenant IDclient_id: Azure Portal -> Microsoft Entra ID -> App registrations -> your app -> Application (client) IDconsumers instead of the directory tenant ID in auth URLsAuthentication requirements in Entra:
Authentication, set Allow public client flows to YesMobile and desktop applications, enable the platform and keep public client flows enabledAADSTS7000218 if the app is still treated as a confidential clientBundled helper:
scripts/ms_todo_auth.py handles device-code auth, token polling, refresh-token exchange, access-token output, and resolved path inspectionOptional local override file:
scripts/.env.example shows the supported variables for a portable setupscripts/.env only if you explicitly want local overridesCreate the default config directory and store the app IDs:
python3 scripts/ms_todo_auth.py show-paths
Then place:
tenant_id in the reported tenant_fileclient_id in the reported client_fileRequest a device code:
python3 scripts/ms_todo_auth.py device-code
The response includes user_code, verification_uri, and device_code, and saves the full payload to device_code.json.
Open the verification_uri, enter the user_code, sign in, then poll for tokens:
python3 scripts/ms_todo_auth.py poll-token
This saves the full token response to token.json but does not echo raw token JSON with bearer or refresh secrets to stdout.
Refresh later when needed:
python3 scripts/ms_todo_auth.py refresh-token
This also updates token.json without echoing raw token JSON secrets to stdout.
Extract the bearer token when needed:
ACCESS_TOKEN=$(python3 scripts/ms_todo_auth.py access-token)
Do not hardcode access tokens in the skill. They expire.
Example with env-var overrides:
MS_TODO_TENANT_ID=consumers \
MS_TODO_CLIENT_ID="your-client-id" \
python3 scripts/ms_todo_auth.py device-code
Example with portable local overrides in scripts/.env:
MS_TODO_TENANT_ID=consumers
MS_TODO_CLIENT_ID=your-client-id
MS_TODO_CONFIG_DIR=./state
ACCESS_TOKEN=$(python3 scripts/ms_todo_auth.py access-token)
curl -s "https://graph.microsoft.com/v1.0/me/todo/lists" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" | jq
curl -s "https://graph.microsoft.com/v1.0/me/todo/lists" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq
LIST_ID="your-list-id"
curl -s "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq
LIST_ID="your-list-id"
curl -s "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID/tasks" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq
LIST_ID="your-list-id"
curl -s -X POST "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID/tasks" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Buy milk",
"importance": "normal"
}' | jq
LIST_ID="your-list-id"
TASK_ID="your-task-id"
curl -s -X PATCH "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Buy milk and eggs",
"status": "inProgress"
}' | jq
LIST_ID="your-list-id"
TASK_ID="your-task-id"
curl -s -X PATCH "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "completed"
}' | jq
LIST_ID="your-list-id"
TASK_ID="your-task-id"
curl -i -X DELETE "https://graph.microsoft.com/v1.0/me/todo/lists/$LIST_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
tenant_id and client_id is acceptable for a personal setup, but store them in the platform config directory unless you intentionally choose scripts/.env for a local portable copy.token.json and exchange it for a new access token later.poll-token and refresh-token persist the full token payload to token.json; stdout is summarized so raw token JSON secrets are not printed.tenant_id to consumers for device-code and token requests.The skill is working when:
python3 scripts/ms_todo_auth.py show-paths reports the expected config locationspython3 scripts/ms_todo_auth.py device-code returns a user_codepython3 scripts/ms_todo_auth.py poll-token writes the platform config token.jsonGET /v1.0/me/todo/lists returns a JSON value arrayAADSTS7000218: app is not configured as a public clientAADSTS700016 on consumers: app does not allow personal Microsoft accountsMailboxNotEnabledForRESTAPI: account auth worked, but mailbox/To Do access is not usable for that account/authority combinationauthorization_pending: complete the device-code sign-in in the browser and retry token pollingBefore using this skill, do this once in Azure Portal and once on your local machine:
Microsoft To Do Skill.Accounts in any organizational directory and personal Microsoft accounts.Application (client) ID.consumers as the tenant value for this skill.Authentication page and enable Allow public client flows.Mobile and desktop applications platform section, enable it and keep the app as a public client.API permissions, add Microsoft Graph delegated permissions, and include Tasks.ReadWrite.offline_access available for refresh-token renewal during device-code auth.python3 scripts/ms_todo_auth.py show-paths to see where this skill expects local config files.tenant_id file and client_id file with your chosen tenant value and copied client ID.python3 scripts/ms_todo_auth.py device-code, open the returned verification_uri, and enter the user_code.python3 scripts/ms_todo_auth.py poll-token.token.json was written, then use python3 scripts/ms_todo_auth.py access-token or call GET /v1.0/me/todo/lists.If device-code auth fails, re-check these settings first:
consumersTasks.ReadWrite is present