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

405 lines (345 loc) 12.3 kB
/** * Teams meetings functionality */ const { callGraphAPI } = require('../utils/graph-api'); const { ensureAuthenticated } = require('../auth'); const config = require('../config'); /** * List online meetings handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleListOnlineMeetings(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/onlineMeetings'; // Build query parameters const queryParams = { '$select': 'id,subject,startDateTime,endDateTime,joinWebUrl,participants,isEntryExitAnnounced,allowedPresenters,audioConferencing', '$top': validCount, '$orderby': 'startDateTime desc' }; console.error(`Fetching ${validCount} online meetings`); // Make API call const response = await callGraphAPI(accessToken, 'GET', apiPath, null, queryParams); const meetings = response.value || []; return { content: [ { type: "text", text: `Found ${meetings.length} online meetings:\n\n${meetings.map(meeting => { const subject = meeting.subject || 'No subject'; const start = meeting.startDateTime ? new Date(meeting.startDateTime).toLocaleString() : 'Not set'; const end = meeting.endDateTime ? new Date(meeting.endDateTime).toLocaleString() : 'Not set'; const presenters = meeting.allowedPresenters || 'Unknown'; const hasAudio = meeting.audioConferencing ? '🔊' : '🔇'; return `📹 ${subject} ID: ${meeting.id} Start: ${start} End: ${end} Presenters: ${presenters} ${hasAudio} Audio: ${meeting.audioConferencing ? 'Enabled' : 'Disabled'} Join URL: ${meeting.joinWebUrl || 'Not available'}`; }).join('\n\n')}` } ] }; } catch (error) { console.error('Error in handleListOnlineMeetings:', error); return { error: { code: -32603, message: `Failed to list online meetings: ${error.message}` } }; } } /** * Create online meeting handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleCreateOnlineMeeting(args) { try { const { subject, startDateTime, endDateTime, allowedPresenters = 'organizer', isEntryExitAnnounced = false, allowAttendeeToEnableCamera = true, allowAttendeeToEnableMic = true, allowTeamworkReactions = true } = args; if (!subject || !startDateTime || !endDateTime) { return { error: { code: -32602, message: "Subject, start time, and end time are required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Build the meeting object const meetingData = { subject: subject, startDateTime: startDateTime, endDateTime: endDateTime, allowedPresenters: allowedPresenters, isEntryExitAnnounced: isEntryExitAnnounced, allowAttendeeToEnableCamera: allowAttendeeToEnableCamera, allowAttendeeToEnableMic: allowAttendeeToEnableMic, allowTeamworkReactions: allowTeamworkReactions }; // Build the API path const apiPath = 'me/onlineMeetings'; console.error(`Creating online meeting: ${subject}`); // Make API call const newMeeting = await callGraphAPI(accessToken, 'POST', apiPath, meetingData); return { content: [ { type: "text", text: `✅ Online meeting created successfully! **Subject:** ${newMeeting.subject} **Meeting ID:** ${newMeeting.id} **Start:** ${new Date(newMeeting.startDateTime).toLocaleString()} **End:** ${new Date(newMeeting.endDateTime).toLocaleString()} **Allowed Presenters:** ${newMeeting.allowedPresenters} **Join Information:** • Join URL: ${newMeeting.joinWebUrl} • Conference ID: ${newMeeting.audioConferencing ? newMeeting.audioConferencing.conferenceId : 'Not available'} • Toll Number: ${newMeeting.audioConferencing ? newMeeting.audioConferencing.tollNumber : 'Not available'} **Settings:** • Entry/Exit Announced: ${newMeeting.isEntryExitAnnounced ? 'Yes' : 'No'} • Camera Allowed: ${newMeeting.allowAttendeeToEnableCamera ? 'Yes' : 'No'} • Microphone Allowed: ${newMeeting.allowAttendeeToEnableMic ? 'Yes' : 'No'} • Reactions Allowed: ${newMeeting.allowTeamworkReactions ? 'Yes' : 'No'} The meeting has been created and is ready to use.` } ] }; } catch (error) { console.error('Error in handleCreateOnlineMeeting:', error); return { error: { code: -32603, message: `Failed to create online meeting: ${error.message}` } }; } } /** * Get online meeting handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleGetOnlineMeeting(args) { try { const { meetingId } = args; if (!meetingId) { return { error: { code: -32602, message: "Meeting ID is required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Build the API path const apiPath = `me/onlineMeetings/${meetingId}`; console.error(`Getting online meeting: ${meetingId}`); // Make API call const meeting = await callGraphAPI(accessToken, 'GET', apiPath); return { content: [ { type: "text", text: `📹 Online Meeting Details **Subject:** ${meeting.subject} **Meeting ID:** ${meeting.id} **Start:** ${new Date(meeting.startDateTime).toLocaleString()} **End:** ${new Date(meeting.endDateTime).toLocaleString()} **Allowed Presenters:** ${meeting.allowedPresenters} **Join Information:** • Join URL: ${meeting.joinWebUrl} • Conference ID: ${meeting.audioConferencing ? meeting.audioConferencing.conferenceId : 'Not available'} • Toll Number: ${meeting.audioConferencing ? meeting.audioConferencing.tollNumber : 'Not available'} • Toll-Free Number: ${meeting.audioConferencing ? meeting.audioConferencing.tollFreeNumber : 'Not available'} **Settings:** • Entry/Exit Announced: ${meeting.isEntryExitAnnounced ? 'Yes' : 'No'} • Camera Allowed: ${meeting.allowAttendeeToEnableCamera ? 'Yes' : 'No'} • Microphone Allowed: ${meeting.allowAttendeeToEnableMic ? 'Yes' : 'No'} • Reactions Allowed: ${meeting.allowTeamworkReactions ? 'Yes' : 'No'} • Recording Allowed: ${meeting.allowRecording ? 'Yes' : 'No'} **Participants:** • Organizer: ${meeting.participants && meeting.participants.organizer ? meeting.participants.organizer.identity.user.displayName : 'Unknown'} • Attendees: ${meeting.participants && meeting.participants.attendees ? meeting.participants.attendees.length : 0}` } ] }; } catch (error) { console.error('Error in handleGetOnlineMeeting:', error); return { error: { code: -32603, message: `Failed to get online meeting: ${error.message}` } }; } } /** * Update online meeting handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleUpdateOnlineMeeting(args) { try { const { meetingId, subject, startDateTime, endDateTime, allowedPresenters, isEntryExitAnnounced, allowAttendeeToEnableCamera, allowAttendeeToEnableMic, allowTeamworkReactions } = args; if (!meetingId) { return { error: { code: -32602, message: "Meeting ID is required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // Build the update object (only include provided fields) const updateData = {}; if (subject !== undefined) updateData.subject = subject; if (startDateTime !== undefined) updateData.startDateTime = startDateTime; if (endDateTime !== undefined) updateData.endDateTime = endDateTime; if (allowedPresenters !== undefined) updateData.allowedPresenters = allowedPresenters; if (isEntryExitAnnounced !== undefined) updateData.isEntryExitAnnounced = isEntryExitAnnounced; if (allowAttendeeToEnableCamera !== undefined) updateData.allowAttendeeToEnableCamera = allowAttendeeToEnableCamera; if (allowAttendeeToEnableMic !== undefined) updateData.allowAttendeeToEnableMic = allowAttendeeToEnableMic; if (allowTeamworkReactions !== undefined) updateData.allowTeamworkReactions = allowTeamworkReactions; // 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/onlineMeetings/${meetingId}`; console.error(`Updating online meeting: ${meetingId}`); // Make API call const updatedMeeting = await callGraphAPI(accessToken, 'PATCH', apiPath, updateData); return { content: [ { type: "text", text: `✅ Online meeting updated successfully! **Subject:** ${updatedMeeting.subject} **Meeting ID:** ${updatedMeeting.id} **Start:** ${new Date(updatedMeeting.startDateTime).toLocaleString()} **End:** ${new Date(updatedMeeting.endDateTime).toLocaleString()} **Allowed Presenters:** ${updatedMeeting.allowedPresenters} **Join URL:** ${updatedMeeting.joinWebUrl} The meeting has been updated with the provided information.` } ] }; } catch (error) { console.error('Error in handleUpdateOnlineMeeting:', error); return { error: { code: -32603, message: `Failed to update online meeting: ${error.message}` } }; } } /** * Delete online meeting handler * @param {object} args - Tool arguments * @returns {object} - MCP response */ async function handleDeleteOnlineMeeting(args) { try { const { meetingId } = args; if (!meetingId) { return { error: { code: -32602, message: "Meeting ID is required" } }; } // Ensure user is authenticated const accessToken = await ensureAuthenticated(); // First, get the meeting details for confirmation const meetingPath = `me/onlineMeetings/${meetingId}`; let meetingSubject = 'Unknown'; try { const meeting = await callGraphAPI(accessToken, 'GET', meetingPath, null, { '$select': 'subject' }); meetingSubject = meeting.subject || 'Unknown'; } catch (error) { console.error('Could not retrieve meeting for deletion:', error); } // Build the API path const apiPath = `me/onlineMeetings/${meetingId}`; console.error(`Deleting online meeting: ${meetingId} (${meetingSubject})`); // Make API call to delete the meeting await callGraphAPI(accessToken, 'DELETE', apiPath); return { content: [ { type: "text", text: `✅ Online meeting deleted successfully! **Meeting:** ${meetingSubject} **Meeting ID:** ${meetingId} The meeting has been permanently removed and participants will no longer be able to join.` } ] }; } catch (error) { console.error('Error in handleDeleteOnlineMeeting:', error); // Handle specific error cases if (error.message.includes('404') || error.message.includes('Not Found')) { return { error: { code: -32603, message: `Meeting not found: ${meetingId}` } }; } return { error: { code: -32603, message: `Failed to delete online meeting: ${error.message}` } }; } } module.exports = { handleListOnlineMeetings, handleCreateOnlineMeeting, handleGetOnlineMeeting, handleUpdateOnlineMeeting, handleDeleteOnlineMeeting };