UNPKG

msexchange-mcp

Version:

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

133 lines (129 loc) 6.9 kB
import { z } from 'zod'; import { SmartQueryBuilder } from '../mail/queryBuilder.js'; import accountsConfig from '../config/accountsLoader.js'; export const queryEmailsTool = { name: 'query_emails', description: `Query emails with intelligent query handling and automatic optimization. Features: - Natural language search: "emails from John about the project" - OData filters: "from eq 'john@example.com' and hasAttachments eq true" - Structured queries with from, to, subject filters - Automatic query simplification for complex filters - Intelligent fallback strategies (AND → OR → filter-only) Search parameters: - search: Natural language query (searches subject, body, from) - filter: OData filter expression - from/to/subject: Quick filters for common searches - folder: Search within specific folder (e.g., "Inbox", "Sent Items") - include_body: Include full email content (default: false) - limit: Max results (default: 10, max: 100) - order_by: Sort order (not available with search parameter) Examples: - Recent emails: {"order_by": "receivedDateTime DESC", "limit": 20} - Unread with attachments: {"filter": "isRead eq false and hasAttachments eq true"} - From specific sender: {"from": "client@company.com", "folder": "Inbox"} - Natural search: {"search": "invoice payment december"} Best practices: - Use 'search' for natural language queries - Use 'filter' for precise OData queries - Combine quick filters (from/to/subject) for simple searches - Set include_body=false for better performance - Use query_emails_batch for large result sets`, parameters: z.object({ user_id: z.string().describe('The user ID or email address'), search: z.string().optional().describe('Natural language search (searches subject, body, from). Cannot be combined with orderBy.'), filter: z.string().optional().describe('OData filter expression. Complex filters may be automatically simplified.'), from: z.string().optional().describe('Filter by sender email address'), to: z.string().optional().describe('Filter by recipient email address'), subject: z.string().optional().describe('Filter by subject (partial match)'), folder: z.string().optional().describe('Folder name to search in (e.g., Inbox, Sent Items)'), limit: z.number().max(100).optional().default(10).describe('Maximum number of emails to return (max 100 for token efficiency)'), skip: z.number().optional().default(0).describe('Number of emails to skip'), 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'), }), 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}`); } // Validate and cap the limit to prevent token overflow issues // When including body, limit to 5 emails to stay under 25k token limit // Without body, allow up to 100 emails const maxLimit = params.include_body ? 5 : 100; const safeLimit = Math.min(params.limit || 10, maxLimit); if (params.limit && params.limit > maxLimit) { console.error(`⚠️ Requested limit ${params.limit} exceeds maximum of ${maxLimit} ${params.include_body ? '(with body)' : '(without body)'}, capping to ${maxLimit}`); } const queryBuilder = new SmartQueryBuilder(account.email); // Build query options const queryOptions = { search: params.search, filter: params.filter, from: params.from, to: params.to, subject: params.subject, folder: params.folder, limit: safeLimit, skip: params.skip, orderBy: params.order_by, includeBody: params.include_body, }; // Execute query with smart handling const result = await queryBuilder.executeQuery(queryOptions); const emails = result.emails; // Log warnings if any if (result.warnings) { result.warnings.forEach(warning => console.error(`⚠️ ${warning}`)); } return { content: [ { type: 'text', text: JSON.stringify({ queryStrategy: result.strategy, warnings: result.warnings, emails: emails.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, })), count: emails.length, hasMore: emails.length === safeLimit, requestedLimit: params.limit, actualLimit: safeLimit, tokenOptimized: true, maxLimitWithBody: 5, maxLimitWithoutBody: 100, paginationAdvice: params.include_body && (params.limit || 10) > 5 ? "For large queries with email bodies, use multiple smaller requests with pagination" : emails.length === safeLimit ? "Use skip parameter for pagination: skip=" + (params.skip || 0) + safeLimit : undefined, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error querying emails: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], }; } }, }; //# sourceMappingURL=queryEmails.js.map