UNPKG

msexchange-mcp

Version:

MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API

139 lines 6.69 kB
import { z } from 'zod'; import { MailService } from '../mail/mailService.js'; import accountsConfig from '../config/accountsLoader.js'; export const queryEmailsBatchTool = { name: 'query_emails_batch', description: 'Query emails in batches to handle large requests efficiently while respecting token limits. Note: When using search, orderBy is not supported due to Graph API limitations.', parameters: z.object({ user_id: z.string().describe('The user ID or email address'), search: z.string().optional().describe('Search query (searches subject, body, from)'), filter: z.string().optional().describe('OData filter expression'), folder: z.string().optional().describe('Folder name to search in (e.g., Inbox, Sent Items)'), total_emails: z.number().max(500).optional().default(50).describe('Total number of emails to retrieve (max 500)'), order_by: z.string().optional().default('receivedDateTime DESC').describe('Sort order (not supported when using search parameter)'), include_body: z.boolean().optional().default(false).describe('Include full email body (automatically batches smaller)'), batch_size: z.number().optional().describe('Override automatic batch sizing'), }), execute: async (params) => { try { const account = accountsConfig.accounts.find(acc => acc.id === params.user_id || acc.email === params.user_id); if (!account) { throw new Error(`User not found: ${params.user_id}`); } const totalEmails = params.total_emails || 50; // Smart batch sizing based on content let batchSize = params.batch_size; if (!batchSize) { if (params.include_body) { batchSize = 3; // Very small batches when including body to stay under token limit } else { batchSize = 50; // Larger batches for metadata only } } const mailService = new MailService(account.email); // Calculate number of batches needed const numBatches = Math.ceil(totalEmails / batchSize); const allEmails = []; let totalProcessed = 0; console.error(`📊 Batch query: ${totalEmails} emails in ${numBatches} batches of ${batchSize}`); // Execute batches with delay to avoid rate limiting for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) { const skip = batchIndex * batchSize; const take = Math.min(batchSize, totalEmails - totalProcessed); if (take <= 0) break; console.error(`📦 Processing batch ${batchIndex + 1}/${numBatches} (skip: ${skip}, take: ${take})`); // Select fields const selectFields = [ 'id', 'subject', 'from', 'toRecipients', 'receivedDateTime', 'sentDateTime', 'hasAttachments', 'importance', 'isRead', 'isDraft', 'bodyPreview', 'categories', 'conversationId', ]; if (params.include_body) { selectFields.push('body'); } try { const emails = await mailService.queryEmails({ search: params.search, filter: params.filter, top: take, skip: skip, orderBy: params.order_by, select: selectFields, }); allEmails.push(...emails); totalProcessed += emails.length; // If we got fewer emails than requested, we've hit the end if (emails.length < take) { console.error(`📋 Reached end of results at batch ${batchIndex + 1}`); break; } // Add delay between batches to be nice to the API if (batchIndex < numBatches - 1) { console.error(`⏰ Waiting 500ms before next batch...`); await new Promise(resolve => setTimeout(resolve, 500)); } } catch (error) { console.error(`❌ Batch ${batchIndex + 1} failed: ${error}`); // Continue with partial results break; } } // Create summary const summary = { totalRequested: totalEmails, totalRetrieved: allEmails.length, batchesProcessed: Math.floor(totalProcessed / batchSize) + (totalProcessed % batchSize > 0 ? 1 : 0), batchSize: batchSize, includesBody: params.include_body, tokenOptimized: true, }; return { content: [ { type: 'text', text: JSON.stringify({ summary, emails: allEmails.map(email => ({ id: email.id, subject: email.subject, from: email.from?.emailAddress?.address, fromName: email.from?.emailAddress?.name, to: email.toRecipients?.map((r) => r.emailAddress.address), receivedDateTime: email.receivedDateTime, hasAttachments: email.hasAttachments, isRead: email.isRead, preview: email.bodyPreview, categories: email.categories, body: params.include_body ? email.body : undefined, })), }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error in batch query: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], }; } }, }; //# sourceMappingURL=queryEmailsBatch.js.map