Skip to main content
Glama
anoopt

Outlook Meetings Scheduler MCP Server

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
NameRequiredDescriptionDefault
addAttendeesNoList of attendees to add to the event
eventIdYesID of the event to update
removeAttendeesNoList of email addresses to remove from the event

Implementation Reference

  • 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.",
              },
            ],
          };
        }
      }
    );
  • 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"),
    },
  • 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);
  • Exported function that registers multiple event update tools, including 'update-event-attendees', called from main index.ts.
    export function registerEventUpdateTools(server: McpServer): void {
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. While 'Add or remove attendees' implies mutation, it doesn't specify permission requirements, whether changes are reversible, how conflicts are handled, or what happens to event notifications. For a mutation tool with zero annotation coverage, this leaves significant behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that communicates the core functionality without any wasted words. It's appropriately sized for the tool's scope and gets straight to the point with clear subject-verb-object structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description is insufficient. It doesn't address permission requirements, error conditions, response format, or how this tool differs from sibling update tools. Given the complexity of modifying calendar events and the lack of structured safety information, more contextual guidance is needed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents all three parameters. The description mentions 'attendees' which aligns with the parameters but adds no additional semantic context beyond what's in the schema. This meets the baseline expectation when schema coverage is complete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Add or remove attendees') and resource ('from a calendar event'), making the purpose immediately understandable. However, it doesn't distinguish this tool from sibling tools like 'update-event' which might also handle attendee updates, leaving some ambiguity about specialization.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'update-event' or 'create-event-with-attendees'. There's no mention of prerequisites, permissions needed, or scenarios where this specific attendee-focused update is preferred over broader event updates.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anoopt/outlook-meetings-scheduler-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server