ms365-mcp-server 1.1.13 ā 1.1.15
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 +181 -50
- package/dist/utils/ms365-operations.js +780 -138
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import path from 'path';
|
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import fs from 'fs/promises';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
|
-
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
import { logger } from './utils/api.js';
|
|
15
15
|
import { MS365Operations } from './utils/ms365-operations.js';
|
|
16
16
|
import { multiUserMS365Auth } from './utils/multi-user-auth.js';
|
|
@@ -67,23 +67,28 @@ function parseArgs() {
|
|
|
67
67
|
}
|
|
68
68
|
const server = new Server({
|
|
69
69
|
name: "ms365-mcp-server",
|
|
70
|
-
version: "1.1.
|
|
70
|
+
version: "1.1.15"
|
|
71
71
|
}, {
|
|
72
72
|
capabilities: {
|
|
73
73
|
resources: {
|
|
74
74
|
read: true,
|
|
75
|
-
list: true
|
|
75
|
+
list: true,
|
|
76
|
+
templates: true
|
|
76
77
|
},
|
|
77
78
|
tools: {
|
|
78
79
|
list: true,
|
|
79
80
|
call: true
|
|
80
81
|
},
|
|
81
82
|
prompts: {
|
|
83
|
+
list: true,
|
|
84
|
+
get: true
|
|
85
|
+
},
|
|
86
|
+
resourceTemplates: {
|
|
82
87
|
list: true
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
});
|
|
86
|
-
logger.log('Server started with version 1.1.
|
|
91
|
+
logger.log('Server started with version 1.1.14');
|
|
87
92
|
// Set up the resource listing request handler
|
|
88
93
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
89
94
|
logger.log('Received list resources request');
|
|
@@ -103,6 +108,20 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
103
108
|
logger.log('Received list prompts request');
|
|
104
109
|
return { prompts: [] };
|
|
105
110
|
});
|
|
111
|
+
/**
|
|
112
|
+
* Handler for getting a specific prompt.
|
|
113
|
+
*/
|
|
114
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
115
|
+
logger.log('Received get prompt request: ' + JSON.stringify(request));
|
|
116
|
+
throw new Error("Prompt getting not implemented");
|
|
117
|
+
});
|
|
118
|
+
/**
|
|
119
|
+
* Handler for listing available resource templates.
|
|
120
|
+
*/
|
|
121
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
122
|
+
logger.log('Received list resource templates request');
|
|
123
|
+
return { resourceTemplates: [] };
|
|
124
|
+
});
|
|
106
125
|
/**
|
|
107
126
|
* List available tools for interacting with Microsoft 365.
|
|
108
127
|
*/
|
|
@@ -119,19 +138,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
119
138
|
description: "User ID for multi-user authentication (required if using multi-user mode)"
|
|
120
139
|
},
|
|
121
140
|
to: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
oneOf: [
|
|
142
|
+
{ type: "string" },
|
|
143
|
+
{ type: "array", items: { type: "string" } }
|
|
144
|
+
],
|
|
145
|
+
description: "Recipient email address (string) or list of recipient email addresses (array)"
|
|
125
146
|
},
|
|
126
147
|
cc: {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
oneOf: [
|
|
149
|
+
{ type: "string" },
|
|
150
|
+
{ type: "array", items: { type: "string" } }
|
|
151
|
+
],
|
|
152
|
+
description: "CC recipient email address (string) or list of CC recipient email addresses (array) - optional"
|
|
130
153
|
},
|
|
131
154
|
bcc: {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
155
|
+
oneOf: [
|
|
156
|
+
{ type: "string" },
|
|
157
|
+
{ type: "array", items: { type: "string" } }
|
|
158
|
+
],
|
|
159
|
+
description: "BCC recipient email address (string) or list of BCC recipient email addresses (array) - optional"
|
|
135
160
|
},
|
|
136
161
|
subject: {
|
|
137
162
|
type: "string",
|
|
@@ -180,8 +205,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
180
205
|
description: "List of email attachments (optional)"
|
|
181
206
|
}
|
|
182
207
|
},
|
|
183
|
-
required: ["to", "subject"]
|
|
184
|
-
additionalProperties: false
|
|
208
|
+
required: ["to", "subject"]
|
|
185
209
|
}
|
|
186
210
|
},
|
|
187
211
|
{
|
|
@@ -196,8 +220,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
196
220
|
},
|
|
197
221
|
action: {
|
|
198
222
|
type: "string",
|
|
199
|
-
enum: ["read", "search", "list", "mark", "move", "delete", "search_to_me", "draft", "update_draft", "send_draft", "list_drafts", "reply_draft", "forward_draft"],
|
|
200
|
-
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 standalone draft - NOTE: Use reply_draft/forward_draft for threaded drafts), update_draft (modify existing draft), send_draft (send saved draft), list_drafts (list draft emails), reply_draft (create threaded reply draft that appears in conversation), forward_draft (create threaded forward draft that appears in conversation)"
|
|
223
|
+
enum: ["read", "search", "search_batched", "list", "mark", "move", "delete", "search_to_me", "draft", "update_draft", "send_draft", "list_drafts", "reply_draft", "forward_draft"],
|
|
224
|
+
description: "Action to perform: read (get email by ID), search (find emails), search_batched (find emails in batches of 50, up to 5 batches for better performance), list (folder contents), mark (read/unread), move (to folder), delete (permanently), search_to_me (emails addressed to you), draft (create standalone draft - NOTE: Use reply_draft/forward_draft for threaded drafts), update_draft (modify existing draft), send_draft (send saved draft), list_drafts (list draft emails), reply_draft (create threaded reply draft that appears in conversation), forward_draft (create threaded forward draft that appears in conversation)"
|
|
201
225
|
},
|
|
202
226
|
messageId: {
|
|
203
227
|
type: "string",
|
|
@@ -268,26 +292,45 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
268
292
|
},
|
|
269
293
|
maxResults: {
|
|
270
294
|
type: "number",
|
|
271
|
-
description: "Maximum number of results to return (
|
|
272
|
-
minimum:
|
|
273
|
-
|
|
295
|
+
description: "Maximum number of results to return. Set to 0 or omit to get ALL matching results (safety limited to 1000). For large result sets, use specific maxResults values to avoid timeouts. Default: 50",
|
|
296
|
+
minimum: 0,
|
|
297
|
+
default: 50
|
|
298
|
+
},
|
|
299
|
+
batchSize: {
|
|
300
|
+
type: "number",
|
|
301
|
+
description: "Number of results per batch for search_batched action (default: 50)",
|
|
302
|
+
minimum: 10,
|
|
303
|
+
maximum: 100,
|
|
274
304
|
default: 50
|
|
275
305
|
},
|
|
306
|
+
maxBatches: {
|
|
307
|
+
type: "number",
|
|
308
|
+
description: "Maximum number of batches for search_batched action (default: 5)",
|
|
309
|
+
minimum: 1,
|
|
310
|
+
maximum: 10,
|
|
311
|
+
default: 5
|
|
312
|
+
},
|
|
276
313
|
// Draft email parameters
|
|
277
314
|
draftTo: {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
315
|
+
oneOf: [
|
|
316
|
+
{ type: "string" },
|
|
317
|
+
{ type: "array", items: { type: "string" } }
|
|
318
|
+
],
|
|
319
|
+
description: "Recipient email address (string) or list of recipient email addresses (array) - required for draft action"
|
|
281
320
|
},
|
|
282
321
|
draftCc: {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
322
|
+
oneOf: [
|
|
323
|
+
{ type: "string" },
|
|
324
|
+
{ type: "array", items: { type: "string" } }
|
|
325
|
+
],
|
|
326
|
+
description: "CC recipient email address (string) or list of CC recipient email addresses (array) - optional for draft action"
|
|
286
327
|
},
|
|
287
328
|
draftBcc: {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
329
|
+
oneOf: [
|
|
330
|
+
{ type: "string" },
|
|
331
|
+
{ type: "array", items: { type: "string" } }
|
|
332
|
+
],
|
|
333
|
+
description: "BCC recipient email address (string) or list of BCC recipient email addresses (array) - optional for draft action"
|
|
291
334
|
},
|
|
292
335
|
draftSubject: {
|
|
293
336
|
type: "string",
|
|
@@ -350,8 +393,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
350
393
|
description: "Conversation ID for threading drafts (optional)"
|
|
351
394
|
}
|
|
352
395
|
},
|
|
353
|
-
required: ["action"]
|
|
354
|
-
additionalProperties: false
|
|
396
|
+
required: ["action"]
|
|
355
397
|
}
|
|
356
398
|
},
|
|
357
399
|
{
|
|
@@ -373,8 +415,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
373
415
|
description: "Attachment ID to download"
|
|
374
416
|
}
|
|
375
417
|
},
|
|
376
|
-
required: ["messageId", "attachmentId"]
|
|
377
|
-
additionalProperties: false
|
|
418
|
+
required: ["messageId", "attachmentId"]
|
|
378
419
|
}
|
|
379
420
|
},
|
|
380
421
|
{
|
|
@@ -395,8 +436,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
395
436
|
type: "string",
|
|
396
437
|
description: "Optional: Search for folders by name (case-insensitive partial match)"
|
|
397
438
|
}
|
|
398
|
-
}
|
|
399
|
-
additionalProperties: false
|
|
439
|
+
}
|
|
400
440
|
}
|
|
401
441
|
},
|
|
402
442
|
{
|
|
@@ -425,8 +465,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
425
465
|
minimum: 1,
|
|
426
466
|
maximum: 500
|
|
427
467
|
}
|
|
428
|
-
}
|
|
429
|
-
additionalProperties: false
|
|
468
|
+
}
|
|
430
469
|
}
|
|
431
470
|
}
|
|
432
471
|
];
|
|
@@ -442,8 +481,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
442
481
|
type: "string",
|
|
443
482
|
description: "User's email address (optional, for identification)"
|
|
444
483
|
}
|
|
445
|
-
}
|
|
446
|
-
additionalProperties: false
|
|
484
|
+
}
|
|
447
485
|
}
|
|
448
486
|
},
|
|
449
487
|
{
|
|
@@ -457,8 +495,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
457
495
|
description: "Your User ID to remove"
|
|
458
496
|
}
|
|
459
497
|
},
|
|
460
|
-
required: ["userId"]
|
|
461
|
-
additionalProperties: false
|
|
498
|
+
required: ["userId"]
|
|
462
499
|
}
|
|
463
500
|
}
|
|
464
501
|
];
|
|
@@ -486,8 +523,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
486
523
|
description: "Account key for logout action (default: current user)",
|
|
487
524
|
default: "default-user"
|
|
488
525
|
}
|
|
489
|
-
}
|
|
490
|
-
additionalProperties: false
|
|
526
|
+
}
|
|
491
527
|
}
|
|
492
528
|
}
|
|
493
529
|
];
|
|
@@ -722,6 +758,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
722
758
|
}
|
|
723
759
|
]
|
|
724
760
|
};
|
|
761
|
+
case "search_batched":
|
|
762
|
+
const batchSize = args?.batchSize || 50;
|
|
763
|
+
const maxBatches = args?.maxBatches || 5;
|
|
764
|
+
logger.log(`DEBUG: Starting batched search with batchSize: ${batchSize}, maxBatches: ${maxBatches}`);
|
|
765
|
+
const batchedSearchResults = await ms365Ops.searchEmailsBatched(args, batchSize, maxBatches);
|
|
766
|
+
logger.log(`DEBUG: Batched search results count: ${batchedSearchResults.messages.length} from ${batchedSearchResults.totalBatches} batches`);
|
|
767
|
+
// Enhanced feedback for batched search results
|
|
768
|
+
let batchedResponseText = `š Batched Email Search Results (${batchedSearchResults.messages.length} found from ${batchedSearchResults.totalBatches} batches)`;
|
|
769
|
+
if (batchedSearchResults.messages.length === 0) {
|
|
770
|
+
batchedResponseText = `š No emails found matching your criteria.\n\nš” Search Tips:\n`;
|
|
771
|
+
if (args?.from) {
|
|
772
|
+
batchedResponseText += `⢠Try partial names: "${args.from.split(' ')[0]}" or "${args.from.split(' ').pop()}"\n`;
|
|
773
|
+
batchedResponseText += `⢠Check spelling of sender name\n`;
|
|
774
|
+
}
|
|
775
|
+
if (args?.subject) {
|
|
776
|
+
batchedResponseText += `⢠Try broader subject terms\n`;
|
|
777
|
+
}
|
|
778
|
+
if (args?.after || args?.before) {
|
|
779
|
+
batchedResponseText += `⢠Try expanding date range\n`;
|
|
780
|
+
}
|
|
781
|
+
batchedResponseText += `⢠Remove some search criteria to get broader results`;
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
batchedResponseText += `\n\n${batchedSearchResults.messages.map((email, index) => {
|
|
785
|
+
// Handle missing IDs more gracefully
|
|
786
|
+
const emailId = email.id || 'ID_MISSING';
|
|
787
|
+
const fromDisplay = email.from?.name ? `${email.from.name} <${email.from.address}>` : email.from?.address || 'Unknown sender';
|
|
788
|
+
return `${index + 1}. š§ ${email.subject || 'No subject'}\n š¤ From: ${fromDisplay}\n š
${email.receivedDateTime ? new Date(email.receivedDateTime).toLocaleDateString() : 'Unknown date'}\n ${email.isRead ? 'š Read' : 'š© Unread'}\n š ID: ${emailId}\n`;
|
|
789
|
+
}).join('\n')}`;
|
|
790
|
+
if (batchedSearchResults.hasMore) {
|
|
791
|
+
batchedResponseText += `\nš” There are more results available. Increase maxBatches parameter to get more emails.`;
|
|
792
|
+
}
|
|
793
|
+
batchedResponseText += `\n\nš Search Summary:\n`;
|
|
794
|
+
batchedResponseText += `⢠Total results: ${batchedSearchResults.messages.length}\n`;
|
|
795
|
+
batchedResponseText += `⢠Batches processed: ${batchedSearchResults.totalBatches}/${maxBatches}\n`;
|
|
796
|
+
batchedResponseText += `⢠Batch size: ${batchSize}\n`;
|
|
797
|
+
batchedResponseText += `⢠More results available: ${batchedSearchResults.hasMore ? 'Yes' : 'No'}`;
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
content: [
|
|
801
|
+
{
|
|
802
|
+
type: "text",
|
|
803
|
+
text: batchedResponseText
|
|
804
|
+
}
|
|
805
|
+
]
|
|
806
|
+
};
|
|
725
807
|
case "search_to_me":
|
|
726
808
|
const searchToMeResults = await ms365Ops.searchEmailsToMe(args);
|
|
727
809
|
// Process attachments for each email
|
|
@@ -851,15 +933,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
851
933
|
if (!args?.draftTo || !args?.draftSubject || !args?.draftBody) {
|
|
852
934
|
throw new Error("draftTo, draftSubject, and draftBody are required for draft action");
|
|
853
935
|
}
|
|
936
|
+
// Helper function to normalize email arrays for drafts
|
|
937
|
+
const normalizeDraftEmailArray = (field) => {
|
|
938
|
+
if (!field)
|
|
939
|
+
return [];
|
|
940
|
+
if (typeof field === 'string')
|
|
941
|
+
return [field];
|
|
942
|
+
if (Array.isArray(field))
|
|
943
|
+
return field;
|
|
944
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
945
|
+
};
|
|
854
946
|
// Check if this looks like a reply based on subject
|
|
855
947
|
const isLikelyReply = args.draftSubject?.toString().toLowerCase().startsWith('re:');
|
|
856
948
|
if (isLikelyReply) {
|
|
857
949
|
logger.log('š SUGGESTION: Consider using "reply_draft" action instead of "draft" for threaded replies that appear in conversations');
|
|
858
950
|
}
|
|
859
951
|
const draftResult = await ms365Ops.saveDraftEmail({
|
|
860
|
-
to: args.draftTo,
|
|
861
|
-
cc: args.draftCc,
|
|
862
|
-
bcc: args.draftBcc,
|
|
952
|
+
to: normalizeDraftEmailArray(args.draftTo),
|
|
953
|
+
cc: normalizeDraftEmailArray(args.draftCc),
|
|
954
|
+
bcc: normalizeDraftEmailArray(args.draftBcc),
|
|
863
955
|
subject: args.draftSubject,
|
|
864
956
|
body: args.draftBody,
|
|
865
957
|
bodyType: args.draftBodyType || 'text',
|
|
@@ -884,13 +976,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
884
976
|
if (!args?.draftId) {
|
|
885
977
|
throw new Error("draftId is required for update_draft action");
|
|
886
978
|
}
|
|
979
|
+
// Helper function to normalize email arrays for draft updates
|
|
980
|
+
const normalizeUpdateEmailArray = (field) => {
|
|
981
|
+
if (!field)
|
|
982
|
+
return [];
|
|
983
|
+
if (typeof field === 'string')
|
|
984
|
+
return [field];
|
|
985
|
+
if (Array.isArray(field))
|
|
986
|
+
return field;
|
|
987
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
988
|
+
};
|
|
887
989
|
const updates = {};
|
|
888
990
|
if (args.draftTo)
|
|
889
|
-
updates.to = args.draftTo;
|
|
991
|
+
updates.to = normalizeUpdateEmailArray(args.draftTo);
|
|
890
992
|
if (args.draftCc)
|
|
891
|
-
updates.cc = args.draftCc;
|
|
993
|
+
updates.cc = normalizeUpdateEmailArray(args.draftCc);
|
|
892
994
|
if (args.draftBcc)
|
|
893
|
-
updates.bcc = args.draftBcc;
|
|
995
|
+
updates.bcc = normalizeUpdateEmailArray(args.draftBcc);
|
|
894
996
|
if (args.draftSubject)
|
|
895
997
|
updates.subject = args.draftSubject;
|
|
896
998
|
if (args.draftBody)
|
|
@@ -1018,7 +1120,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1018
1120
|
const graphClient = await enhancedMS365Auth.getGraphClient();
|
|
1019
1121
|
ms365Ops.setGraphClient(graphClient);
|
|
1020
1122
|
}
|
|
1021
|
-
|
|
1123
|
+
// Validate and normalize email input
|
|
1124
|
+
if (!args?.to) {
|
|
1125
|
+
throw new Error("'to' field is required for sending email");
|
|
1126
|
+
}
|
|
1127
|
+
if (!args?.subject) {
|
|
1128
|
+
throw new Error("'subject' field is required for sending email");
|
|
1129
|
+
}
|
|
1130
|
+
// Helper function to normalize email arrays
|
|
1131
|
+
const normalizeEmailArray = (field) => {
|
|
1132
|
+
if (!field)
|
|
1133
|
+
return [];
|
|
1134
|
+
if (typeof field === 'string')
|
|
1135
|
+
return [field];
|
|
1136
|
+
if (Array.isArray(field))
|
|
1137
|
+
return field;
|
|
1138
|
+
throw new Error(`Invalid email field format. Expected string or array of strings.`);
|
|
1139
|
+
};
|
|
1140
|
+
// Normalize the email message
|
|
1141
|
+
const emailMessage = {
|
|
1142
|
+
to: normalizeEmailArray(args.to),
|
|
1143
|
+
cc: normalizeEmailArray(args.cc),
|
|
1144
|
+
bcc: normalizeEmailArray(args.bcc),
|
|
1145
|
+
subject: args.subject,
|
|
1146
|
+
body: args.body || '',
|
|
1147
|
+
bodyType: args.bodyType || 'text',
|
|
1148
|
+
replyTo: args.replyTo,
|
|
1149
|
+
importance: args.importance || 'normal',
|
|
1150
|
+
attachments: args.attachments || []
|
|
1151
|
+
};
|
|
1152
|
+
const emailResult = await ms365Ops.sendEmail(emailMessage);
|
|
1022
1153
|
return {
|
|
1023
1154
|
content: [
|
|
1024
1155
|
{
|