MCP server providing Microsoft Outlook integration via Graph API: - Email: list, search, read, send, thread reconstruction - Calendar: list, create, decline, cancel, delete events - Folders: list, create, move emails - Inbox rules: list, create, reorder - MSAL device code flow auth with persistent token cache - Test mode with mock data - Comprehensive README with setup, config, and tool reference 20 MCP tools across 5 modules. Node.js >= 14. MIT license.
126 lines
3.7 KiB
JavaScript
126 lines
3.7 KiB
JavaScript
/**
|
|
* Read email functionality
|
|
*/
|
|
const config = require('../config');
|
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
const { ensureAuthenticated } = require('../auth');
|
|
const { cleanBody } = require('../utils/bodyParser');
|
|
|
|
/**
|
|
* Read email handler
|
|
* @param {object} args - Tool arguments
|
|
* @returns {object} - MCP response
|
|
*/
|
|
async function handleReadEmail(args) {
|
|
const emailId = args.id;
|
|
|
|
if (!emailId) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "Email ID is required."
|
|
}]
|
|
};
|
|
}
|
|
|
|
try {
|
|
// Get access token
|
|
const accessToken = await ensureAuthenticated();
|
|
|
|
// Make API call to get email details
|
|
const endpoint = `me/messages/${encodeURIComponent(emailId)}`;
|
|
const queryParams = {
|
|
$select: config.EMAIL_DETAIL_FIELDS
|
|
};
|
|
|
|
try {
|
|
const email = await callGraphAPI(accessToken, 'GET', endpoint, null, queryParams);
|
|
|
|
if (!email) {
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Email with ID ${emailId} not found.`
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
// 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 = new Date(email.receivedDateTime).toLocaleString();
|
|
|
|
// 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
|
|
const formattedEmail = `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}`;
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: formattedEmail
|
|
}
|
|
]
|
|
};
|
|
} catch (error) {
|
|
console.error(`Error reading email: ${error.message}`);
|
|
|
|
// Improved error handling with more specific messages
|
|
if (error.message.includes("doesn't belong to the targeted mailbox")) {
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `The email ID seems invalid or doesn't belong to your mailbox. Please try with a different email ID.`
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: `Failed to read email: ${error.message}`
|
|
}
|
|
]
|
|
};
|
|
}
|
|
}
|
|
} 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 email: ${error.message}`
|
|
}]
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = handleReadEmail;
|