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
JavaScript
/**
* 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
};