@softeria/ms-365-mcp-server 0.161.4 → 0.163.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -809,16 +809,21 @@ is refused with the same 404 as any other bad ticket.
809
809
 
810
810
  ```
811
811
  MS365_MCP_ATTACHMENT_URL_BASE=http://m365-mcp:3000 # required
812
- MS365_MCP_ATTACHMENT_URL_KEY=... # required (or _KEY_FILE=/path)
813
- MS365_MCP_ATTACHMENT_URL_KEY_ID=1 # optional, default 1
812
+ MS365_MCP_ATTACHMENT_URL_KEY=... # optional (or _KEY_FILE=/path), see below
813
+ MS365_MCP_ATTACHMENT_URL_KEY_ID=1 # optional, default 1; needs a key
814
814
  MS365_MCP_ATTACHMENT_URL_TTL_S=120 # optional, default 120, max 300
815
815
  ```
816
816
 
817
817
  `MS365_MCP_ATTACHMENT_URL_BASE` is deliberately **not** `MS365_MCP_PUBLIC_URL`: that one is
818
818
  browser-facing, for OAuth redirects, while this is fetched server-to-server and is
819
819
  commonly a container address. A missing or malformed setting fails at startup rather than
820
- per-request — a signing feature that comes up without a key would mint URLs nothing can
821
- verify, silently.
820
+ per-request.
821
+
822
+ The signing key is optional. Without one, minted URLs are just `/attachment?t=<ticket>` and
823
+ any HTTP client can fetch them; the ticket alone authorises redemption. Set a key only if
824
+ the fetcher verifies the `dgk`/`dgx`/`dgs` signature (see below), such as a docglean-mcp
825
+ sidecar, which will refuse an unsigned URL. Setting `_KEY_ID` without a key, or a `_KEY_FILE` that is
826
+ empty or unreadable, fails at startup.
822
827
 
823
828
  ### Splitting the attachment listener
824
829
 
@@ -904,9 +909,10 @@ directly.
904
909
  `--attachment-host` takes a bare IPv4 address, IPv6 address (bracketed `[::1]` or bare
905
910
  `::1`) or hostname. It is refused rather than coerced — `--attachment-host 10.0.0.5:3001`
906
911
  is an error naming `--attachment-port`, not a bind to something else. Note that
907
- `MS365_MCP_ATTACHMENT_URL_BASE` still must not be an IPv6 literal (the URL signature covers
908
- the host and the two implementations normalise IPv6 differently); if you bind the listener
909
- to an IPv6 address, name it in the base by hostname.
912
+ with a signing key set, `MS365_MCP_ATTACHMENT_URL_BASE` must not be an IPv6 literal (the URL
913
+ signature covers the host and the two implementations normalise IPv6 differently); if you
914
+ bind the listener to an IPv6 address, name it in the base by hostname. Without a key there
915
+ is no such restriction.
910
916
 
911
917
  Point `MS365_MCP_ATTACHMENT_URL_BASE` at the attachment port. The server cannot check this
912
918
  for you: the base is usually a container name on a network this process cannot resolve, so
@@ -915,7 +921,7 @@ base and the bound port are logged at startup, one line apart, for exactly that
915
921
 
916
922
  ### The signature, and who checks what
917
923
 
918
- `dgk`/`dgx`/`dgs` are **not** checked by this server on redemption, and that is deliberate.
924
+ This server never checks `dgk`/`dgx`/`dgs` on redemption, and that is deliberate.
919
925
  They exist for the fetcher: a document-conversion sidecar that refuses to dial a private
920
926
  address unless the URL carries a valid HMAC from an origin it has been configured to trust.
921
927
  What authorises redemption _here_ is the ticket. Verifying the signature on the way back in
package/dist/cli.js CHANGED
@@ -29,7 +29,7 @@ program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").versio
29
29
  "Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)"
