ms365-mcp-server 1.1.9 → 1.1.11

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
@@ -308,6 +308,95 @@ Files are:
308
308
  ### Cleanup
309
309
  Attachments are automatically cleaned up after 24 hours. You can also manually delete files from the `public/attachments` directory.
310
310
 
311
+ ### Enhanced Draft Email Operations
312
+ ```javascript
313
+ // Create a basic draft
314
+ {
315
+ "tool": "manage_email",
316
+ "arguments": {
317
+ "action": "draft",
318
+ "draftTo": ["colleague@company.com"],
319
+ "draftSubject": "Project Update",
320
+ "draftBody": "Draft content here...",
321
+ "draftBodyType": "text"
322
+ }
323
+ }
324
+
325
+ // Create a threaded reply draft (automatically appears in email thread)
326
+ {
327
+ "tool": "manage_email",
328
+ "arguments": {
329
+ "action": "reply_draft",
330
+ "originalMessageId": "original_email_id",
331
+ "draftBody": "My reply content...", // Optional comment
332
+ "replyToAll": false
333
+ }
334
+ }
335
+
336
+ // Create a forward draft (automatically includes original content)
337
+ {
338
+ "tool": "manage_email",
339
+ "arguments": {
340
+ "action": "forward_draft",
341
+ "originalMessageId": "original_email_id",
342
+ "draftBody": "Forwarding this for your review..." // Optional comment
343
+ }
344
+ }
345
+
346
+ // Update an existing draft
347
+ {
348
+ "tool": "manage_email",
349
+ "arguments": {
350
+ "action": "update_draft",
351
+ "draftId": "draft_email_id",
352
+ "draftSubject": "Updated Subject",
353
+ "draftBody": "Updated content..."
354
+ }
355
+ }
356
+
357
+ // Send a saved draft
358
+ {
359
+ "tool": "manage_email",
360
+ "arguments": {
361
+ "action": "send_draft",
362
+ "draftId": "draft_email_id"
363
+ }
364
+ }
365
+
366
+ // List all draft emails
367
+ {
368
+ "tool": "manage_email",
369
+ "arguments": {
370
+ "action": "list_drafts",
371
+ "maxResults": 20
372
+ }
373
+ }
374
+ ```
375
+
376
+ ### Folder Operations
377
+ ```javascript
378
+ // List all folders including subfolders with hierarchy
379
+ {
380
+ "tool": "list_folders"
381
+ }
382
+
383
+ // List only child folders of a specific parent folder
384
+ {
385
+ "tool": "list_folders",
386
+ "arguments": {
387
+ "parentFolderId": "inbox_folder_id"
388
+ }
389
+ }
390
+
391
+ // Search for folders by name (case-insensitive)
392
+ {
393
+ "tool": "list_folders",
394
+ "arguments": {
395
+ "searchName": "projects"
396
+ }
397
+ }
398
+ ```
399
+
311
400
  ## šŸŽÆ Command Line Options
312
401
 
