UNPKG

msexchange-mcp

Version:

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

212 lines (207 loc) 9.34 kB
import { z } from 'zod'; import { MailService } from '../mail/mailService.js'; import accountsConfig from '../config/accountsLoader.js'; export const getEmailContentTool = { name: 'get_email_content', description: `Fetch full content of a specific email with granular control over data returned. Features: - Control what data to retrieve (body, headers, attachments) - Choose body format (text, HTML, or best available) - Set max body length to manage tokens - Optimize for different use cases Parameters: - include_body: Retrieve email body (default: true) - include_headers: Include email headers (default: false) - include_attachments: Include attachment info (default: true) - body_format: "text", "html", or "best" (default: "best") - max_body_length: Truncate body if longer (useful for previews) Use cases: - Full email analysis: All parameters true - Quick preview: include_body=true, max_body_length=500 - Attachment check: include_body=false, include_attachments=true - Header inspection: include_headers=true, include_body=false Examples: - Full content: {"message_id": "abc123", "include_headers": true} - Preview only: {"message_id": "abc123", "max_body_length": 200} - Just attachments: {"message_id": "abc123", "include_body": false} Best practices: - Use max_body_length for token efficiency - Set include_headers=false unless specifically needed - Choose appropriate body_format for your use case - Consider using get_email_by_id for basic info`, parameters: z.object({ user_id: z.string().describe('The user ID or email address'), message_id: z.string().describe('The email message ID'), include_body: z.boolean().optional().default(true).describe('Include full email body'), body_format: z.enum(['text', 'html', 'best']).optional().default('best').describe('Preferred body format'), include_attachments: z.boolean().optional().default(true).describe('Include attachment details'), include_headers: z.boolean().optional().default(false).describe('Include email headers'), max_body_length: z.number().optional().describe('Maximum characters for body (truncates if longer)'), }), 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 mailService = new MailService(account.email); // Fetch the email const email = await mailService.getEmailById(params.message_id); // Process body based on parameters let body = null; let bodyTruncated = false; if (params.include_body && email.body) { const bodyContent = email.body.content || ''; // Handle format preference if (params.body_format === 'text' && email.body.contentType === 'html') { // Simple HTML to text conversion body = { contentType: 'text', content: bodyContent .replace(/<br\s*\/?>/gi, '\n') .replace(/<\/p>/gi, '\n\n') .replace(/<[^>]+>/g, '') .replace(/&nbsp;/g, ' ') .replace(/&amp;/g, '&') .replace(/&lt;/g, '<') .replace(/&gt;/g, '>') .replace(/&quot;/g, '"') .replace(/&#39;/g, "'") .trim() }; } else { body = email.body; } // Truncate if requested if (params.max_body_length && body.content.length > params.max_body_length) { body.content = body.content.substring(0, params.max_body_length) + '...'; bodyTruncated = true; } } // Build response object const response = { id: email.id, conversationId: email.conversationId, subject: email.subject, from: { email: email.from?.emailAddress?.address, name: email.from?.emailAddress?.name, }, to: email.toRecipients?.map(r => ({ email: r.emailAddress.address, name: r.emailAddress.name })), cc: email.ccRecipients?.map(r => ({ email: r.emailAddress.address, name: r.emailAddress.name })), bcc: email.bccRecipients?.map(r => ({ email: r.emailAddress.address, name: r.emailAddress.name })), receivedDateTime: email.receivedDateTime, sentDateTime: email.sentDateTime, importance: email.importance, isRead: email.isRead, isDraft: email.isDraft, hasAttachments: email.hasAttachments, categories: email.categories, }; // Add body if requested if (params.include_body) { response.body = body; response.bodyPreview = email.bodyPreview; if (bodyTruncated) { response.bodyTruncated = true; response.originalBodyLength = email.body?.content?.length; } } // Add attachments if requested if (params.include_attachments && email.hasAttachments) { try { // Fetch attachment details const attachments = email.attachments || []; response.attachments = attachments.map((att) => ({ id: att.id, name: att.name, contentType: att.contentType, size: att.size, isInline: att.isInline, contentId: att.contentId, })); } catch (error) { response.attachments = { error: 'Unable to fetch attachment details' }; } } // Add headers if requested if (params.include_headers) { if (email.internetMessageHeaders) { response.headers = email.internetMessageHeaders.reduce((acc, header) => { acc[header.name] = header.value; return acc; }, {}); } else { response.headers = { note: 'Headers not available for this email' }; } } // Add metadata response.metadata = { fetchedAt: new Date().toISOString(), tokenEfficient: !params.include_body || !!params.max_body_length, contentSize: { subject: email.subject?.length || 0, body: email.body?.content?.length || 0, preview: email.bodyPreview?.length || 0, } }; // Add related actions response.relatedActions = [ 'Use list_attachments to see all attachments', 'Use get_attachment to download specific attachments', 'Use reply_email to reply to this email', 'Use forward_email to forward this email', 'Use find_conversation to see the full thread' ]; return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error) { // Provide helpful error recovery const suggestions = []; if (error instanceof Error) { if (error.message.includes('token')) { suggestions.push('Try with include_body=false first'); suggestions.push('Use max_body_length to limit content size'); suggestions.push('Fetch attachments separately'); } else if (error.message.includes('not found')) { suggestions.push('Verify the message ID is correct'); suggestions.push('Check if the email still exists'); suggestions.push('Try searching for the email again'); } } return { content: [ { type: 'text', text: JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error', messageId: params.message_id, suggestions }, null, 2), }, ], }; } }, }; //# sourceMappingURL=getEmailContent.js.map