Skip to main content
Glama
taylorwilsdon

Google Workspace MCP Server - Control Gmail, Calendar, Docs, Sheets, Slides, Chat, Forms & Drive

modify_event

Edit existing calendar events by updating details such as title, time, location, attendees, and description. Designed for Google Workspace users to manage events efficiently.

Instructions

Modifies an existing event.

Args:
    user_google_email (str): The user's Google email address. Required.
    event_id (str): The ID of the event to modify.
    calendar_id (str): Calendar ID (default: 'primary').
    summary (Optional[str]): New event title.
    start_time (Optional[str]): New start time (RFC3339, e.g., "2023-10-27T10:00:00-07:00" or "2023-10-27" for all-day).
    end_time (Optional[str]): New end time (RFC3339, e.g., "2023-10-27T11:00:00-07:00" or "2023-10-28" for all-day).
    description (Optional[str]): New event description.
    location (Optional[str]): New event location.
    attendees (Optional[List[str]]): New attendee email addresses.
    timezone (Optional[str]): New timezone (e.g., "America/New_York").

Returns:
    str: Confirmation message of the successful event modification with event link.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
attendeesNo
calendar_idNoprimary
descriptionNo
end_timeNo
event_idYes
locationNo
serviceYes
start_timeNo
summaryNo
timezoneNo
user_google_emailYes

