ms365-mcp-server 1.1.16 → 1.1.17
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 +1122 -9
- package/dist/utils/batch-performance-monitor.js +106 -0
- package/dist/utils/batch-test-scenarios.js +277 -0
- package/dist/utils/context-aware-search.js +499 -0
- package/dist/utils/cross-reference-detector.js +352 -0
- package/dist/utils/document-workflow.js +433 -0
- package/dist/utils/enhanced-fuzzy-search.js +514 -0
- package/dist/utils/error-handler.js +337 -0
- package/dist/utils/intelligence-engine.js +71 -0
- package/dist/utils/intelligent-cache.js +379 -0
- package/dist/utils/large-mailbox-search.js +599 -0
- package/dist/utils/ms365-operations.js +657 -181
- package/dist/utils/performance-monitor.js +395 -0
- package/dist/utils/proactive-intelligence.js +390 -0
- package/dist/utils/rate-limiter.js +284 -0
- package/dist/utils/search-batch-pipeline.js +222 -0
- package/dist/utils/thread-reconstruction.js +700 -0
- package/package.json +1 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { logger } from './api.js';
|
|
2
|
+
export class SearchBatchPipeline {
|
|
3
|
+
constructor(ms365Ops) {
|
|
4
|
+
this.ms365Ops = ms365Ops;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Execute a complete search-to-batch pipeline
|
|
8
|
+
* Combines search and batch operations for maximum efficiency
|
|
9
|
+
*/
|
|
10
|
+
async executeSearchBatchPipeline(options) {
|
|
11
|
+
const pipelineStartTime = Date.now();
|
|
12
|
+
logger.log(`🔄 SEARCH-BATCH PIPELINE: Starting ${options.batchAction} pipeline`);
|
|
13
|
+
logger.log(`🔍 Search criteria:`, JSON.stringify(options.searchCriteria, null, 2));
|
|
14
|
+
// Step 1: Perform optimized search
|
|
15
|
+
const searchStartTime = Date.now();
|
|
16
|
+
const searchResults = await this.ms365Ops.searchEmails({
|
|
17
|
+
...options.searchCriteria,
|
|
18
|
+
maxResults: options.maxResults || 50
|
|
19
|
+
});
|
|
20
|
+
const searchTime = Date.now() - searchStartTime;
|
|
21
|
+
logger.log(`🔍 Search completed: ${searchResults.messages.length} emails found in ${searchTime}ms`);
|
|
22
|
+
if (searchResults.messages.length === 0) {
|
|
23
|
+
return this.createEmptyPipelineResult(pipelineStartTime, searchTime);
|
|
24
|
+
}
|
|
25
|
+
// Step 2: Execute batch operation
|
|
26
|
+
const batchStartTime = Date.now();
|
|
27
|
+
const messageIds = searchResults.messages.map(email => email.id);
|
|
28
|
+
let batchResults;
|
|
29
|
+
let batchHttpCalls = 0;
|
|
30
|
+
let successCount = 0;
|
|
31
|
+
let errorCount = 0;
|
|
32
|
+
switch (options.batchAction) {
|
|
33
|
+
case 'retrieve':
|
|
34
|
+
batchResults = await this.ms365Ops.getEmailsBatch(messageIds, options.includeAttachments || false);
|
|
35
|
+
batchHttpCalls = Math.ceil(messageIds.length / 20); // 20 per batch
|
|
36
|
+
successCount = batchResults.length;
|
|
37
|
+
break;
|
|
38
|
+
case 'mark_read':
|
|
39
|
+
case 'mark_unread':
|
|
40
|
+
const markOperations = messageIds.map((id, index) => ({
|
|
41
|
+
id: `mark_${index}`,
|
|
42
|
+
operation: 'mark',
|
|
43
|
+
messageId: id,
|
|
44
|
+
params: { isRead: options.batchAction === 'mark_read' }
|
|
45
|
+
}));
|
|
46
|
+
const markResults = await this.ms365Ops.batchEmailOperations(markOperations);
|
|
47
|
+
batchResults = Array.from(markResults.entries());
|
|
48
|
+
batchHttpCalls = Math.ceil(markOperations.length / 20);
|
|
49
|
+
for (const [id, result] of markResults) {
|
|
50
|
+
if (result.success)
|
|
51
|
+
successCount++;
|
|
52
|
+
else
|
|
53
|
+
errorCount++;
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
case 'move':
|
|
57
|
+
if (!options.batchParams?.destinationFolderId) {
|
|
58
|
+
throw new Error('destinationFolderId is required for move operations');
|
|
59
|
+
}
|
|
60
|
+
const moveOperations = messageIds.map((id, index) => ({
|
|
61
|
+
id: `move_${index}`,
|
|
62
|
+
operation: 'move',
|
|
63
|
+
messageId: id,
|
|
64
|
+
params: { destinationFolderId: options.batchParams.destinationFolderId }
|
|
65
|
+
}));
|
|
66
|
+
const moveResults = await this.ms365Ops.batchEmailOperations(moveOperations);
|
|
67
|
+
batchResults = Array.from(moveResults.entries());
|
|
68
|
+
batchHttpCalls = Math.ceil(moveOperations.length / 20);
|
|
69
|
+
for (const [id, result] of moveResults) {
|
|
70
|
+
if (result.success)
|
|
71
|
+
successCount++;
|
|
72
|
+
else
|
|
73
|
+
errorCount++;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case 'delete':
|
|
77
|
+
const deleteOperations = messageIds.map((id, index) => ({
|
|
78
|
+
id: `delete_${index}`,
|
|
79
|
+
operation: 'delete',
|
|
80
|
+
messageId: id,
|
|
81
|
+
params: {}
|
|
82
|
+
}));
|
|
83
|
+
const deleteResults = await this.ms365Ops.batchEmailOperations(deleteOperations);
|
|
84
|
+
batchResults = Array.from(deleteResults.entries());
|
|
85
|
+
batchHttpCalls = Math.ceil(deleteOperations.length / 20);
|
|
86
|
+
for (const [id, result] of deleteResults) {
|
|
87
|
+
if (result.success)
|
|
88
|
+
successCount++;
|
|
89
|
+
else
|
|
90
|
+
errorCount++;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`Unknown batch action: ${options.batchAction}`);
|
|
95
|
+
}
|
|
96
|
+
const batchTime = Date.now() - batchStartTime;
|
|
97
|
+
const totalTime = Date.now() - pipelineStartTime;
|
|
98
|
+
// Calculate performance improvements
|
|
99
|
+
const traditionalCalls = messageIds.length + 1; // search + individual operations
|
|
100
|
+
const batchedCalls = 1 + batchHttpCalls; // search + batch operations
|
|
101
|
+
const callReduction = ((traditionalCalls - batchedCalls) / traditionalCalls) * 100;
|
|
102
|
+
const estimatedTraditionalTime = traditionalCalls * 150; // Estimate 150ms per call
|
|
103
|
+
const result = {
|
|
104
|
+
searchResults: {
|
|
105
|
+
foundEmails: searchResults.messages.length,
|
|
106
|
+
searchTime
|
|
107
|
+
},
|
|
108
|
+
batchResults: {
|
|
109
|
+
processedEmails: messageIds.length,
|
|
110
|
+
batchTime,
|
|
111
|
+
httpCalls: batchHttpCalls,
|
|
112
|
+
successCount,
|
|
113
|
+
errorCount
|
|
114
|
+
},
|
|
115
|
+
totalTime,
|
|
116
|
+
performanceImprovement: {
|
|
117
|
+
traditionalCalls,
|
|
118
|
+
batchedCalls,
|
|
119
|
+
callReduction,
|
|
120
|
+
timeEstimate: `${estimatedTraditionalTime}ms traditional vs ${totalTime}ms batched`
|
|
121
|
+
},
|
|
122
|
+
results: batchResults
|
|
123
|
+
};
|
|
124
|
+
logger.log(`🚀 PIPELINE COMPLETED: ${successCount} successful, ${errorCount} errors in ${totalTime}ms`);
|
|
125
|
+
logger.log(`📊 Performance: ${callReduction.toFixed(1)}% fewer HTTP calls`);
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Smart search-to-batch workflow for large folders
|
|
130
|
+
* Automatically optimizes for folders with many emails
|
|
131
|
+
*/
|
|
132
|
+
async smartSearchAndProcess(searchCriteria, batchAction, batchParams) {
|
|
133
|
+
// Check if searching in a large folder
|
|
134
|
+
if (searchCriteria.folder) {
|
|
135
|
+
const folders = await this.ms365Ops.findFolderByName(searchCriteria.folder);
|
|
136
|
+
if (folders.length > 0 && folders[0].totalItemCount > 10000) {
|
|
137
|
+
logger.log(`🏠 Large folder detected (${folders[0].totalItemCount} emails). Using optimized pipeline.`);
|
|
138
|
+
// For large folders, use progressive search with smaller batches
|
|
139
|
+
return await this.executeSearchBatchPipeline({
|
|
140
|
+
searchCriteria: {
|
|
141
|
+
...searchCriteria,
|
|
142
|
+
maxResults: 25 // Smaller batches for large folders
|
|
143
|
+
},
|
|
144
|
+
batchAction,
|
|
145
|
+
batchParams,
|
|
146
|
+
maxResults: 25
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Standard pipeline for normal folders
|
|
151
|
+
return await this.executeSearchBatchPipeline({
|
|
152
|
+
searchCriteria,
|
|
153
|
+
batchAction,
|
|
154
|
+
batchParams,
|
|
155
|
+
maxResults: 50
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Batch retrieve with search integration
|
|
160
|
+
* Optimized for getting full email details after search
|
|
161
|
+
*/
|
|
162
|
+
async searchAndRetrieveFull(searchCriteria, includeAttachments = false) {
|
|
163
|
+
const result = await this.executeSearchBatchPipeline({
|
|
164
|
+
searchCriteria,
|
|
165
|
+
batchAction: 'retrieve',
|
|
166
|
+
includeAttachments,
|
|
167
|
+
maxResults: 50
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
searchSummary: `Found ${result.searchResults.foundEmails} emails, retrieved full details for ${result.batchResults.successCount}`,
|
|
171
|
+
emails: result.results,
|
|
172
|
+
performance: result.performanceImprovement
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Quick email processing workflows
|
|
177
|
+
*/
|
|
178
|
+
async quickWorkflows() {
|
|
179
|
+
return {
|
|
180
|
+
// Mark all unread emails from specific sender as read
|
|
181
|
+
markSenderEmailsRead: async (senderEmail) => {
|
|
182
|
+
return await this.smartSearchAndProcess({ from: senderEmail, isUnread: true }, 'mark_read');
|
|
183
|
+
},
|
|
184
|
+
// Move all emails with specific subject to folder
|
|
185
|
+
moveEmailsBySubject: async (subject, destinationFolderId) => {
|
|
186
|
+
return await this.smartSearchAndProcess({ subject }, 'move', { destinationFolderId });
|
|
187
|
+
},
|
|
188
|
+
// Delete old emails (older than date)
|
|
189
|
+
deleteOldEmails: async (beforeDate) => {
|
|
190
|
+
return await this.smartSearchAndProcess({ before: beforeDate }, 'delete');
|
|
191
|
+
},
|
|
192
|
+
// Get all emails with attachments from specific sender
|
|
193
|
+
getEmailsWithAttachments: async (senderEmail) => {
|
|
194
|
+
return await this.searchAndRetrieveFull({ from: senderEmail, hasAttachment: true }, true);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
createEmptyPipelineResult(pipelineStartTime, searchTime) {
|
|
199
|
+
const totalTime = Date.now() - pipelineStartTime;
|
|
200
|
+
return {
|
|
201
|
+
searchResults: {
|
|
202
|
+
foundEmails: 0,
|
|
203
|
+
searchTime
|
|
204
|
+
},
|
|
205
|
+
batchResults: {
|
|
206
|
+
processedEmails: 0,
|
|
207
|
+
batchTime: 0,
|
|
208
|
+
httpCalls: 0,
|
|
209
|
+
successCount: 0,
|
|
210
|
+
errorCount: 0
|
|
211
|
+
},
|
|
212
|
+
totalTime,
|
|
213
|
+
performanceImprovement: {
|
|
214
|
+
traditionalCalls: 1,
|
|
215
|
+
batchedCalls: 1,
|
|
216
|
+
callReduction: 0,
|
|
217
|
+
timeEstimate: 'No emails found to process'
|
|
218
|
+
},
|
|
219
|
+
results: []
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|