msexchange-mcp
Version:
MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API
212 lines (207 loc) • 9.34 kB
JavaScript
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(/ /g, ' ')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/&
.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