30
30
  ).option(
31
31
  "--enable-attachment-urls",
32
- "HTTP mode only. Let get-download-url mint a short-TTL, single-use URL served by this server for Graph byte resources that expose no pre-authenticated URL of their own (mail and event attachments, meeting recordings, other $value endpoints). Also adds get-download-url to any --preset that lacks it (except teams-write). Requires MS365_MCP_ATTACHMENT_URL_BASE and MS365_MCP_ATTACHMENT_URL_KEY (or _KEY_FILE)"
32
+ "HTTP mode only. Let get-download-url mint a short-TTL, single-use URL served by this server for Graph byte resources that expose no pre-authenticated URL of their own (mail and event attachments, meeting recordings, other $value endpoints). Also adds get-download-url to any --preset that lacks it (except teams-write). Requires MS365_MCP_ATTACHMENT_URL_BASE; MS365_MCP_ATTACHMENT_URL_KEY (or _KEY_FILE) is optional and adds an HMAC signature for fetchers that verify one"
33
33
  ).option(
34
34
  "--attachment-port <port>",
35
35
  "HTTP mode only. Serve the attachment download route on its own listener on this port instead of on the MCP app, so a caller that can fetch attachments cannot also reach /mcp. Requires --enable-attachment-urls, and MS365_MCP_ATTACHMENT_URL_BASE must name this port. A separate port only isolates the two surfaces if they also bind separate interfaces \u2014 see --attachment-host. Equivalent env var: MS365_MCP_ATTACHMENT_PORT."
@@ -1322,7 +1322,7 @@
1322
1322
  "toolName": "create-todo-task",
1323
1323
  "presets": ["personal", "tasks"],
1324
1324
  "scopes": ["Tasks.ReadWrite"],
1325
- "llmTip": "Creates a new task in a Microsoft To Do list. Body: { title: \"...\" }; optional dueDateTime, reminderDateTime, importance, body (notes), recurrence, categories. Requires todoTaskListId from list-todo-task-lists."
1325
+ "llmTip": "Creates a new task in a Microsoft To Do list. Body: { title: \"...\" }; optional dueDateTime, reminderDateTime, importance, body (notes), recurrence, categories, and checklistItems: [{ displayName: \"...\", isChecked: false }] to create the task's steps inline (isChecked is optional). Requires todoTaskListId from list-todo-task-lists. To change steps after the task exists, use the *-todo-checklist-item tools."
1326
1326
  },
1327
1327
  {
1328
1328
  "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
@@ -1330,7 +1330,7 @@
1330
1330
  "toolName": "update-todo-task",
1331
1331
  "presets": ["personal", "tasks"],
1332
1332
  "scopes": ["Tasks.ReadWrite"],
1333
- "llmTip": "Updates a Microsoft To Do task. Use this to mark a to-do item complete or done (body: { status: \"completed\" }), reopen it (status: \"notStarted\"), rename it (title), or change its due date (dueDateTime), reminder (reminderDateTime), importance, or notes (body). Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks."
1333
+ "llmTip": "Updates a Microsoft To Do task. Use this to mark a to-do item complete or done (body: { status: \"completed\" }), reopen it (status: \"notStarted\"), rename it (title), or change its due date (dueDateTime), reminder (reminderDateTime), importance, or notes (body). Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks. Steps (checklistItems) can't be changed here: Graph rejects checklistItems in a PATCH on the task with 400. Use create-todo-checklist-item, update-todo-checklist-item, or delete-todo-checklist-item instead."
1334
1334
  },
1335
1335
  {
1336
1336
  "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
@@ -1363,6 +1363,38 @@
1363
1363
  "scopes": ["Tasks.ReadWrite"],
1364
1364
  "llmTip": "Removes a linked resource from a To Do task."
1365
1365
  },
