Skip to main content
Glama
NOVA-3951

Fastmail Calendar MCP Server

update_event

Idempotent

Modify existing calendar events by updating details like title, time, description, or location using the event URL from Fastmail Calendar MCP Server.

Instructions

Modify an existing event. PREREQUISITE: You must first call list_calendars, then list_events to get the eventUrl. Only include fields you want to change; omitted fields stay the same.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
eventUrlYesREQUIRED. The event URL from list_events output. Example: 'https://caldav.fastmail.com/dav/calendars/user/.../event.ics'
summaryNoOptional. New title for the event.
descriptionNoOptional. New description/notes for the event.
startDateNoOptional. New start time in ISO format.
endDateNoOptional. New end time in ISO format.
locationNoOptional. New location for the event.

Implementation Reference

  • Executes the update_event tool: handles test mode simulation, fetches the target event by searching all calendars, modifies the iCal content using regex replacements for updated fields (summary, description, location, startDate, endDate), and updates the calendar object using davClient.
    case "update_event": {
      const {
        eventUrl,
        summary,
        description,
        startDate,
        endDate,
        location,
      } = args as {
        eventUrl: string;
        summary?: string;
        description?: string;
        startDate?: string;
        endDate?: string;
        location?: string;
      };
    
      if (isTestMode) {
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify({
                _testMode: true,
                _message: "Demo mode - event update simulated. No actual changes were made. Use real Fastmail credentials to update events.",
                simulatedUpdate: {
                  eventUrl,
                  updatedFields: { summary, description, startDate, endDate, location },
                },
              }, null, 2),
            },
          ],
        };
      }
    
      let existingEvent: DAVCalendarObject | undefined;
    
      for (const calendar of calendars) {
        const events = await davClient.fetchCalendarObjects({
          calendar,
        });
    
        existingEvent = events.find(
          (e: DAVCalendarObject) => e.url === eventUrl
        );
    
        if (existingEvent) {
          break;
        }
      }
    
      if (!existingEvent) {
        throw new Error(`Event not found: ${eventUrl}`);
      }
    
      let updatedIcal = existingEvent.data;
    
      if (summary) {
        updatedIcal = updatedIcal.replace(
          /SUMMARY:.*\r?\n/,
          `SUMMARY:${summary}\r\n`
        );
      }
    
      if (description !== undefined) {
        if (updatedIcal.includes("DESCRIPTION:")) {
          updatedIcal = updatedIcal.replace(
            /DESCRIPTION:.*\r?\n/,
            `DESCRIPTION:${description}\r\n`
          );
        } else {
          updatedIcal = updatedIcal.replace(
            /SUMMARY:.*\r?\n/,
            `$&DESCRIPTION:${description}\r\n`
          );
        }
      }
    
      if (location !== undefined) {
        if (updatedIcal.includes("LOCATION:")) {
          updatedIcal = updatedIcal.replace(
            /LOCATION:.*\r?\n/,
            `LOCATION:${location}\r\n`
          );
        } else {
          updatedIcal = updatedIcal.replace(
            /SUMMARY:.*\r?\n/,
            `$&LOCATION:${location}\r\n`
          );
        }
      }
    
      if (startDate) {
        const start = new Date(startDate);
        if (isNaN(start.getTime())) {
          throw new Error(`Invalid start date: ${startDate}`);
        }
        updatedIcal = updatedIcal.replace(
          /DTSTART:.*\r?\n/,
          `DTSTART:${formatICalDate(start)}\r\n`
        );
      }
    
      if (endDate) {
        const end = new Date(endDate);
        if (isNaN(end.getTime())) {
          throw new Error(`Invalid end date: ${endDate}`);
        }
        updatedIcal = updatedIcal.replace(
          /DTEND:.*\r?\n/,
          `DTEND:${formatICalDate(end)}\r\n`
        );
      }
    
      await davClient.updateCalendarObject({
        calendarObject: {
          url: eventUrl,
          data: updatedIcal,
          etag: existingEvent.etag,
        },
      });
    
      return {
        content: [
          {
            type: "text",
            text: `Event updated successfully: ${eventUrl}`,
          },
        ],
      };
    }
  • src/index.ts:352-391 (registration)
    Registers the update_event tool in the ListToolsRequestSchema response, defining its name, description, input schema (eventUrl required, other fields optional), and annotations indicating it's not read-only or destructive.
    {
      name: "update_event",
      description: `Modify an existing event. PREREQUISITE: You must first call list_calendars, then list_events to get the eventUrl. Only include fields you want to change; omitted fields stay the same.`,
      inputSchema: {
        type: "object",
        properties: {
          eventUrl: {
            type: "string",
            description: "REQUIRED. The event URL from list_events output. Example: 'https://caldav.fastmail.com/dav/calendars/user/.../event.ics'",
          },
          summary: {
            type: "string",
            description: "Optional. New title for the event.",
          },
          description: {
            type: "string",
            description: "Optional. New description/notes for the event.",
          },
          startDate: {
            type: "string",
            description: "Optional. New start time in ISO format.",
          },
          endDate: {
            type: "string",
            description: "Optional. New end time in ISO format.",
          },
          location: {
            type: "string",
            description: "Optional. New location for the event.",
          },
        },
        required: ["eventUrl"],
      },
      annotations: {
        title: "Update Event",
        readOnlyHint: false,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false,
      },
  • Helper function used in update_event to format new startDate and endDate into iCal-compatible string format (YYYYMMDDTHHMMSSZ).
    function formatICalDate(date: Date): string {
      return date
        .toISOString()
        .replace(/[-:]/g, "")
        .replace(/\.\d{3}/, "");
  • Input schema definition for the update_event tool, specifying parameters and requirements.
    inputSchema: {
      type: "object",
      properties: {
        eventUrl: {
          type: "string",
          description: "REQUIRED. The event URL from list_events output. Example: 'https://caldav.fastmail.com/dav/calendars/user/.../event.ics'",
        },
        summary: {
          type: "string",
          description: "Optional. New title for the event.",
        },
        description: {
          type: "string",
          description: "Optional. New description/notes for the event.",
        },
        startDate: {
          type: "string",
          description: "Optional. New start time in ISO format.",
        },
        endDate: {
          type: "string",
          description: "Optional. New end time in ISO format.",
        },
        location: {
          type: "string",
          description: "Optional. New location for the event.",
        },
      },
      required: ["eventUrl"],
Behavior4/5

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

The description adds valuable behavioral context beyond annotations: it specifies the prerequisite workflow (calling list_calendars and list_events first) and the partial-update behavior ('Only include fields you want to change; omitted fields stay the same'). Annotations cover read/write status (readOnlyHint=false), idempotency (idempotentHint=true), and non-destructiveness (destructiveHint=false), but the description complements this with practical usage details. No contradiction with annotations.

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 extremely concise and front-loaded: the first sentence states the core purpose, followed by prerequisite and behavioral details in two additional sentences. Every sentence earns its place by providing critical guidance without redundancy or fluff.

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

Completeness4/5

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

Given the tool's complexity (mutation with prerequisites), rich annotations (covering safety and idempotency), and full schema coverage, the description is nearly complete. It adds key contextual details like prerequisites and partial-update behavior. The main gap is lack of output schema or description of return values, but annotations help mitigate this. It's well-suited for the agent's needs.

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%, with all parameters well-documented in the input schema (e.g., eventUrl is REQUIRED with an example, others are optional with clear purposes). The description adds minimal parameter semantics beyond the schema, only reinforcing the eventUrl prerequisite and partial-update approach. This meets the baseline of 3 when schema coverage is high.

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

Purpose5/5

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

The description clearly states the specific action ('Modify an existing event') and resource ('event'), distinguishing it from siblings like create_event (creates new), delete_event (removes), get_event_details (reads), and list_events (lists). It goes beyond just restating the name/title by specifying it's for modifying existing events.

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

Usage Guidelines5/5

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

The description provides explicit prerequisites ('You must first call list_calendars, then list_events to get the eventUrl'), which tells the agent exactly when and how to prepare for using this tool. It also implicitly distinguishes from alternatives by focusing on modification rather than creation, deletion, or listing.

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

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/NOVA-3951/Fastmail-Calendar-MCP'

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