313
402
  ```bash
package/dist/index.js CHANGED
@@ -67,7 +67,7 @@ function parseArgs() {
67
67
  }
68
68
  const server = new Server({
69
69
  name: "ms365-mcp-server",
70
- version: "1.1.9"
70
+ version: "1.1.11"
71
71
  }, {
72
72
  capabilities: {
73
73
  resources: {
@@ -195,8 +195,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
195
195
  },
196
196
  action: {
197
197
  type: "string",
198
- enum: ["read", "search", "list", "mark", "move", "delete", "search_to_me", "draft"],
199
- description: "Action to perform: read (get email by ID), search (find emails), list (folder contents), mark (read/unread), move (to folder), delete (permanently), search_to_me (emails addressed to you), draft (create/save draft)"
198
+ enum: ["read", "search", "list", "mark", "move", "delete", "search_to_me", "draft", "update_draft", "send_draft", "list_drafts", "reply_draft", "forward_draft"],
199
+ description: "Action to perform: read (get email by ID), search (find emails), list (folder contents), mark (read/unread), move (to folder), delete (permanently), search_to_me (emails addressed to you), draft (create/save draft), update_draft (modify existing draft), send_draft (send saved draft), list_drafts (list draft emails), reply_draft (create reply draft), forward_draft (create forward draft)"
200
200
  },
201
201
  messageId: {
202
202
  type: "string",
@@ -329,6 +329,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
329
329
  required: ["name", "contentBytes"]
330
330
  },
331
331
  description: "List of email attachments for draft (optional)"
332
+ },
333
+ // Additional draft parameters
334
+ draftId: {
335
+ type: "string",
336
+ description: "Draft email ID (required for update_draft and send_draft actions)"
337
+ },
338
+ originalMessageId: {
339
+ type: "string",
340
+ description: "Original message ID for reply_draft and forward_draft actions"
341
+ },
342
+ replyToAll: {
343
+ type: "boolean",
344
+ description: "Reply to all recipients (used with reply_draft action)",
345
+ default: false
346
+ },
347
+ conversationId: {
348
+ type: "string",
349
+ description: "Conversation ID for threading drafts (optional)"
332
350
  }
333
351
  },
334
352
  required: ["action"],
@@ -360,13 +378,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
360
378
  },
361
379
  {
362
380
  name: "list_folders",
363
- description: "List all mail folders in the mailbox. Returns folder names, IDs, and item counts for navigation and organization.",
381
+ description: "List all mail folders in the mailbox including subfolders. Returns folder hierarchy with names, IDs, paths, and item counts for navigation and organization. Now includes child folders inside parent folders like Inbox subfolders.",
364
382
  inputSchema: {
365
383
  type: "object",
366
384
  properties: {
367
385
  userId: {
368
386
  type: "string",
369
387
  description: "User ID for multi-user authentication (required if using multi-user mode)"
388
+ },
389
+ parentFolderId: {
390
+ type: "string",
391
+ description: "Optional: List only child folders of this parent folder ID. If not specified, returns complete folder hierarchy."
392
+ },
393
+ searchName: {
394
+ type: "string",
395
+ description: "Optional: Search for folders by name (case-insensitive partial match)"
370
396
  }
371
397
  },
372
398
  additionalProperties: false
@@ -822,7 +848,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
822
848
  body: args.draftBody,
823
849
  bodyType: args.draftBodyType || 'text',
824
850
  importance: args.draftImportance || 'normal',
825
- attachments: args.draftAttachments
851
+ attachments: args.draftAttachments,
852
+ conversationId: args.conversationId
826
853
  });
827
854
  return {
828
855
  content: [
@@ -832,6 +859,85 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
832
859
  }
833
860
  ]
834
861
  };
862
+ case "update_draft":
863
+ if (!args?.draftId) {
864
+ throw new Error("draftId is required for update_draft action");
865
+ }
866
+ const updates = {};
867
+ if (args.draftTo)
868
+ updates.to = args.draftTo;
869
+ if (args.draftCc)
870
+ updates.cc = args.draftCc;
871
+ if (args.draftBcc)
872
+ updates.bcc = args.draftBcc;
873
+ if (args.draftSubject)
874
+ updates.subject = args.draftSubject;
875
+ if (args.draftBody)
876
+ updates.body = args.draftBody;
877
+ if (args.draftBodyType)
878
+ updates.bodyType = args.draftBodyType;
879
+ if (args.draftImportance)
880
+ updates.importance = args.draftImportance;
881
+ if (args.draftAttachments)
882
+ updates.attachments = args.draftAttachments;
883
+ const updateResult = await ms365Ops.updateDraftEmail(args.draftId, updates);
884
+ return {
885
+ content: [
886
+ {
887
+ type: "text",
888
+ text: `āœ… Draft email updated successfully!\nšŸ†” Draft ID: ${updateResult.id}\nšŸ“ Status: ${updateResult.status}`
889
+ }
890
+ ]
891
+ };
892
+ case "send_draft":
893
+ if (!args?.draftId) {
894
+ throw new Error("draftId is required for send_draft action");
895
+ }
896
+ const sendResult = await ms365Ops.sendDraftEmail(args.draftId);
897
+ return {
898
+ content: [
899
+ {
900
+ type: "text",
901
+ text: `āœ… Draft email sent successfully!\nšŸ†” Draft ID: ${sendResult.id}\nšŸ“¤ Status: ${sendResult.status}`
902
+ }
903
+ ]
904
+ };
905
+ case "list_drafts":
906
+ const draftsList = await ms365Ops.listDrafts(args?.maxResults || 50);
907
+ return {
908
+ content: [
909
+ {
910
+ type: "text",
911
+ text: `šŸ“ Draft Emails (${draftsList.messages.length} found)\n\n${draftsList.messages.map((draft) => `šŸ“ ${draft.subject || 'No subject'}\n šŸ‘„ To: ${draft.toRecipients?.map((r) => r.address).join(', ') || 'No recipients'}\n šŸ†” ID: ${draft.id}\n šŸ“… Created: ${new Date(draft.receivedDateTime || draft.sentDateTime).toLocaleDateString()}\n šŸ’¬ Conversation: ${draft.conversationId || 'None'}\n`).join('\n')}\n${draftsList.hasMore ? 'āž”ļø More drafts available - use maxResults to see more' : ''}`
912
+ }
913
+ ]
914
+ };
915
+ case "reply_draft":
916
+ if (!args?.originalMessageId) {
917
+ throw new Error("originalMessageId is required for reply_draft action");
918
+ }
919
+ const replyDraftResult = await ms365Ops.createReplyDraft(args.originalMessageId, args.draftBody, args.replyToAll || false);
920
+ return {
921
+ content: [
922
+ {
923
+ type: "text",
924
+ text: `āœ… Reply draft created successfully!\nšŸ“§ Original Message: ${args.originalMessageId}\nšŸ’¬ Reply Type: ${args.replyToAll ? 'Reply All' : 'Reply'}\nšŸ†” Draft ID: ${replyDraftResult.id}\nšŸ’¬ Conversation ID: ${replyDraftResult.conversationId}\nšŸ“§ To: ${replyDraftResult.toRecipients?.join(', ') || 'Auto-determined'}`
925
+ }
926
+ ]
927
+ };
928
+ case "forward_draft":
929
+ if (!args?.originalMessageId) {
930
+ throw new Error("originalMessageId is required for forward_draft action");
931
+ }
932
+ const forwardDraftResult = await ms365Ops.createForwardDraft(args.originalMessageId, args.draftBody);
933
+ return {
934
+ content: [
935
+ {
936
+ type: "text",
937
+ text: `āœ… Forward draft created successfully!\nšŸ“§ Original Message: ${args.originalMessageId}\nšŸ†” Draft ID: ${forwardDraftResult.id}\nšŸ’¬ Conversation ID: ${forwardDraftResult.conversationId}\nšŸ“‹ Subject: ${forwardDraftResult.subject}`
938
+ }
939
+ ]
940
+ };
835
941
  default:
836
942
  throw new Error(`Unknown email action: ${emailAction}`);
837
943
  }
@@ -986,12 +1092,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
986
1092
  const graphClient = await enhancedMS365Auth.getGraphClient();
987
1093
  ms365Ops.setGraphClient(graphClient);
988
1094
  }
989
- const folders = await ms365Ops.listFolders();
1095
+ let folders;
1096
+ let resultTitle = "šŸ“ Mail Folders";
1097
+ if (args?.parentFolderId) {
1098
+ // List only child folders of specified parent
1099
+ const parentFolderId = args.parentFolderId;
1100
+ folders = await ms365Ops.listChildFolders(parentFolderId);
1101
+ resultTitle = `šŸ“ Child Folders of ${parentFolderId}`;
1102
+ }
1103
+ else if (args?.searchName) {
1104
+ // Search for folders by name
1105
+ const searchName = args.searchName;
1106
+ folders = await ms365Ops.findFolderByName(searchName);
1107
+ resultTitle = `šŸ“ Folders matching "${searchName}"`;
1108
+ }
1109
+ else {
1110
+ // List all folders with hierarchy
1111
+ folders = await ms365Ops.listFolders();
1112
+ resultTitle = "šŸ“ Mail Folders (Complete Hierarchy)";
1113
+ }
1114
+ // Format folders with proper hierarchy display
1115
+ const formatFolder = (folder) => {
1116
+ const indent = ' '.repeat(folder.depth || 0);
1117
+ const icon = folder.depth === 0 ? 'šŸ“' : 'šŸ“‚';
1118
+ const pathInfo = folder.fullPath ? `\n${indent} šŸ“ Path: ${folder.fullPath}` : '';
1119
+ return `${indent}${icon} ${folder.displayName}\n${indent} šŸ†” ID: ${folder.id}\n${indent} šŸ“§ Total Items: ${folder.totalItemCount}\n${indent} šŸ“© Unread: ${folder.unreadItemCount}${pathInfo}\n`;
1120
+ };
1121
+ const folderCount = folders.length;
1122
+ const totalItems = folders.reduce((sum, folder) => sum + (folder.totalItemCount || 0), 0);
1123
+ const totalUnread = folders.reduce((sum, folder) => sum + (folder.unreadItemCount || 0), 0);
990
1124
  return {
991
1125
  content: [
992
1126
  {
993
1127
  type: "text",
994
- text: `šŸ“ Mail Folders\n\n${folders.map((folder) => `šŸ“‚ ${folder.displayName}\n šŸ†” ID: ${folder.id}\n šŸ“§ Total Items: ${folder.totalItemCount}\n šŸ“© Unread: ${folder.unreadItemCount}\n`).join('\n')}`
1128
+ text: `${resultTitle}\n\nšŸ“Š Summary: ${folderCount} folders, ${totalItems} total items, ${totalUnread} unread\n\n${folders.map(formatFolder).join('\n')}`
995
1129
  }
996
1130
  ]
997
1131
  };