- Handle empty 2xx Graph API responses without JSON parse errors - Fix list-rules handler import (handleListRules named export) - Add timezone-aware display formatting using MS_TIMEZONE / DEFAULT_TIMEZONE - Return created event ID in create-event success message - Change default timezone from Windows name to IANA (America/New_York)
156 lines
No EOL
5 KiB
JavaScript
156 lines
No EOL
5 KiB
JavaScript
/**
|
|
* Read multiple emails functionality
|
|
*/
|
|
const config = require('../config');
|
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
const { ensureAuthenticated } = require('../auth');
|
|
const { cleanBody } = require('../utils/bodyParser');
|
|
const { formatDateTime } = require('../utils/time-formatter');
|
|
|
|
/**
|
|
* Format a single email for display
|
|
* @param {object} email - Email object from Graph API
|
|
* @param {string} emailId - Email ID for error context
|
|
* @returns {string} - Formatted email text
|
|
*/
|
|
function formatEmail(email, emailId) {
|
|
if (!email) {
|
|
return `Email ID ${emailId}: Not found or inaccessible`;
|
|
}
|
|
|
|
try {
|
|
// Format sender, recipients, etc.
|
|
const sender = email.from ? `${email.from.emailAddress.name} (${email.from.emailAddress.address})` : 'Unknown';
|
|
const to = email.toRecipients ? email.toRecipients.map(r => `${r.emailAddress.name} (${r.emailAddress.address})`).join(", ") : 'None';
|
|
const cc = email.ccRecipients && email.ccRecipients.length > 0 ? email.ccRecipients.map(r => `${r.emailAddress.name} (${r.emailAddress.address})`).join(", ") : 'None';
|
|
const bcc = email.bccRecipients && email.bccRecipients.length > 0 ? email.bccRecipients.map(r => `${r.emailAddress.name} (${r.emailAddress.address})`).join(", ") : 'None';
|
|
const date = formatDateTime(email.receivedDateTime);
|
|
|
|
// Extract and clean body content (cleanBody handles both HTML and plain text)
|
|
let body = '';
|
|
if (email.body) {
|
|
body = cleanBody(email.body.content);
|
|
} else {
|
|
body = cleanBody(email.bodyPreview) || 'No content';
|
|
}
|
|
|
|
// Format the email
|
|
return `From: ${sender}
|
|
To: ${to}
|
|
${cc !== 'None' ? `CC: ${cc}\n` : ''}${bcc !== 'None' ? `BCC: ${bcc}\n` : ''}Subject: ${email.subject}
|
|
Date: ${date}
|
|
Importance: ${email.importance || 'normal'}
|
|
Has Attachments: ${email.hasAttachments ? 'Yes' : 'No'}
|
|
|
|
${body}`;
|
|
} catch (error) {
|
|
return `Email ID ${emailId}: Error formatting email - ${error.message}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read multiple emails handler
|
|
* @param {object} args - Tool arguments
|
|
* @returns {object} - MCP response
|
|
*/
|
|
async function handleReadMultipleEmails(args) {
|
|
const emailIds = args.ids;
|
|
|
|
if (!emailIds || !Array.isArray(emailIds) || emailIds.length === 0) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "Email IDs array is required and must contain at least one ID."
|
|
}]
|
|
};
|
|
}
|
|
|
|
// Limit the number of emails to prevent overwhelming responses
|
|
const maxEmails = 10;
|
|
if (emailIds.length > maxEmails) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: `Too many email IDs provided. Maximum allowed is ${maxEmails}, but ${emailIds.length} were provided.`
|
|
}]
|
|
};
|
|
}
|
|
|
|
try {
|
|
// Get access token
|
|
const accessToken = await ensureAuthenticated();
|
|
|
|
// Create concurrent API calls for all email IDs
|
|
const emailPromises = emailIds.map(async (emailId) => {
|
|
try {
|
|
const endpoint = `me/messages/${encodeURIComponent(emailId)}`;
|
|
const queryParams = {
|
|
$select: config.EMAIL_DETAIL_FIELDS
|
|
};
|
|
|
|
const email = await callGraphAPI(accessToken, 'GET', endpoint, null, queryParams);
|
|
return { emailId, email, error: null };
|
|
} catch (error) {
|
|
console.error(`Error reading email ${emailId}: ${error.message}`);
|
|
return { emailId, email: null, error: error.message };
|
|
}
|
|
});
|
|
|
|
// Wait for all API calls to complete
|
|
const results = await Promise.all(emailPromises);
|
|
|
|
// Format all emails
|
|
const formattedEmails = results.map((result, index) => {
|
|
const emailNumber = index + 1;
|
|
const separator = "=".repeat(80);
|
|
|
|
if (result.error) {
|
|
return `${separator}
|
|
EMAIL ${emailNumber} (ID: ${result.emailId})
|
|
${separator}
|
|
Error: ${result.error}`;
|
|
} else {
|
|
const formattedEmail = formatEmail(result.email, result.emailId);
|
|
return `${separator}
|
|
EMAIL ${emailNumber} (ID: ${result.emailId})
|
|
${separator}
|
|
${formattedEmail}`;
|
|
}
|
|
});
|
|
|
|
// Count successful vs failed reads
|
|
const successCount = results.filter(r => !r.error && r.email).length;
|
|
const errorCount = results.filter(r => r.error || !r.email).length;
|
|
|
|
const summary = `Retrieved ${successCount} email(s) successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}.
|
|
|
|
`;
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: summary + formattedEmails.join('\n\n')
|
|
}
|
|
]
|
|
};
|
|
} catch (error) {
|
|
if (error.message === 'Authentication required') {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "Authentication required. Please use the 'authenticate' tool first."
|
|
}]
|
|
};
|
|
}
|
|
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: `Error accessing emails: ${error.message}`
|
|
}]
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = handleReadMultipleEmails; |