UNPKG

outlook-mcp

Version:

Comprehensive MCP server for Claude to access Microsoft Outlook and Teams via Microsoft Graph API - including Email, Calendar, Contacts, Tasks, Teams, Chats, and Online Meetings

309 lines (263 loc) 8.3 kB
/** * Email drafts functionality */ const { callGraphAPI } = require('../utils/graph-api'); const { ensureAuthenticated } = require('../auth'); const config = require('../config'); /** * List email drafts handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleListDrafts(args) { try { const { count = config.DEFAULT_PAGE_SIZE } = args; // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Validate count parameter const validCount = Math.min(Math.max(1, count), config.MAX_RESULT_COUNT); // Build the API path const apiPath = 'me/mailFolders/drafts/messages'; // Build query parameters const queryParams = { '$select': config.EMAIL_SELECT_FIELDS, '$top': validCount, '$orderby': 'lastModifiedDateTime desc' }; console.error(`Fetching ${validCount} drafts`); // Make API call const response = await callGraphAPI(accessToken, 'GET', apiPath, null, queryParams); const drafts = response.value || []; return { content: [ { type: "text", text: `Found ${drafts.length} drafts:\n\n${drafts.map(draft => { const subject = draft.subject || 'No subject'; const to = draft.toRecipients && draft.toRecipients.length > 0 ? draft.toRecipients.map(r => r.emailAddress.address).join(', ') : 'No recipients'; const lastModified = draft.lastModifiedDateTime ? new Date(draft.lastModifiedDateTime).toLocaleString() : 'Unknown'; return `✏️ ${subject} ID: ${draft.id} To: ${to} Last Modified: ${lastModified} Preview: ${draft.bodyPreview || 'No preview'}`; }).join('\n\n')}` } ] }; } catch (error) { console.error('Error in handleListDrafts:', error); return { error: { code: -32603, message: `Failed to list drafts: ${error.message}` } }; } } /** * Create draft handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleCreateDraft(args) { try { const { to = '', cc = '', bcc = '', subject = '', body = '', importance = 'normal' } = args; // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Parse recipients const parseRecipients = (recipients) => { if (!recipients) return []; return recipients.split(',').map(email => ({ emailAddress: { address: email.trim() } })); }; // Build the draft object const draftData = { subject: subject, body: { contentType: 'HTML', content: body }, importance: importance, toRecipients: parseRecipients(to), ccRecipients: parseRecipients(cc), bccRecipients: parseRecipients(bcc) }; // Build the API path const apiPath = 'me/messages'; console.error(`Creating draft: ${subject}`); // Make API call const draft = await callGraphAPI(accessToken, 'POST', apiPath, draftData); return { content: [ { type: "text", text: `✅ Draft created successfully! **Draft ID:** ${draft.id} **Subject:** ${draft.subject} **To:** ${draft.toRecipients && draft.toRecipients.length > 0 ? draft.toRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **CC:** ${draft.ccRecipients && draft.ccRecipients.length > 0 ? draft.ccRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **BCC:** ${draft.bccRecipients && draft.bccRecipients.length > 0 ? draft.bccRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **Importance:** ${draft.importance} The draft has been saved and can be edited or sent later.` } ] }; } catch (error) { console.error('Error in handleCreateDraft:', error); return { error: { code: -32603, message: `Failed to create draft: ${error.message}` } }; } } /** * Update draft handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleUpdateDraft(args) { try { const { draftId, to, cc, bcc, subject, body, importance } = args; if (!draftId) { return { error: { code: -32602, message: "Draft ID is required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Parse recipients const parseRecipients = (recipients) => { if (!recipients) return undefined; return recipients.split(',').map(email => ({ emailAddress: { address: email.trim() } })); }; // Build the update object (only include provided fields) const updateData = {}; if (subject !== undefined) updateData.subject = subject; if (importance !== undefined) updateData.importance = importance; if (body !== undefined) { updateData.body = { contentType: 'HTML', content: body }; } const toRecipients = parseRecipients(to); const ccRecipients = parseRecipients(cc); const bccRecipients = parseRecipients(bcc); if (toRecipients !== undefined) updateData.toRecipients = toRecipients; if (ccRecipients !== undefined) updateData.ccRecipients = ccRecipients; if (bccRecipients !== undefined) updateData.bccRecipients = bccRecipients; // Ensure we have at least one field to update if (Object.keys(updateData).length === 0) { return { error: { code: -32602, message: "At least one field must be provided to update" } }; } // Build the API path const apiPath = `me/messages/${draftId}`; console.error(`Updating draft: ${draftId}`); // Make API call const updatedDraft = await callGraphAPI(accessToken, 'PATCH', apiPath, updateData); return { content: [ { type: "text", text: `✅ Draft updated successfully! **Draft ID:** ${updatedDraft.id} **Subject:** ${updatedDraft.subject} **To:** ${updatedDraft.toRecipients && updatedDraft.toRecipients.length > 0 ? updatedDraft.toRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **CC:** ${updatedDraft.ccRecipients && updatedDraft.ccRecipients.length > 0 ? updatedDraft.ccRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **BCC:** ${updatedDraft.bccRecipients && updatedDraft.bccRecipients.length > 0 ? updatedDraft.bccRecipients.map(r => r.emailAddress.address).join(', ') : 'None'} **Importance:** ${updatedDraft.importance} The draft has been updated with the provided information.` } ] }; } catch (error) { console.error('Error in handleUpdateDraft:', error); return { error: { code: -32603, message: `Failed to update draft: ${error.message}` } }; } } /** * Send draft handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleSendDraft(args) { try { const { draftId } = args; if (!draftId) { return { error: { code: -32602, message: "Draft ID is required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Build the API path const apiPath = `me/messages/${draftId}/send`; console.error(`Sending draft: ${draftId}`); // Make API call await callGraphAPI(accessToken, 'POST', apiPath, {}); return { content: [ { type: "text", text: `✅ Draft sent successfully! **Draft ID:** ${draftId} The draft has been sent and moved to your sent items.` } ] }; } catch (error) { console.error('Error in handleSendDraft:', error); return { error: { code: -32603, message: `Failed to send draft: ${error.message}` } }; } } module.exports = { handleListDrafts, handleCreateDraft, handleUpdateDraft, handleSendDraft };