Implementation Reference

  • Registers the 'modify_event' function as a server tool using the @server.tool() decorator from core.server.
    @server.tool()
  • The function signature and comprehensive docstring define the schema for the 'modify_event' tool inputs, including optional parameters for event fields, reminders, transparency, and Google Meet.
    async def modify_event(
        service,
        user_google_email: str,
        event_id: str,
        calendar_id: str = "primary",
        summary: Optional[str] = None,
        start_time: Optional[str] = None,
        end_time: Optional[str] = None,
        description: Optional[str] = None,
        location: Optional[str] = None,
        attendees: Optional[List[str]] = None,
        timezone: Optional[str] = None,
        add_google_meet: Optional[bool] = None,
        reminders: Optional[Union[str, List[Dict[str, Any]]]] = None,
        use_default_reminders: Optional[bool] = None,
        transparency: Optional[str] = None,
    ) -> str:
        """
        Modifies an existing event.
    
        Args:
            user_google_email (str): The user's Google email address. Required.
            event_id (str): The ID of the event to modify.
            calendar_id (str): Calendar ID (default: 'primary').
            summary (Optional[str]): New event title.
            start_time (Optional[str]): New start time (RFC3339, e.g., "2023-10-27T10:00:00-07:00" or "2023-10-27" for all-day).
            end_time (Optional[str]): New end time (RFC3339, e.g., "2023-10-27T11:00:00-07:00" or "2023-10-28" for all-day).
            description (Optional[str]): New event description.
            location (Optional[str]): New event location.
            attendees (Optional[List[str]]): New attendee email addresses.
            timezone (Optional[str]): New timezone (e.g., "America/New_York").
            add_google_meet (Optional[bool]): Whether to add or remove Google Meet video conference. If True, adds Google Meet; if False, removes it; if None, leaves unchanged.
            reminders (Optional[Union[str, List[Dict[str, Any]]]]): JSON string or list of reminder objects to replace existing reminders. Each should have 'method' ("popup" or "email") and 'minutes' (0-40320). Max 5 reminders. Example: '[{"method": "popup", "minutes": 15}]' or [{"method": "popup", "minutes": 15}]
            use_default_reminders (Optional[bool]): Whether to use calendar's default reminders. If specified, overrides current reminder settings.
            transparency (Optional[str]): Event transparency for busy/free status. "opaque" shows as Busy, "transparent" shows as Available/Free. If None, preserves existing transparency setting.
    
        Returns:
            str: Confirmation message of the successful event modification with event link.
        """
  • Core handler logic for modifying Google Calendar events. Constructs update payload from optional parameters, preserves existing data, handles special features like reminders, transparency, and Google Meet, then executes the Google Calendar API update.
    async def modify_event(
        service,
        user_google_email: str,
        event_id: str,
        calendar_id: str = "primary",
        summary: Optional[str] = None,
        start_time: Optional[str] = None,
        end_time: Optional[str] = None,
        description: Optional[str] = None,
        location: Optional[str] = None,
        attendees: Optional[List[str]] = None,
        timezone: Optional[str] = None,
        add_google_meet: Optional[bool] = None,
        reminders: Optional[Union[str, List[Dict[str, Any]]]] = None,
        use_default_reminders: Optional[bool] = None,
        transparency: Optional[str] = None,
    ) -> str:
        """
        Modifies an existing event.
    
        Args:
            user_google_email (str): The user's Google email address. Required.
            event_id (str): The ID of the event to modify.
            calendar_id (str): Calendar ID (default: 'primary').
            summary (Optional[str]): New event title.
            start_time (Optional[str]): New start time (RFC3339, e.g., "2023-10-27T10:00:00-07:00" or "2023-10-27" for all-day).
            end_time (Optional[str]): New end time (RFC3339, e.g., "2023-10-27T11:00:00-07:00" or "2023-10-28" for all-day).
            description (Optional[str]): New event description.
            location (Optional[str]): New event location.
            attendees (Optional[List[str]]): New attendee email addresses.
            timezone (Optional[str]): New timezone (e.g., "America/New_York").
            add_google_meet (Optional[bool]): Whether to add or remove Google Meet video conference. If True, adds Google Meet; if False, removes it; if None, leaves unchanged.
            reminders (Optional[Union[str, List[Dict[str, Any]]]]): JSON string or list of reminder objects to replace existing reminders. Each should have 'method' ("popup" or "email") and 'minutes' (0-40320). Max 5 reminders. Example: '[{"method": "popup", "minutes": 15}]' or [{"method": "popup", "minutes": 15}]
            use_default_reminders (Optional[bool]): Whether to use calendar's default reminders. If specified, overrides current reminder settings.
            transparency (Optional[str]): Event transparency for busy/free status. "opaque" shows as Busy, "transparent" shows as Available/Free. If None, preserves existing transparency setting.
    
        Returns:
            str: Confirmation message of the successful event modification with event link.
        """
        logger.info(
            f"[modify_event] Invoked. Email: '{user_google_email}', Event ID: {event_id}"
        )
    
        # Build the event body with only the fields that are provided
        event_body: Dict[str, Any] = {}
        if summary is not None:
            event_body["summary"] = summary
        if start_time is not None:
            event_body["start"] = (
                {"date": start_time}
                if "T" not in start_time
                else {"dateTime": start_time}
            )
            if timezone is not None and "dateTime" in event_body["start"]:
                event_body["start"]["timeZone"] = timezone
        if end_time is not None:
            event_body["end"] = (
                {"date": end_time} if "T" not in end_time else {"dateTime": end_time}
            )
            if timezone is not None and "dateTime" in event_body["end"]:
                event_body["end"]["timeZone"] = timezone
        if description is not None:
            event_body["description"] = description
        if location is not None:
            event_body["location"] = location
        if attendees is not None:
            event_body["attendees"] = [{"email": email} for email in attendees]
        
        # Handle reminders
        if reminders is not None or use_default_reminders is not None:
            reminder_data = {}
            if use_default_reminders is not None:
                reminder_data["useDefault"] = use_default_reminders
            else:
                # Preserve existing event's useDefault value if not explicitly specified
                try:
                    existing_event = service.events().get(calendarId=calendar_id, eventId=event_id).execute()
                    reminder_data["useDefault"] = existing_event.get("reminders", {}).get("useDefault", True)
                except Exception as e:
                    logger.warning(f"[modify_event] Could not fetch existing event for reminders: {e}")
                    reminder_data["useDefault"] = True  # Fallback to True if unable to fetch
            
            # If custom reminders are provided, automatically disable default reminders
            if reminders is not None:
                if reminder_data.get("useDefault", False):
                    reminder_data["useDefault"] = False
                    logger.info("[modify_event] Custom reminders provided - disabling default reminders")
                
                validated_reminders = _parse_reminders_json(reminders, "modify_event")
                if reminders and not validated_reminders:
                    logger.warning("[modify_event] Reminders provided but failed validation. No custom reminders will be set.")
                elif validated_reminders:
                    reminder_data["overrides"] = validated_reminders
                    logger.info(f"[modify_event] Updated reminders with {len(validated_reminders)} custom reminders")
            
            event_body["reminders"] = reminder_data
    
        # Handle transparency validation
        _apply_transparency_if_valid(event_body, transparency, "modify_event")
    
        if (
            timezone is not None
            and "start" not in event_body
            and "end" not in event_body
        ):
            # If timezone is provided but start/end times are not, we need to fetch the existing event
            # to apply the timezone correctly. This is a simplification; a full implementation
            # might handle this more robustly or require start/end with timezone.
            # For now, we'll log a warning and skip applying timezone if start/end are missing.
            logger.warning(
                "[modify_event] Timezone provided but start_time and end_time are missing. Timezone will not be applied unless start/end times are also provided."
            )
    
        if not event_body:
            message = "No fields provided to modify the event."
            logger.warning(f"[modify_event] {message}")
            raise Exception(message)
    
        # Log the event ID for debugging
        logger.info(
            f"[modify_event] Attempting to update event with ID: '{event_id}' in calendar '{calendar_id}'"
        )
    
        # Get the existing event to preserve fields that aren't being updated
        try:
            existing_event = await asyncio.to_thread(
                lambda: service.events().get(calendarId=calendar_id, eventId=event_id).execute()
            )
            logger.info(
                "[modify_event] Successfully retrieved existing event before update"
            )
    
            # Preserve existing fields if not provided in the update
            _preserve_existing_fields(event_body, existing_event, {
                "summary": summary,
                "description": description,
                "location": location,
                "attendees": attendees
            })
    
            # Handle Google Meet conference data
            if add_google_meet is not None:
                if add_google_meet:
                    # Add Google Meet
                    request_id = str(uuid.uuid4())
                    event_body["conferenceData"] = {
                        "createRequest": {
                            "requestId": request_id,
                            "conferenceSolutionKey": {
                                "type": "hangoutsMeet"
                            }
                        }
                    }
                    logger.info(f"[modify_event] Adding Google Meet conference with request ID: {request_id}")
                else:
                    # Remove Google Meet by setting conferenceData to empty
                    event_body["conferenceData"] = {}
                    logger.info("[modify_event] Removing Google Meet conference")
            elif 'conferenceData' in existing_event:
                # Preserve existing conference data if not specified
                event_body["conferenceData"] = existing_event["conferenceData"]
                logger.info("[modify_event] Preserving existing conference data")
    
        except HttpError as get_error:
            if get_error.resp.status == 404:
                logger.error(
                    f"[modify_event] Event not found during pre-update verification: {get_error}"
                )
                message = f"Event not found during verification. The event with ID '{event_id}' could not be found in calendar '{calendar_id}'. This may be due to incorrect ID format or the event no longer exists."
                raise Exception(message)
            else:
                logger.warning(
                    f"[modify_event] Error during pre-update verification, but proceeding with update: {get_error}"
                )
    
        # Proceed with the update
        updated_event = await asyncio.to_thread(
            lambda: service.events()
            .update(calendarId=calendar_id, eventId=event_id, body=event_body, conferenceDataVersion=1)
            .execute()
        )
    
        link = updated_event.get("htmlLink", "No link available")
        confirmation_message = f"Successfully modified event '{updated_event.get('summary', summary)}' (ID: {event_id}) for {user_google_email}. Link: {link}"
    
        # Add Google Meet information if conference was added
        if add_google_meet is True and "conferenceData" in updated_event:
            conference_data = updated_event["conferenceData"]
            if "entryPoints" in conference_data:
                for entry_point in conference_data["entryPoints"]:
                    if entry_point.get("entryPointType") == "video":
                        meet_link = entry_point.get("uri", "")
                        if meet_link:
                            confirmation_message += f" Google Meet: {meet_link}"
                            break
        elif add_google_meet is False:
            confirmation_message += " (Google Meet removed)"
    
        logger.info(
            f"Event modified successfully for {user_google_email}. ID: {updated_event.get('id')}, Link: {link}"
        )
        return confirmation_message
  • Helper to parse and validate custom reminders JSON/list input. Used in modify_event for the 'reminders' parameter.
    def _parse_reminders_json(reminders_input: Optional[Union[str, List[Dict[str, Any]]]], function_name: str) -> List[Dict[str, Any]]:
        """
        Parse reminders from JSON string or list object and validate them.
        
        Args:
            reminders_input: JSON string containing reminder objects or list of reminder objects
            function_name: Name of calling function for logging
            
        Returns:
            List of validated reminder objects
        """
        if not reminders_input:
            return []
        
        # Handle both string (JSON) and list inputs
        if isinstance(reminders_input, str):
            try:
                reminders = json.loads(reminders_input)
                if not isinstance(reminders, list):
                    logger.warning(f"[{function_name}] Reminders must be a JSON array, got {type(reminders).__name__}")
                    return []
            except json.JSONDecodeError as e:
                logger.warning(f"[{function_name}] Invalid JSON for reminders: {e}")
                return []
        elif isinstance(reminders_input, list):
            reminders = reminders_input
        else:
            logger.warning(f"[{function_name}] Reminders must be a JSON string or list, got {type(reminders_input).__name__}")
            return []
        
        # Validate reminders
        if len(reminders) > 5:
            logger.warning(f"[{function_name}] More than 5 reminders provided, truncating to first 5")
            reminders = reminders[:5]
        
        validated_reminders = []
        for reminder in reminders:
            if not isinstance(reminder, dict) or "method" not in reminder or "minutes" not in reminder:
                logger.warning(f"[{function_name}] Invalid reminder format: {reminder}, skipping")
                continue
            
            method = reminder["method"].lower()
            if method not in ["popup", "email"]:
                logger.warning(f"[{function_name}] Invalid reminder method '{method}', must be 'popup' or 'email', skipping")
                continue
            
            minutes = reminder["minutes"]
            if not isinstance(minutes, int) or minutes < 0 or minutes > 40320:
                logger.warning(f"[{function_name}] Invalid reminder minutes '{minutes}', must be integer 0-40320, skipping")
                continue
            
            validated_reminders.append({
                "method": method,
                "minutes": minutes
            })
        
        return validated_reminders
  • Helper to preserve existing event fields (summary, description, location, attendees) when not explicitly updated in modify_event.
    def _preserve_existing_fields(event_body: Dict[str, Any], existing_event: Dict[str, Any], field_mappings: Dict[str, Any]) -> None:
        """
        Helper function to preserve existing event fields when not explicitly provided.
    
        Args:
            event_body: The event body being built for the API call
            existing_event: The existing event data from the API
            field_mappings: Dict mapping field names to their new values (None means preserve existing)
        """
        for field_name, new_value in field_mappings.items():
            if new_value is None and field_name in existing_event:
                event_body[field_name] = existing_event[field_name]
                logger.info(f"[modify_event] Preserving existing {field_name}")
            elif new_value is not None:
                event_body[field_name] = new_value
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses that the tool modifies events and returns a confirmation message with a link, which adds some behavioral context. However, it doesn't cover critical aspects like authentication needs, error handling, rate limits, or what happens if parameters conflict with existing event data.

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

