ms365-mcp-server 1.1.12 → 1.1.14
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/dist/index.js +98 -45
- package/dist/utils/ms365-operations.js +60 -6
- package/package.json +1 -1
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.14"
|
|
71
71
|
}, {
|
|
72
72
|
capabilities: {
|
|
73
73
|
resources: {
|
|
@@ -83,7 +83,7 @@ const server = new Server({
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
|
-
logger.log('Server started with version 1.1.
|
|
86
|
+
logger.log('Server started with version 1.1.14');
|
|
87
87
|
// Set up the resource listing request handler
|
|
88
88
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
89
89
|
logger.log('Received list resources request');
|
|
@@ -119,19 +119,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
119
119
|
description: "User ID for multi-user authentication (required if using multi-user mode)"
|
|
120
120
|
},
|
|
121
121
|
to: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
oneOf: [
|
|
123
|
+
{ type: "string" },
|
|
124
|
+
{ type: "array", items: { type: "string" } }
|
|
125
|
+
],
|
|
126
|
+
description: "Recipient email address (string) or list of recipient email addresses (array)"
|
|
125
127
|
},
|
|
126
128
|
cc: {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
oneOf: [
|
|
130
|
+
{ type: "string" },
|
|
131
|
+
{ type: "array", items: { type: "string" } }
|
|
132
|
+
],
|
|
133
|
+
description: "CC recipient email address (string) or list of CC recipient email addresses (array) - optional"
|
|
130
134
|
},
|
|
131
135
|
bcc: {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
oneOf: [
|
|
137
|
+
{ type: "string" },
|
|
138
|
+
{ type: "array", items: { type: "string" } }
|
|
139
|
+
],
|
|
140
|
+
description: "BCC recipient email address (string) or list of BCC recipient email addresses (array) - optional"
|
|
135
141
|
},
|
|
136
142
|
subject: {
|
|
137
143
|
type: "string",
|
|
@@ -180,8 +186,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
180
186
|
description: "List of email attachments (optional)"
|
|
181
187
|
}
|
|
182
188
|
},
|
|
183
|
-
required: ["to", "subject"]
|
|
184
|
-
additionalProperties: false
|
|
189
|
+
required: ["to", "subject"]
|
|
185
190
|
}
|
|
186
191
|
},
|
|
187
192
|
{
|
|
@@ -275,19 +280,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
275
280
|
},
|
|
276
281
|
// Draft email parameters
|
|
277
282
|
draftTo: {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
oneOf: [
|
|
284
|
+
{ type: "string" },
|
|
285
|
+
{ type: "array", items: { type: "string" } }
|
|
286
|
+
],
|
|
287
|
+
description: "Recipient email address (string) or list of recipient email addresses (array) - required for draft action"
|
|
281
288
|
},
|
|
282
289
|
draftCc: {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
290
|
+
oneOf: [
|
|
291
|
+
{ type: "string" },
|
|
292
|
+
{ type: "array", items: { type: "string" } }
|
|
293
|
+
],
|
|
294
|
+
description: "CC recipient email address (string) or list of CC recipient email addresses (array) - optional for draft action"
|
|
286
295
|
},
|
|
287
296
|
draftBcc: {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
297
|
+
oneOf: [
|
|
298
|
+
{ type: "string" },
|
|
299
|
+
{ type: "array", items: { type: "string" } }
|
|
300
|
+
],
|
|
301
|
+
description: "BCC recipient email address (string) or list of BCC recipient email addresses (array) - optional for draft action"
|
|
291
302
|
},
|
|
292
303
|
draftSubject: {
|
|
293
304
|
type: "string",
|
|
@@ -350,8 +361,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
350
361
|
description: "Conversation ID for threading drafts (optional)"
|
|
351
362
|
}
|
|
352
363
|
},
|
|
353
|
-
required: ["action"]
|
|
354
|
-
additionalProperties: false
|
|
364
|
+
required: ["action"]
|
|
355
365
|
}
|
|
356
366
|
},
|
|
357
367
|
{
|
|
@@ -373,8 +383,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
373
383
|
description: "Attachment ID to download"
|
|
374
384
|
}
|
|
375
385
|
},
|
|
376
|
-
required: ["messageId", "attachmentId"]
|
|
377
|
-
additionalProperties: false
|
|
386
|
+
required: ["messageId", "attachmentId"]
|
|
378
387
|
}
|
|
379
388
|
},
|
|
380
389
|
{
|
|
@@ -395,8 +404,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
395
404
|
type: "string",
|
|
396
405
|
description: "Optional: Search for folders by name (case-insensitive partial match)"
|
|
397
406
|
}
|
|
398
|
-
}
|
|
399
|
-
additionalProperties: false
|
|
407
|
+
}
|
|
400
408
|
}
|
|
401
409
|
},
|
|
402
410
|
{
|
|
@@ -425,8 +433,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
425
433
|
minimum: 1,
|
|
426
434
|
maximum: 500
|
|
427
435
|
}
|
|
428
|
-
}
|
|
429
|
-
additionalProperties: false
|
|
436
|
+
}
|
|
430
437
|
}
|
|
431
438
|
}
|
|
432
439
|
];
|
|
@@ -442,8 +449,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
442
449
|
type: "string",
|
|
443
450
|
description: "User's email address (optional, for identification)"
|
|
444
451
|
}
|
|
445
|
-
}
|
|
446
|
-
additionalProperties: false
|
|
452
|
+
}
|
|
447
453
|
}
|
|
448
454
|
},
|
|
449
455
|
{
|
|
@@ -457,8 +463,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
457
463
|
description: "Your User ID to remove"
|
|
458
464
|
}
|
|
459
465
|
},
|
|
460
|
-
required: ["userId"]
|
|
461
|
-
additionalProperties: false
|
|
466
|
+
required: ["userId"]
|
|
462
467
|
}
|
|
463
468
|
}
|
|
464
469
|
];
|
|
@@ -486,8 +491,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
486
491
|
description: "Account key for logout action (default: current user)",
|
|
487
492
|
default: "default-user"
|
|
488
493
|
}
|
|
489
|
-
}
|
|
490
|
-
additionalProperties: false
|
|
494
|
+
}
|
|
491
495
|
}
|
|
492
496
|
}
|
|
493
497
|
];
|
|
@@ -851,15 +855,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
851
855
|
if (!args?.draftTo || !args?.draftSubject || !args?.draftBody) {
|
|
852
856
|
throw new Error("draftTo, draftSubject, and draftBody are required for draft action");
|
|
853
857
|
}
|
|
858
|
+
// Helper function to normalize email arrays for drafts
|
|
859
|
+
const normalizeDraftEmailArray = (field) => {
|
|
860
|
+
if (!field)
|
|
861
|
+
return [];
|
|
862
|
+
if (typeof field === 'string')
|
|
863
|
+
return [field];
|
|
864
|
+
if (Array.isArray(field))
|
|
865
|
+
return field;
|
|
866
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
867
|
+
};
|
|
854
868
|
// Check if this looks like a reply based on subject
|
|
855
869
|
const isLikelyReply = args.draftSubject?.toString().toLowerCase().startsWith('re:');
|
|
856
870
|
if (isLikelyReply) {
|
|
857
871
|
logger.log('🔔 SUGGESTION: Consider using "reply_draft" action instead of "draft" for threaded replies that appear in conversations');
|
|
858
872
|
}
|
|
859
873
|
const draftResult = await ms365Ops.saveDraftEmail({
|
|
860
|
-
to: args.draftTo,
|
|
861
|
-
cc: args.draftCc,
|
|
862
|
-
bcc: args.draftBcc,
|
|
874
|
+
to: normalizeDraftEmailArray(args.draftTo),
|
|
875
|
+
cc: normalizeDraftEmailArray(args.draftCc),
|
|
876
|
+
bcc: normalizeDraftEmailArray(args.draftBcc),
|
|
863
877
|
subject: args.draftSubject,
|
|
864
878
|
body: args.draftBody,
|
|
865
879
|
bodyType: args.draftBodyType || 'text',
|
|
@@ -884,13 +898,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
884
898
|
if (!args?.draftId) {
|
|
885
899
|
throw new Error("draftId is required for update_draft action");
|
|
886
900
|
}
|
|
901
|
+
// Helper function to normalize email arrays for draft updates
|
|
902
|
+
const normalizeUpdateEmailArray = (field) => {
|
|
903
|
+
if (!field)
|
|
904
|
+
return [];
|
|
905
|
+
if (typeof field === 'string')
|
|
906
|
+
return [field];
|
|
907
|
+
if (Array.isArray(field))
|
|
908
|
+
return field;
|
|
909
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
910
|
+
};
|
|
887
911
|
const updates = {};
|
|
888
912
|
if (args.draftTo)
|
|
889
|
-
updates.to = args.draftTo;
|
|
913
|
+
updates.to = normalizeUpdateEmailArray(args.draftTo);
|
|
890
914
|
if (args.draftCc)
|
|
891
|
-
updates.cc = args.draftCc;
|
|
915
|
+
updates.cc = normalizeUpdateEmailArray(args.draftCc);
|
|
892
916
|
if (args.draftBcc)
|
|
893
|
-
updates.bcc = args.draftBcc;
|
|
917
|
+
updates.bcc = normalizeUpdateEmailArray(args.draftBcc);
|
|
894
918
|
if (args.draftSubject)
|
|
895
919
|
updates.subject = args.draftSubject;
|
|
896
920
|
if (args.draftBody)
|
|
@@ -937,7 +961,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
937
961
|
if (!args?.originalMessageId) {
|
|
938
962
|
throw new Error("originalMessageId is required for reply_draft action");
|
|
939
963
|
}
|
|
940
|
-
const replyDraftResult = await ms365Ops.createReplyDraft(args.originalMessageId, args.draftBody, args.replyToAll || false);
|
|
964
|
+
const replyDraftResult = await ms365Ops.createReplyDraft(args.originalMessageId, args.draftBody, args.replyToAll || false, args.draftBodyType || 'text');
|
|
941
965
|
return {
|
|
942
966
|
content: [
|
|
943
967
|
{
|
|
@@ -950,7 +974,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
950
974
|
if (!args?.originalMessageId) {
|
|
951
975
|
throw new Error("originalMessageId is required for forward_draft action");
|
|
952
976
|
}
|
|
953
|
-
const forwardDraftResult = await ms365Ops.createForwardDraft(args.originalMessageId, args.draftBody);
|
|
977
|
+
const forwardDraftResult = await ms365Ops.createForwardDraft(args.originalMessageId, args.draftBody, args.draftBodyType || 'text');
|
|
954
978
|
return {
|
|
955
979
|
content: [
|
|
956
980
|
{
|
|
@@ -1018,7 +1042,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1018
1042
|
const graphClient = await enhancedMS365Auth.getGraphClient();
|
|
1019
1043
|
ms365Ops.setGraphClient(graphClient);
|
|
1020
1044
|
}
|
|
1021
|
-
|
|
1045
|
+
// Validate and normalize email input
|
|
1046
|
+
if (!args?.to) {
|
|
1047
|
+
throw new Error("'to' field is required for sending email");
|
|
1048
|
+
}
|
|
1049
|
+
if (!args?.subject) {
|
|
1050
|
+
throw new Error("'subject' field is required for sending email");
|
|
1051
|
+
}
|
|
1052
|
+
// Helper function to normalize email arrays
|
|
1053
|
+
const normalizeEmailArray = (field) => {
|
|
1054
|
+
if (!field)
|
|
1055
|
+
return [];
|
|
1056
|
+
if (typeof field === 'string')
|
|
1057
|
+
return [field];
|
|
1058
|
+
if (Array.isArray(field))
|
|
1059
|
+
return field;
|
|
1060
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
1061
|
+
};
|
|
1062
|
+
// Normalize the email message
|
|
1063
|
+
const emailMessage = {
|
|
1064
|
+
to: normalizeEmailArray(args.to),
|
|
1065
|
+
cc: normalizeEmailArray(args.cc),
|
|
1066
|
+
bcc: normalizeEmailArray(args.bcc),
|
|
1067
|
+
subject: args.subject,
|
|
1068
|
+
body: args.body || '',
|
|
1069
|
+
bodyType: args.bodyType || 'text',
|
|
1070
|
+
replyTo: args.replyTo,
|
|
1071
|
+
importance: args.importance || 'normal',
|
|
1072
|
+
attachments: args.attachments || []
|
|
1073
|
+
};
|
|
1074
|
+
const emailResult = await ms365Ops.sendEmail(emailMessage);
|
|
1022
1075
|
return {
|
|
1023
1076
|
content: [
|
|
1024
1077
|
{
|
|
@@ -522,7 +522,7 @@ export class MS365Operations {
|
|
|
522
522
|
/**
|
|
523
523
|
* Create a threaded reply draft from a specific message
|
|
524
524
|
*/
|
|
525
|
-
async createReplyDraft(originalMessageId, body, replyToAll = false) {
|
|
525
|
+
async createReplyDraft(originalMessageId, body, replyToAll = false, bodyType = 'text') {
|
|
526
526
|
try {
|
|
527
527
|
const graphClient = await this.getGraphClient();
|
|
528
528
|
logger.log(`Creating reply draft for message: ${originalMessageId}`);
|
|
@@ -536,7 +536,25 @@ export class MS365Operations {
|
|
|
536
536
|
const originalBodyContent = originalMessage.body?.content || '';
|
|
537
537
|
const fromDisplay = originalMessage.from?.emailAddress?.name || originalMessage.from?.emailAddress?.address || '';
|
|
538
538
|
const sentDate = new Date(originalMessage.sentDateTime).toLocaleString();
|
|
539
|
-
|
|
539
|
+
// Helper function to escape HTML characters
|
|
540
|
+
const escapeHtml = (text) => {
|
|
541
|
+
return text
|
|
542
|
+
.replace(/&/g, '&')
|
|
543
|
+
.replace(/</g, '<')
|
|
544
|
+
.replace(/>/g, '>')
|
|
545
|
+
.replace(/"/g, '"')
|
|
546
|
+
.replace(/'/g, ''');
|
|
547
|
+
};
|
|
548
|
+
// Helper function to convert text to HTML
|
|
549
|
+
const textToHtml = (text) => {
|
|
550
|
+
return escapeHtml(text).replace(/\n/g, '<br>');
|
|
551
|
+
};
|
|
552
|
+
// Process the user's body based on the specified type
|
|
553
|
+
let processedUserBody = body || '';
|
|
554
|
+
if (bodyType === 'text' && processedUserBody) {
|
|
555
|
+
processedUserBody = textToHtml(processedUserBody);
|
|
556
|
+
}
|
|
557
|
+
const completeReplyBody = `${processedUserBody}
|
|
540
558
|
|
|
541
559
|
<br><br>
|
|
542
560
|
<div style="border-left: 2px solid #ccc; padding-left: 10px; margin-top: 10px;">
|
|
@@ -670,20 +688,38 @@ ${originalBodyContent}
|
|
|
670
688
|
/**
|
|
671
689
|
* Create a threaded forward draft from a specific message
|
|
672
690
|
*/
|
|
673
|
-
async createForwardDraft(originalMessageId, comment) {
|
|
691
|
+
async createForwardDraft(originalMessageId, comment, bodyType = 'text') {
|
|
674
692
|
try {
|
|
675
693
|
const graphClient = await this.getGraphClient();
|
|
676
694
|
logger.log(`Creating forward draft for message: ${originalMessageId}`);
|
|
677
695
|
// First, try using the official Microsoft Graph createForward endpoint for proper threading
|
|
678
696
|
try {
|
|
679
697
|
logger.log(`Using official Graph API endpoint: /me/messages/${originalMessageId}/createForward`);
|
|
698
|
+
// Helper function to escape HTML characters
|
|
699
|
+
const escapeHtml = (text) => {
|
|
700
|
+
return text
|
|
701
|
+
.replace(/&/g, '&')
|
|
702
|
+
.replace(/</g, '<')
|
|
703
|
+
.replace(/>/g, '>')
|
|
704
|
+
.replace(/"/g, '"')
|
|
705
|
+
.replace(/'/g, ''');
|
|
706
|
+
};
|
|
707
|
+
// Helper function to convert text to HTML
|
|
708
|
+
const textToHtml = (text) => {
|
|
709
|
+
return escapeHtml(text).replace(/\n/g, '<br>');
|
|
710
|
+
};
|
|
711
|
+
// Process the comment based on the specified type
|
|
712
|
+
let processedComment = comment || '';
|
|
713
|
+
if (bodyType === 'text' && processedComment) {
|
|
714
|
+
processedComment = textToHtml(processedComment);
|
|
715
|
+
}
|
|
680
716
|
const forwardDraft = await graphClient
|
|
681
717
|
.api(`/me/messages/${originalMessageId}/createForward`)
|
|
682
718
|
.post({
|
|
683
719
|
message: {
|
|
684
720
|
body: {
|
|
685
721
|
contentType: 'html',
|
|
686
|
-
content:
|
|
722
|
+
content: processedComment
|
|
687
723
|
}
|
|
688
724
|
}
|
|
689
725
|
});
|
|
@@ -717,11 +753,29 @@ ${originalBodyContent}
|
|
|
717
753
|
referencesHeader = `${existingReferences.value} ${referencesHeader}`;
|
|
718
754
|
}
|
|
719
755
|
}
|
|
720
|
-
|
|
756
|
+
// Helper function to escape HTML characters for fallback
|
|
757
|
+
const escapeHtml = (text) => {
|
|
758
|
+
return text
|
|
759
|
+
.replace(/&/g, '&')
|
|
760
|
+
.replace(/</g, '<')
|
|
761
|
+
.replace(/>/g, '>')
|
|
762
|
+
.replace(/"/g, '"')
|
|
763
|
+
.replace(/'/g, ''');
|
|
764
|
+
};
|
|
765
|
+
// Helper function to convert text to HTML for fallback
|
|
766
|
+
const textToHtml = (text) => {
|
|
767
|
+
return escapeHtml(text).replace(/\n/g, '<br>');
|
|
768
|
+
};
|
|
769
|
+
// Process the comment based on the specified type for fallback
|
|
770
|
+
let processedComment = comment || '';
|
|
771
|
+
if (bodyType === 'text' && processedComment) {
|
|
772
|
+
processedComment = textToHtml(processedComment);
|
|
773
|
+
}
|
|
774
|
+
const forwardedBody = `${processedComment ? processedComment + '<br><br>' : ''}---------- Forwarded message ----------<br>From: ${originalMessage.from?.emailAddress?.name || originalMessage.from?.emailAddress?.address}<br>Date: ${originalMessage.sentDateTime}<br>Subject: ${originalMessage.subject}<br>To: ${originalMessage.toRecipients?.map((r) => r.emailAddress.address).join(', ')}<br><br>${originalMessage.body?.content || ''}`;
|
|
721
775
|
const draftBody = {
|
|
722
776
|
subject: originalMessage.subject?.startsWith('Fwd:') ? originalMessage.subject : `Fwd: ${originalMessage.subject}`,
|
|
723
777
|
body: {
|
|
724
|
-
contentType:
|
|
778
|
+
contentType: 'html',
|
|
725
779
|
content: forwardedBody
|
|
726
780
|
},
|
|
727
781
|
conversationId: originalMessage.conversationId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ms365-mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "Microsoft 365 MCP Server for managing Microsoft 365 email through natural language interactions with full OAuth2 authentication support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|