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 +89 -0
- package/dist/index.js +141 -7
- package/dist/utils/ms365-operations.js +1056 -109
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
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:
|
|
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
|
};
|