Conciseness4/5

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

The description is well-structured with clear sections for Args and Returns, making it easy to parse. It's appropriately sized, with each sentence adding necessary information. However, the parameter list is lengthy, which is unavoidable given the tool's complexity, but it remains efficient without wasted words.

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

Completeness3/5

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

Given the complexity (11 parameters, no annotations, no output schema), the description is moderately complete. It covers parameters thoroughly and includes return information, but lacks behavioral details like error cases, permissions, or side effects. For a mutation tool with no structured support, it should provide more context on risks and outcomes.

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

Parameters5/5

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

The schema description coverage is 0%, so the description must compensate. It provides detailed semantics for all 11 parameters, including data types, optionality, defaults, and examples (e.g., RFC3339 format for times). This adds significant value beyond the bare schema, fully documenting parameter meanings and usage.

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 tool's purpose with 'Modifies an existing event,' which is a specific verb+resource combination. However, it doesn't explicitly differentiate from sibling tools like 'create_event' or 'delete_event,' though the verb 'modifies' implies it's for updates rather than creation or deletion.

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 'create_event' or 'delete_event.' It lacks context about prerequisites, such as needing an existing event ID, and doesn't mention any exclusions or specific scenarios for usage.

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/taylorwilsdon/google_workspace_mcp'

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