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.
163 lines
4.4 KiB
JavaScript
163 lines
4.4 KiB
JavaScript
/**
|
|
* Move emails functionality
|
|
*/
|
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
const { ensureAuthenticated } = require('../auth');
|
|
const { getFolderIdByName } = require('../email/folder-utils');
|
|
|
|
/**
|
|
* Move emails handler
|
|
* @param {object} args - Tool arguments
|
|
* @returns {object} - MCP response
|
|
*/
|
|
async function handleMoveEmails(args) {
|
|
const emailIds = args.emailIds || '';
|
|
const targetFolder = args.targetFolder || '';
|
|
const sourceFolder = args.sourceFolder || '';
|
|
|
|
if (!emailIds) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "Email IDs are required. Please provide a comma-separated list of email IDs to move."
|
|
}]
|
|
};
|
|
}
|
|
|
|
if (!targetFolder) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "Target folder name is required."
|
|
}]
|
|
};
|
|
}
|
|
|
|
try {
|
|
// Get access token
|
|
const accessToken = await ensureAuthenticated();
|
|
|
|
// Parse email IDs
|
|
const ids = emailIds.split(',').map(id => id.trim()).filter(id => id);
|
|
|
|
if (ids.length === 0) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: "No valid email IDs provided."
|
|
}]
|
|
};
|
|
}
|
|
|
|
// Move emails
|
|
const result = await moveEmailsToFolder(accessToken, ids, targetFolder, sourceFolder);
|
|
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: result.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 moving emails: ${error.message}`
|
|
}]
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move emails to a folder
|
|
* @param {string} accessToken - Access token
|
|
* @param {Array<string>} emailIds - Array of email IDs to move
|
|
* @param {string} targetFolderName - Name of the target folder
|
|
* @param {string} sourceFolderName - Name of the source folder (optional)
|
|
* @returns {Promise<object>} - Result object with status and message
|
|
*/
|
|
async function moveEmailsToFolder(accessToken, emailIds, targetFolderName, sourceFolderName) {
|
|
try {
|
|
// Get the target folder ID
|
|
const targetFolderId = await getFolderIdByName(accessToken, targetFolderName);
|
|
if (!targetFolderId) {
|
|
return {
|
|
success: false,
|
|
message: `Target folder "${targetFolderName}" not found. Please specify a valid folder name.`
|
|
};
|
|
}
|
|
|
|
// Track successful and failed moves
|
|
const results = {
|
|
successful: [],
|
|
failed: []
|
|
};
|
|
|
|
// Process each email one by one to handle errors independently
|
|
for (const emailId of emailIds) {
|
|
try {
|
|
// Move the email
|
|
await callGraphAPI(
|
|
accessToken,
|
|
'POST',
|
|
`me/messages/${emailId}/move`,
|
|
{
|
|
destinationId: targetFolderId
|
|
}
|
|
);
|
|
|
|
results.successful.push(emailId);
|
|
} catch (error) {
|
|
console.error(`Error moving email ${emailId}: ${error.message}`);
|
|
results.failed.push({
|
|
id: emailId,
|
|
error: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
// Generate result message
|
|
let message = '';
|
|
|
|
if (results.successful.length > 0) {
|
|
message += `Successfully moved ${results.successful.length} email(s) to "${targetFolderName}".`;
|
|
}
|
|
|
|
if (results.failed.length > 0) {
|
|
if (message) message += '\n\n';
|
|
message += `Failed to move ${results.failed.length} email(s). Errors:`;
|
|
|
|
// Show first few errors with details
|
|
const maxErrors = Math.min(results.failed.length, 3);
|
|
for (let i = 0; i < maxErrors; i++) {
|
|
const failure = results.failed[i];
|
|
message += `\n- Email ${i+1}: ${failure.error}`;
|
|
}
|
|
|
|
// If there are more errors, just mention the count
|
|
if (results.failed.length > maxErrors) {
|
|
message += `\n...and ${results.failed.length - maxErrors} more.`;
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: results.successful.length > 0,
|
|
message,
|
|
results
|
|
};
|
|
} catch (error) {
|
|
console.error(`Error in moveEmailsToFolder: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
module.exports = handleMoveEmails;
|