update-event-attendees
Modify event attendee lists in Microsoft Outlook by adding or removing participants using their email addresses and names.
Instructions
Add or remove attendees from a calendar event
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| addAttendees | No | List of attendees to add to the event | |
| eventId | Yes | ID of the event to update | |
| removeAttendees | No | List of email addresses to remove from the event |
Implementation Reference
- src/tools/event-update.ts:187-316 (handler)The handler function for the 'update-event-attendees' tool. It authenticates, fetches the current event attendees, processes additions (avoiding duplicates) and removals (case-insensitive), updates the event attendees via Microsoft Graph API, and returns a formatted text response detailing changes.async ({ eventId, addAttendees, removeAttendees }) => { const { graph, userEmail, authError } = await getGraphConfig(); if (authError) { return { content: [{ type: "text", text: `🔐 Authentication Required\n\n${authError}\n\nPlease complete the authentication and try again.` }] }; } // First get the current event to get existing attendees const currentEvent = await graph.getEvent(eventId, userEmail); if (!currentEvent) { return { content: [ { type: "text", text: "Could not find the event to update. Please check the event ID.", }, ], }; } // Get current attendees or initialize empty array const currentAttendees = currentEvent.attendees || []; // Process attendee removals if any let updatedAttendees = [...currentAttendees]; let removedAttendeesList: string[] = []; if (removeAttendees && removeAttendees.length > 0) { // Create a set of lowercase email addresses to remove for case-insensitive matching const emailsToRemove = new Set(removeAttendees.map((email: string) => email.toLowerCase())); // Filter out attendees that should be removed updatedAttendees = currentAttendees.filter((attendee: Attendee) => { const email = attendee.emailAddress?.address?.toLowerCase() || ''; const shouldRemove = emailsToRemove.has(email); if (shouldRemove) { removedAttendeesList.push(attendee.emailAddress?.address || email); } return !shouldRemove; }); } // Process attendee additions if any let addedAttendeesList: string[] = []; if (addAttendees && addAttendees.length > 0) { // Get existing email addresses to avoid duplicates const existingEmails = new Set( updatedAttendees .filter((a: Attendee) => a.emailAddress?.address) .map((a: Attendee) => a.emailAddress!.address!.toLowerCase()) ); // Create new attendee objects for those that don't already exist const newAttendees: Attendee[] = addAttendees .filter((a: any) => !existingEmails.has(a.email.toLowerCase())) .map((attendee: any) => { addedAttendeesList.push(attendee.email); return { emailAddress: { address: attendee.email, name: attendee.name || attendee.email }, type: attendee.type || "required" }; }); // Add new attendees to the list updatedAttendees = [...updatedAttendees, ...newAttendees]; } // Only update if there are changes if ((addAttendees && addAttendees.length > 0) || (removeAttendees && removeAttendees.length > 0)) { // Update the event with only the attendees property const eventUpdates: Partial<Event> = { attendees: updatedAttendees }; // Call the Graph API to update the event const result = await graph.updateEvent(eventId, eventUpdates, userEmail); if (!result) { return { content: [ { type: "text", text: "Failed to update event attendees. Check the logs for details.", }, ], }; } // Format the result for response const eventUrl = result.webLink || "No event URL available"; let successMessage = `Calendar event attendees updated successfully!\n\n`; successMessage += `Event: ${currentEvent.subject || "No subject"}\n`; if (addedAttendeesList.length > 0) { successMessage += `\nAdded attendees:\n${addedAttendeesList.join('\n')}\n`; } if (removedAttendeesList.length > 0) { successMessage += `\nRemoved attendees:\n${removedAttendeesList.join('\n')}\n`; } successMessage += `\nEvent URL: ${eventUrl}`; return { content: [ { type: "text", text: successMessage, }, ], }; } else { return { content: [ { type: "text", text: "No changes to attendees were specified.", }, ], }; } } );
- src/tools/event-update.ts:174-186 (schema)Zod schema defining the input parameters for the 'update-event-attendees' tool: eventId (required), addAttendees (optional array of attendee objects), removeAttendees (optional array of emails).{ eventId: z.string().describe("ID of the event to update"), addAttendees: z.array( z.object({ email: z.string().describe("Email address of the attendee to add"), name: z.string().optional().describe("Name of the attendee"), type: z.enum(["required", "optional"]).optional().describe("Type of attendee: required or optional") }) ).optional().describe("List of attendees to add to the event"), removeAttendees: z.array( z.string().describe("Email addresses of attendees to remove from the event") ).optional().describe("List of email addresses to remove from the event"), },
- src/tools/event-update.ts:170-316 (registration)The registerTool call that registers the 'update-event-attendees' tool on the MCP server, providing name, description, input schema, and handler implementation.registerTool( server, "update-event-attendees", "Add or remove attendees from a calendar event", { eventId: z.string().describe("ID of the event to update"), addAttendees: z.array( z.object({ email: z.string().describe("Email address of the attendee to add"), name: z.string().optional().describe("Name of the attendee"), type: z.enum(["required", "optional"]).optional().describe("Type of attendee: required or optional") }) ).optional().describe("List of attendees to add to the event"), removeAttendees: z.array( z.string().describe("Email addresses of attendees to remove from the event") ).optional().describe("List of email addresses to remove from the event"), }, async ({ eventId, addAttendees, removeAttendees }) => { const { graph, userEmail, authError } = await getGraphConfig(); if (authError) { return { content: [{ type: "text", text: `🔐 Authentication Required\n\n${authError}\n\nPlease complete the authentication and try again.` }] }; } // First get the current event to get existing attendees const currentEvent = await graph.getEvent(eventId, userEmail); if (!currentEvent) { return { content: [ { type: "text", text: "Could not find the event to update. Please check the event ID.", }, ], }; } // Get current attendees or initialize empty array const currentAttendees = currentEvent.attendees || []; // Process attendee removals if any let updatedAttendees = [...currentAttendees]; let removedAttendeesList: string[] = []; if (removeAttendees && removeAttendees.length > 0) { // Create a set of lowercase email addresses to remove for case-insensitive matching const emailsToRemove = new Set(removeAttendees.map((email: string) => email.toLowerCase())); // Filter out attendees that should be removed updatedAttendees = currentAttendees.filter((attendee: Attendee) => { const email = attendee.emailAddress?.address?.toLowerCase() || ''; const shouldRemove = emailsToRemove.has(email); if (shouldRemove) { removedAttendeesList.push(attendee.emailAddress?.address || email); } return !shouldRemove; }); } // Process attendee additions if any let addedAttendeesList: string[] = []; if (addAttendees && addAttendees.length > 0) { // Get existing email addresses to avoid duplicates const existingEmails = new Set( updatedAttendees .filter((a: Attendee) => a.emailAddress?.address) .map((a: Attendee) => a.emailAddress!.address!.toLowerCase()) ); // Create new attendee objects for those that don't already exist const newAttendees: Attendee[] = addAttendees .filter((a: any) => !existingEmails.has(a.email.toLowerCase())) .map((attendee: any) => { addedAttendeesList.push(attendee.email); return { emailAddress: { address: attendee.email, name: attendee.name || attendee.email }, type: attendee.type || "required" }; }); // Add new attendees to the list updatedAttendees = [...updatedAttendees, ...newAttendees]; } // Only update if there are changes if ((addAttendees && addAttendees.length > 0) || (removeAttendees && removeAttendees.length > 0)) { // Update the event with only the attendees property const eventUpdates: Partial<Event> = { attendees: updatedAttendees }; // Call the Graph API to update the event const result = await graph.updateEvent(eventId, eventUpdates, userEmail); if (!result) { return { content: [ { type: "text", text: "Failed to update event attendees. Check the logs for details.", }, ], }; } // Format the result for response const eventUrl = result.webLink || "No event URL available"; let successMessage = `Calendar event attendees updated successfully!\n\n`; successMessage += `Event: ${currentEvent.subject || "No subject"}\n`; if (addedAttendeesList.length > 0) { successMessage += `\nAdded attendees:\n${addedAttendeesList.join('\n')}\n`; } if (removedAttendeesList.length > 0) { successMessage += `\nRemoved attendees:\n${removedAttendeesList.join('\n')}\n`; } successMessage += `\nEvent URL: ${eventUrl}`; return { content: [ { type: "text", text: successMessage, }, ], }; } else { return { content: [ { type: "text", text: "No changes to attendees were specified.", }, ], }; } } );
- src/index.ts:35-35 (registration)Top-level registration invocation in the main server file, calling registerEventUpdateTools(server) which includes registration of 'update-event-attendees'.registerEventUpdateTools(server);
- src/tools/event-update.ts:11-11 (registration)Exported function that registers multiple event update tools, including 'update-event-attendees', called from main index.ts.export function registerEventUpdateTools(server: McpServer): void {