Skip to main content
Glama
anoopt

Outlook Meetings Scheduler MCP Server

update-event

Modify existing calendar events by updating subject, body, time, location, or attendees. Use event ID to ensure accurate changes to scheduled meetings in Outlook.

Instructions

Update an existing calendar event

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
attendeesNoList of attendees to add or update for the event
bodyNoNew content/body for the calendar event
endNoNew end time in ISO format (e.g. 2025-04-20T13:00:00)
eventIdYesID of the event to update
locationNoNew location for the event
startNoNew start time in ISO format (e.g. 2025-04-20T12:00:00)
subjectNoNew subject for the calendar event
timeZoneNoNew time zone for the event

Implementation Reference

  • The main handler function for the 'update-event' tool, which updates calendar event details including subject, body, dates, location, and attendees using the Microsoft Graph API.
        async ({ eventId, subject, body, start, end, timeZone, location, attendees }) => {
          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.` }]
            };
          }
      
          // Create the event update object with only properties that should be updated
          const eventUpdates: Partial<Event> = {};
          
          if (subject) {
            eventUpdates.subject = subject;
          }
          
          if (body) {
            eventUpdates.body = {
              contentType: "html",
              content: `${body}<br/>Updated around ${format(new Date(), 'dd-MMM-yyyy HH:mm')}`
            };
          }
          
          if (start) {
            eventUpdates.start = {
              dateTime: start,
              timeZone: timeZone || undefined
            };
          }
          
          if (end) {
            eventUpdates.end = {
              dateTime: end,
              timeZone: timeZone || undefined
            };
          }
          
          if (location) {
            eventUpdates.location = {
              displayName: location
            };
          }
          
          // Handle attendees if provided
          if (attendees && attendees.length > 0) {
            // First get the current event to merge with existing attendees
            const currentEvent = await graph.getEvent(eventId, userEmail);
            
            if (!currentEvent || !currentEvent.attendees) {
              // If no current attendees, just use the new ones
              const formattedAttendees: Attendee[] = attendees.map((attendee: any) => ({
                emailAddress: {
                  address: attendee.email,
                  name: attendee.name || attendee.email
                },
                type: attendee.type || "required"
              }));
              
              eventUpdates.attendees = formattedAttendees;
            } else {
              // Merge with existing attendees, avoiding duplicates
              const existingAttendees = currentEvent.attendees || [];
              const existingEmails = new Set(
                existingAttendees
                  .filter((a: Attendee) => a.emailAddress?.address)
                  .map((a: Attendee) => a.emailAddress!.address!.toLowerCase())
              );
              
              // Format new attendees
              const newAttendees: Attendee[] = attendees
                .filter((a: any) => !existingEmails.has(a.email.toLowerCase()))
                .map((attendee: any) => ({
                  emailAddress: {
                    address: attendee.email,
                    name: attendee.name || attendee.email
                  },
                  type: attendee.type || "required"
                }));
              
              // Combine existing and new attendees
              eventUpdates.attendees = [...existingAttendees, ...newAttendees];
            }
          }
      
          // First get the current event to show what's being updated
          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.",
                },
              ],
            };
          }
          
          // 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 calendar event. Check the logs for details.",
                },
              ],
            };
          }
      
          // Format the result for response
          const eventUrl = result.webLink || "No event URL available";
          const successMessage = `
    Calendar event updated successfully!
    
    Event ID: ${eventId}
    ${subject ? `New Subject: ${subject}\nPrevious: ${currentEvent.subject || "No subject"}` : ''}
    ${start ? `New Start: ${start}\nPrevious: ${currentEvent.start?.dateTime || "No start time"}` : ''}
    ${end ? `New End: ${end}\nPrevious: ${currentEvent.end?.dateTime || "No end time"}` : ''}
    ${location ? `New Location: ${location}\nPrevious: ${currentEvent.location?.displayName || "No location"}` : ''}
    Event URL: ${eventUrl}
                        `;
      
          return {
            content: [
              {
                type: "text",
                text: successMessage,
              },
            ],
          };
        }
  • Zod schema defining the input parameters for the 'update-event' tool.
    {
      eventId: z.string().describe("ID of the event to update"),
      subject: z.string().optional().describe("New subject for the calendar event"),
      body: z.string().optional().describe("New content/body for the calendar event"),
      start: z.string().optional().describe("New start time in ISO format (e.g. 2025-04-20T12:00:00)"),
      end: z.string().optional().describe("New end time in ISO format (e.g. 2025-04-20T13:00:00)"),
      timeZone: z.string().optional().describe("New time zone for the event"),
      location: z.string().optional().describe("New location for the event"),
      attendees: z.array(
        z.object({
          email: z.string().describe("Email address of the attendee"),
          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 or update for the event"),
    },
  • The registerTool call that registers the 'update-event' tool with the MCP server, including name, description, schema, and handler.
      registerTool(
        server,
        "update-event",
        "Update an existing calendar event",
        {
          eventId: z.string().describe("ID of the event to update"),
          subject: z.string().optional().describe("New subject for the calendar event"),
          body: z.string().optional().describe("New content/body for the calendar event"),
          start: z.string().optional().describe("New start time in ISO format (e.g. 2025-04-20T12:00:00)"),
          end: z.string().optional().describe("New end time in ISO format (e.g. 2025-04-20T13:00:00)"),
          timeZone: z.string().optional().describe("New time zone for the event"),
          location: z.string().optional().describe("New location for the event"),
          attendees: z.array(
            z.object({
              email: z.string().describe("Email address of the attendee"),
              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 or update for the event"),
        },
        async ({ eventId, subject, body, start, end, timeZone, location, attendees }) => {
          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.` }]
            };
          }
      
          // Create the event update object with only properties that should be updated
          const eventUpdates: Partial<Event> = {};
          
          if (subject) {
            eventUpdates.subject = subject;
          }
          
          if (body) {
            eventUpdates.body = {
              contentType: "html",
              content: `${body}<br/>Updated around ${format(new Date(), 'dd-MMM-yyyy HH:mm')}`
            };
          }
          
          if (start) {
            eventUpdates.start = {
              dateTime: start,
              timeZone: timeZone || undefined
            };
          }
          
          if (end) {
            eventUpdates.end = {
              dateTime: end,
              timeZone: timeZone || undefined
            };
          }
          
          if (location) {
            eventUpdates.location = {
              displayName: location
            };
          }
          
          // Handle attendees if provided
          if (attendees && attendees.length > 0) {
            // First get the current event to merge with existing attendees
            const currentEvent = await graph.getEvent(eventId, userEmail);
            
            if (!currentEvent || !currentEvent.attendees) {
              // If no current attendees, just use the new ones
              const formattedAttendees: Attendee[] = attendees.map((attendee: any) => ({
                emailAddress: {
                  address: attendee.email,
                  name: attendee.name || attendee.email
                },
                type: attendee.type || "required"
              }));
              
              eventUpdates.attendees = formattedAttendees;
            } else {
              // Merge with existing attendees, avoiding duplicates
              const existingAttendees = currentEvent.attendees || [];
              const existingEmails = new Set(
                existingAttendees
                  .filter((a: Attendee) => a.emailAddress?.address)
                  .map((a: Attendee) => a.emailAddress!.address!.toLowerCase())
              );
              
              // Format new attendees
              const newAttendees: Attendee[] = attendees
                .filter((a: any) => !existingEmails.has(a.email.toLowerCase()))
                .map((attendee: any) => ({
                  emailAddress: {
                    address: attendee.email,
                    name: attendee.name || attendee.email
                  },
                  type: attendee.type || "required"
                }));
              
              // Combine existing and new attendees
              eventUpdates.attendees = [...existingAttendees, ...newAttendees];
            }
          }
      
          // First get the current event to show what's being updated
          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.",
                },
              ],
            };
          }
          
          // 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 calendar event. Check the logs for details.",
                },
              ],
            };
          }
      
          // Format the result for response
          const eventUrl = result.webLink || "No event URL available";
          const successMessage = `
    Calendar event updated successfully!
    
    Event ID: ${eventId}
    ${subject ? `New Subject: ${subject}\nPrevious: ${currentEvent.subject || "No subject"}` : ''}
    ${start ? `New Start: ${start}\nPrevious: ${currentEvent.start?.dateTime || "No start time"}` : ''}
    ${end ? `New End: ${end}\nPrevious: ${currentEvent.end?.dateTime || "No end time"}` : ''}
    ${location ? `New Location: ${location}\nPrevious: ${currentEvent.location?.displayName || "No location"}` : ''}
    Event URL: ${eventUrl}
                        `;
      
          return {
            content: [
              {
                type: "text",
                text: successMessage,
              },
            ],
          };
        }
      );
  • src/index.ts:35-35 (registration)
    Top-level registration call in the main server file that invokes registerEventUpdateTools to add the 'update-event' tool to the MCP server.
    registerEventUpdateTools(server);
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 'update' implies mutation, it doesn't specify whether this requires specific permissions, whether changes are reversible, what happens to fields not mentioned in the update (partial vs. full replacement), or what the response looks like. For a mutation tool with zero annotation coverage, this is a significant gap.

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 states the core purpose without any wasted words. It's appropriately sized and front-loaded with the essential information.

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 8 parameters, no annotations, and no output schema, the description is insufficient. It doesn't address behavioral aspects like permissions, side effects, or response format. While the schema covers parameters well, the description fails to provide the contextual information needed for safe and effective use of this update operation.

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?

The schema description coverage is 100%, so the schema already documents all 8 parameters thoroughly. The description adds no additional parameter information beyond what's in the schema. According to guidelines, when schema coverage is high (>80%), the baseline score is 3 even with no parameter info in the description.

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 verb ('update') and resource ('existing calendar event'), making the purpose immediately understandable. However, it doesn't differentiate this tool from its sibling 'update-event-attendees' which also updates events but focuses specifically on attendees.

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 about when to use this tool versus alternatives like 'update-event-attendees' (for attendee-only updates) or 'create-event' (for new events). There's no mention of prerequisites, such as needing an existing event ID, or when partial versus full updates are appropriate.

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