Install
openclaw skills install m365-planner-v2Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports group-based plan management, task assignment, progress tracking, and recurring tasks. Requires Azure AD app registration with Group.Read.All and Tasks.ReadWrite.All permissions.
openclaw skills install m365-planner-v2Manage Microsoft 365 Planner through Microsoft Graph API.
os.homedir() instead of hardcoded pathsos.homedir() instead of hardcoded /home/claw/.openclaw/.envmgc CLI@microsoft/microsoft-graph-client and axios# Test connection
node scripts/test-connection.js
# List all plans
node scripts/list_plans.js
# List plans for specific group
node scripts/list_plans.js <group-id>
# Create plan
node scripts/create_plan.js "Project Name" <group-id>
# Create task
node scripts/create_task.js <plan-id> <bucket-id> "Task Title"
# Delete completed tasks
node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>"
Azure Portal:
M365-Planner-IntegrationAccounts in this organizational directory onlyOr via Azure CLI:
az login
az ad app create --display-name "M365-Planner-Integration" --sign-in-audience "AzureADMyOrg"
Note the Application (client) ID from the output.
Important: Use Application Permissions (not Delegated)!
Azure Portal:
Group.Read.All (not Group.ReadWrite.All – sufficient for Planner)Tasks.ReadWrite.AllOr via Azure CLI:
APP_ID="your-app-id"
# Group.Read.All
az ad app permission add \
--id $APP_ID \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions 5b567253-7703-48e2-861c-caed61531407=Role
# Tasks.ReadWrite.All
az ad app permission add \
--id $APP_ID \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions bdfbf15f-ee85-495a-99a9-ef9b2abb1dcb=Role
# Admin Consent
az ad app permission admin-consent --id $APP_ID
Azure Portal:
OpenClaw IntegrationOr via Azure CLI:
az ad app credential reset \
--id $APP_ID \
--append \
--display-name "OpenClaw Integration"
Store credentials in ~/.openclaw/.env:
# Microsoft 365 Planner Credentials
M365_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
M365_CLIENT_SECRET="your-secret-value"
M365_TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Secure permissions:
chmod 600 ~/.openclaw/.env
node scripts/test-connection.js
Expected output:
✅ Access Token successfully received!
📋 Test: M365 Groups...
3 groups found:
- My Team ✅ M365/Planner-capable
✅ Connection successful!
Planner ONLY works with M365 Groups!
Check groups:
node scripts/test-connection.js
Shows all groups with status "✅ M365/Planner-capable".
Create M365 Group (if none exists):
All update and delete operations require the If-Match header!
Planner uses Optimistic Concurrency Control. Requests fail without ETag.
Delete Example:
// Wrong ❌
await client.api(`/planner/tasks/${taskId}`).delete();
// Correct ✅
const task = await client.api(`/planner/tasks/${taskId}`).get();
await client.api(`/planner/tasks/${taskId}`)
.headers({ 'If-Match': task['@odata.etag'] })
.delete();
Update Example:
const task = await client.api(`/planner/tasks/${taskId}`).get();
await client.api(`/planner/tasks/${taskId}`)
.headers({ 'If-Match': task['@odata.etag'] })
.update({ percentComplete: 50 });
Do NOT use:
GET /planner/plans ❌ (requires complex filter)
Use:
GET /groups/{group-id}/planner/plans ✅
Example:
const plans = await client.api(`/groups/${groupId}/planner/plans`).get();
Microsoft Planner supports native recurring tasks!
In Planner Web UI or Mobile App:
Example:
⚠️ API Limitation: The Graph API does not support creating recurring tasks directly. Recurring tasks must be set up via Planner UI.
| Operation | Script |
|---|---|
| List all plans | node scripts/list_plans.js |
| Create plan | node scripts/create_plan.js <name> <group-id> |
| Delete plan | node scripts/delete_plan.js <plan-id> |
| Operation | Script |
|---|---|
| List buckets | Integrated in list_plans.js |
| Create bucket | node scripts/create_bucket.js <plan-id> <name> |
| Delete bucket | node scripts/delete_bucket.js <bucket-id> |
| Operation | Script |
|---|---|
| List tasks | Integrated in list_plans.js |
| Create task | node scripts/create_task.js <plan-id> <bucket-id> <title> |
| Update task | node scripts/update_task.js <task-id> --percent-complete 50 |
| Delete task | node scripts/delete_task.js <task-id> |
| Cleanup | node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>" |
Tests connection to Microsoft Graph:
node scripts/test-connection.js
Shows all plans in an M365 Group with:
# Without argument: Shows all available groups
node scripts/list_plans.js
# With Group ID: Shows plans for specific group
node scripts/list_plans.js <group-id>
Creates a new plan with default buckets:
node scripts/create_plan.js "Project Alpha" <group-id>
Cleans up completed tasks from a bucket:
node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>"
Example:
node scripts/cleanup_verlaengerungen.js abc-123 "My Project" "Completed"
Cause: Admin consent not granted
Solution:
az ad app permission admin-consent --id <app-id>
Or in Azure Portal: API permissions → Grant admin consent
Cause: Planner only works with M365 Groups
Solution:
Cause: Update/Delete without ETag
Solution:
// First get task for ETag
const task = await client.api(`/planner/tasks/${id}`).get();
// Then update/delete with If-Match header
await client.api(`/planner/tasks/${id}`)
.headers({ 'If-Match': task['@odata.etag'] })
.delete();
Cause: /planner/plans endpoint requires filter
Solution: Use group-based endpoint:
// Wrong ❌
const plans = await client.api('/planner/plans').get();
// Correct ✅
const plans = await client.api(`/groups/${groupId}/planner/plans`).get();
Cause: Node.js packages not installed
Solution:
cd ~/.openclaw/workspace/skills/m365-planner
npm install
Install packages locally in skill directory:
cd ~/.openclaw/workspace/skills/m365-planner
npm install @microsoft/microsoft-graph-client axios
os.homedir() for cross-system compatibilityos.homedir() instead of hardcoded /home/claw/.openclaw/.env