1366
+ {
1367
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems",
1368
+ "method": "get",
1369
+ "toolName": "list-todo-checklist-items",
1370
+ "presets": ["personal", "tasks"],
1371
+ "scopes": ["Tasks.Read"],
1372
+ "llmTip": "Lists the steps (checklist items) of a To Do task. Each item has id, displayName, isChecked, checkedDateTime, and createdDateTime. Graph supports no OData query parameters on this endpoint, so do not pass select, filter, orderby, or top. Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks."
1373
+ },
1374
+ {
1375
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems",
1376
+ "method": "post",
1377
+ "toolName": "create-todo-checklist-item",
1378
+ "presets": ["personal", "tasks"],
1379
+ "scopes": ["Tasks.ReadWrite"],
1380
+ "llmTip": "Adds a step (checklist item) to an existing To Do task. Body: { displayName: \"...\" }; optional isChecked (defaults to false). Returns the new item with its id. To add steps while creating a task, pass checklistItems in the create-todo-task body instead."
1381
+ },
1382
+ {
1383
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems/{checklistItem-id}",
1384
+ "method": "patch",
1385
+ "toolName": "update-todo-checklist-item",
1386
+ "presets": ["personal", "tasks"],
1387
+ "scopes": ["Tasks.ReadWrite"],
1388
+ "llmTip": "Updates a step (checklist item) of a To Do task. Use this to check a step off (body: { isChecked: true }), uncheck it (isChecked: false), or rename it (displayName). Requires checklistItemId from list-todo-checklist-items."
1389
+ },
1390
+ {
1391
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems/{checklistItem-id}",
1392
+ "method": "delete",
1393
+ "toolName": "delete-todo-checklist-item",
1394
+ "presets": ["personal", "tasks"],
1395
+ "scopes": ["Tasks.ReadWrite"],
1396
+ "llmTip": "Deletes a step (checklist item) from a To Do task. This cannot be undone. Requires checklistItemId from list-todo-checklist-items."
1397
+ },
1366
1398
  {
1367
1399
  "pathPattern": "/me/planner/tasks",
1368
1400
  "method": "get",
@@ -4148,6 +4148,11 @@ const microsoft_graph_todoTaskCollectionResponse = z.object({
4148
4148
  "@odata.nextLink": z.string().nullable(),
4149
4149
  value: z.array(microsoft_graph_todoTask)
4150
4150
  }).partial().passthrough();
4151
+ const microsoft_graph_checklistItemCollectionResponse = z.object({
4152
+ "@odata.count": z.number().int().nullable(),
4153
+ "@odata.nextLink": z.string().nullable(),
4154
+ value: z.array(microsoft_graph_checklistItem)
4155
+ }).partial().passthrough();
4151
4156
  const microsoft_graph_linkedResourceCollectionResponse = z.object({
4152
4157
  "@odata.count": z.number().int().nullable(),
4153
4158
  "@odata.nextLink": z.string().nullable(),
@@ -5295,6 +5300,7 @@ const schemas = {
5295
5300
  microsoft_graph_todoTaskList,
5296
5301
  microsoft_graph_todoTaskListCollectionResponse,
5297
5302
  microsoft_graph_todoTaskCollectionResponse,
5303
+ microsoft_graph_checklistItemCollectionResponse,
5298
5304
  microsoft_graph_linkedResourceCollectionResponse,
5299
5305
  microsoft_graph_checkInMethod,
5300
5306
  microsoft_graph_checkInClaim,
@@ -12347,6 +12353,103 @@ Currently, a user can be associated with a team in two different ways:`,
12347
12353
  ],
12348
12354
  response: z.void()
12349
12355
  },
12356
+ {
12357
+ method: "get",
12358
+ path: "/me/todo/lists/:todoTaskListId/tasks/:todoTaskId/checklistItems",
12359
+ alias: "list-todo-checklist-items",
12360
+ description: `Get the checklistItem resources associated to a todoTask from the checklistItems navigation property.`,
12361
+ requestFormat: "json",
12362
+ parameters: [
12363
+ {
12364
+ name: "$top",
12365
+ type: "Query",
12366
+ schema: z.number().int().gte(0).describe("Show only the first n items").optional()
12367
+ },
12368
+ {
12369
+ name: "$skip",
12370
+ type: "Query",
12371
+ schema: z.number().int().gte(0).describe("Skip the first n items").optional()
12372
+ },
12373
+ {
12374
+ name: "$search",
12375
+ type: "Query",
12376
+ schema: z.string().describe("Search items by search phrases").optional()
12377
+ },
12378
+ {
12379
+ name: "$filter",
12380
+ type: "Query",
12381
+ schema: z.string().describe("Filter items by property values").optional()
12382
+ },
12383
+ {
12384
+ name: "$count",
12385
+ type: "Query",
12386
+ schema: z.boolean().describe("Include count of items").optional()
12387
+ },
12388
+ {
12389
+ name: "$orderby",
12390
+ type: "Query",
12391
+ schema: z.array(z.string()).describe("Order items by property values").optional()
12392
+ },
12393
+ {
12394
+ name: "$select",
12395
+ type: "Query",
12396
+ schema: z.array(z.string()).describe("Select properties to be returned").optional()
12397
+ },
12398
+ {
12399
+ name: "$expand",
12400
+ type: "Query",
12401
+ schema: z.array(z.string()).describe("Expand related entities").optional()
12402
+ }
12403
+ ],
12404
+ response: z.void()
12405
+ },
12406
+ {
12407
+ method: "post",
12408
+ path: "/me/todo/lists/:todoTaskListId/tasks/:todoTaskId/checklistItems",
12409
+ alias: "create-todo-checklist-item",
12410
+ description: `Create a new checklistItem object.`,
12411
+ requestFormat: "json",
12412
+ parameters: [
12413
+ {
12414
+ name: "body",
12415
+ description: `New navigation property`,
12416
+ type: "Body",
12417
+ schema: microsoft_graph_checklistItem
12418
+ }
12419
+ ],
12420
+ response: z.void()
12421
+ },
12422
+ {
12423
+ method: "patch",
12424
+ path: "/me/todo/lists/:todoTaskListId/tasks/:todoTaskId/checklistItems/:checklistItemId",
12425
+ alias: "update-todo-checklist-item",
12426
+ description: `Update the properties of a checklistItem object.`,
12427
+ requestFormat: "json",
12428
+ parameters: [
12429
+ {
12430
+ name: "body",
12431
+ description: `New navigation property values`,
12432
+ type: "Body",
12433
+ schema: microsoft_graph_checklistItem
12434
+ }
12435
+ ],
12436
+ response: z.void()
12437
+ },
12438
+ {
12439
+ method: "delete",
12440
+ path: "/me/todo/lists/:todoTaskListId/tasks/:todoTaskId/checklistItems/:checklistItemId",
12441
+ alias: "delete-todo-checklist-item",
12442
+ description: `Delete a checklistItem object.`,
12443
+ requestFormat: "json",
12444
+ parameters: [
12445
+ {
12446
+ name: "If-Match",
12447
+ type: "Header",
12448
+ schema: z.string().describe("ETag").optional()
12449
+ }
12450
+ ],
12451
+ response: z.void()
12452
+ },
12350
12453
  {
12351
12454
  method: "get",
12352
12455
  path: "/me/todo/lists/:todoTaskListId/tasks/:todoTaskId/linkedResources",
@@ -5,6 +5,7 @@ const TICKET_PARAM = "t";
5
5
  function buildAttachmentUrl(config, ticketId, nowMs = Date.now()) {
6
6
  const url = new URL(ATTACHMENT_ROUTE, config.base);
7
7
  url.searchParams.set(TICKET_PARAM, ticketId);
8
+ if (!config.key) return url.toString();
8
9
  return signUrl(
9
10
  url.toString(),
10
11
  { key: config.key, keyId: config.keyId, ttlSeconds: config.ttlSeconds },
@@ -19,13 +19,20 @@ function pythonStrip(value) {
19
19
  function readKey(env) {
20
20
  const keyFile = env.MS365_MCP_ATTACHMENT_URL_KEY_FILE;
21
21
  if (keyFile) {
22
+ let key;
22
23
  try {
23
- return pythonStrip(readFileSync(keyFile, "utf8"));
24
+ key = pythonStrip(readFileSync(keyFile, "utf8"));
24
25
  } catch {
25
26
  throw new AttachmentUrlConfigError(
26
27
  `MS365_MCP_ATTACHMENT_URL_KEY_FILE could not be read: ${keyFile}`
27
28
  );
28
29
  }
30
+ if (!key) {
31
+ throw new AttachmentUrlConfigError(
32
+ `MS365_MCP_ATTACHMENT_URL_KEY_FILE is empty: ${keyFile}. Unset it to mint unsigned URLs.`
33
+ );
34
+ }
35
+ return key;
29
36
  }
30
37
  return env.MS365_MCP_ATTACHMENT_URL_KEY ?? "";
31
38
  }
@@ -50,20 +57,26 @@ function loadAttachmentUrlConfig(enabled, env = process.env) {
50
57
  `MS365_MCP_ATTACHMENT_URL_BASE must be http or https, got ${parsedBase.protocol}`
51
58
  );
52
59
  }
53
- if (parsedBase.hostname.startsWith("[")) {
54
- throw new AttachmentUrlConfigError(
55
- `MS365_MCP_ATTACHMENT_URL_BASE must not be an IPv6 literal: the URL signature covers the host, and this runtime and the verifying sidecar normalise IPv6 spellings differently, so every minted URL would be refused. Use a hostname (a container or service name) instead of ${parsedBase.hostname}.`
56
- );
57
- }
58
60
  if (parsedBase.search || parsedBase.hash) {
59
61
  throw new AttachmentUrlConfigError(
60
62
  "MS365_MCP_ATTACHMENT_URL_BASE must not carry a query string or fragment."
61
63
  );
62
64
  }
63
65
  const key = readKey(env);
64
- if (!key) {
66
+ if (key && !key.trim()) {
67
+ throw new AttachmentUrlConfigError(
68
+ "MS365_MCP_ATTACHMENT_URL_KEY is whitespace only. Set a real key, or unset it to mint unsigned URLs."
69
+ );
70
+ }
71
+ if (key) {
72
+ if (parsedBase.hostname.startsWith("[")) {
73
+ throw new AttachmentUrlConfigError(
74
+ `MS365_MCP_ATTACHMENT_URL_BASE must not be an IPv6 literal: the URL signature covers the host, and this runtime and the verifying sidecar normalise IPv6 spellings differently, so every minted URL would be refused. Use a hostname (a container or service name) instead of ${parsedBase.hostname}.`
75
+ );
76
+ }
77
+ } else if (env.MS365_MCP_ATTACHMENT_URL_KEY_ID) {
65
78
  throw new AttachmentUrlConfigError(
66
- "--enable-attachment-urls requires MS365_MCP_ATTACHMENT_URL_KEY or MS365_MCP_ATTACHMENT_URL_KEY_FILE -- the HMAC key shared with the sidecar that will verify the minted URL."
79
+ "MS365_MCP_ATTACHMENT_URL_KEY_ID is set but no signing key is: set MS365_MCP_ATTACHMENT_URL_KEY or MS365_MCP_ATTACHMENT_URL_KEY_FILE, or unset the key id."
67
80
  );
68
81
  }
69
82
  for (let index = 0; index < key.length; index += 1) {
@@ -92,7 +105,7 @@ function loadAttachmentUrlConfig(enabled, env = process.env) {
92
105
  }
93
106
  return {
94
107
  base: parsedBase.origin,
95
- key,
108
+ key: key || void 0,
96
109
  keyId,
97
110
  ttlSeconds
98
111
  };
package/dist/server.js CHANGED
@@ -730,7 +730,7 @@ class MicrosoftGraphServer {
730
730
  attachmentApp.get(ATTACHMENT_ROUTE, createAttachmentHandler(attachmentDeps));
731
731
  attachmentApp.put(ATTACHMENT_ROUTE, createAttachmentUploadHandler(attachmentDeps));
732
732
  logger.info(
733
- ` - Attachment URLs: ${attachmentConfig.base}${ATTACHMENT_ROUTE} (ttl ${attachmentConfig.ttlSeconds}s, key id ${attachmentConfig.keyId})`
733
+ ` - Attachment URLs: ${attachmentConfig.base}${ATTACHMENT_ROUTE} (ttl ${attachmentConfig.ttlSeconds}s, ${attachmentConfig.key ? `signed, key id ${attachmentConfig.keyId}` : "unsigned"})`
734
734
  );
735
735
  if (dedicated) {
736
736
  logger.info(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
3
  "mcpName": "io.github.Softeria/ms-365-mcp-server",
4
- "version": "0.161.4",
4
+ "version": "0.163.0",
5
5
  "description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -1322,7 +1322,7 @@
1322
1322
  "toolName": "create-todo-task",
1323
1323
  "presets": ["personal", "tasks"],
1324
1324
  "scopes": ["Tasks.ReadWrite"],
1325
- "llmTip": "Creates a new task in a Microsoft To Do list. Body: { title: \"...\" }; optional dueDateTime, reminderDateTime, importance, body (notes), recurrence, categories. Requires todoTaskListId from list-todo-task-lists."
1325
+ "llmTip": "Creates a new task in a Microsoft To Do list. Body: { title: \"...\" }; optional dueDateTime, reminderDateTime, importance, body (notes), recurrence, categories, and checklistItems: [{ displayName: \"...\", isChecked: false }] to create the task's steps inline (isChecked is optional). Requires todoTaskListId from list-todo-task-lists. To change steps after the task exists, use the *-todo-checklist-item tools."
1326
1326
  },
1327
1327
  {
1328
1328
  "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
@@ -1330,7 +1330,7 @@
1330
1330
  "toolName": "update-todo-task",
1331
1331
  "presets": ["personal", "tasks"],
1332
1332
  "scopes": ["Tasks.ReadWrite"],
1333
- "llmTip": "Updates a Microsoft To Do task. Use this to mark a to-do item complete or done (body: { status: \"completed\" }), reopen it (status: \"notStarted\"), rename it (title), or change its due date (dueDateTime), reminder (reminderDateTime), importance, or notes (body). Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks."
1333
+ "llmTip": "Updates a Microsoft To Do task. Use this to mark a to-do item complete or done (body: { status: \"completed\" }), reopen it (status: \"notStarted\"), rename it (title), or change its due date (dueDateTime), reminder (reminderDateTime), importance, or notes (body). Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks. Steps (checklistItems) can't be changed here: Graph rejects checklistItems in a PATCH on the task with 400. Use create-todo-checklist-item, update-todo-checklist-item, or delete-todo-checklist-item instead."
1334
1334
  },
1335
1335
  {
1336
1336
  "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
@@ -1363,6 +1363,38 @@
1363
1363
  "scopes": ["Tasks.ReadWrite"],
1364
1364
  "llmTip": "Removes a linked resource from a To Do task."
1365
1365
  },
1366
+ {
1367
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems",
1368
+ "method": "get",
1369
+ "toolName": "list-todo-checklist-items",
1370
+ "presets": ["personal", "tasks"],
1371
+ "scopes": ["Tasks.Read"],
1372
+ "llmTip": "Lists the steps (checklist items) of a To Do task. Each item has id, displayName, isChecked, checkedDateTime, and createdDateTime. Graph supports no OData query parameters on this endpoint, so do not pass select, filter, orderby, or top. Requires todoTaskListId from list-todo-task-lists and todoTaskId from list-todo-tasks."
1373
+ },
1374
+ {
1375
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems",
1376
+ "method": "post",
1377
+ "toolName": "create-todo-checklist-item",
1378
+ "presets": ["personal", "tasks"],
1379
+ "scopes": ["Tasks.ReadWrite"],
1380
+ "llmTip": "Adds a step (checklist item) to an existing To Do task. Body: { displayName: \"...\" }; optional isChecked (defaults to false). Returns the new item with its id. To add steps while creating a task, pass checklistItems in the create-todo-task body instead."
1381
+ },
1382
+ {
1383
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems/{checklistItem-id}",
1384
+ "method": "patch",
1385
+ "toolName": "update-todo-checklist-item",
1386
+ "presets": ["personal", "tasks"],
1387
+ "scopes": ["Tasks.ReadWrite"],
1388
+ "llmTip": "Updates a step (checklist item) of a To Do task. Use this to check a step off (body: { isChecked: true }), uncheck it (isChecked: false), or rename it (displayName). Requires checklistItemId from list-todo-checklist-items."
1389
+ },
1390
+ {
1391
+ "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}/checklistItems/{checklistItem-id}",
1392
+ "method": "delete",
1393
+ "toolName": "delete-todo-checklist-item",
1394
+ "presets": ["personal", "tasks"],
1395
+ "scopes": ["Tasks.ReadWrite"],
1396
+ "llmTip": "Deletes a step (checklist item) from a To Do task. This cannot be undone. Requires checklistItemId from list-todo-checklist-items."
1397
+ },
1366
1398
  {
1367
1399
  "pathPattern": "/me/planner/tasks",
1368
1400
  "method": "get",