ms365-mcp-server 1.1.15 → 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.
@@ -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
+ }