Skip to main content
Glama
taylorwilsdon

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

modify_gmail_message_labels

Add or remove Gmail message labels to organize emails efficiently. Specify email address and message ID to update labels dynamically.

Instructions

Adds or removes labels from a Gmail message.

Args:
    user_google_email (str): The user's Google email address. Required.
    message_id (str): The ID of the message to modify.
    add_label_ids (Optional[List[str]]): List of label IDs to add to the message.
    remove_label_ids (Optional[List[str]]): List of label IDs to remove from the message.

Returns:
    str: Confirmation message of the label changes applied to the message.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
add_label_idsNo
message_idYes
remove_label_idsNo
serviceYes
user_google_emailYes

Implementation Reference

  • The primary handler function that implements the modify_gmail_message_labels tool. It uses the Gmail API to add or remove labels from a specific message.
    @handle_http_errors("modify_gmail_message_labels", service_type="gmail")
    @require_google_service("gmail", GMAIL_MODIFY_SCOPE)
    async def modify_gmail_message_labels(
        service,
        user_google_email: str,
        message_id: str,
        add_label_ids: List[str] = Field(default=[], description="Label IDs to add to the message."),
        remove_label_ids: List[str] = Field(default=[], description="Label IDs to remove from the message."),
    ) -> str:
        """
        Adds or removes labels from a Gmail message.
        To archive an email, remove the INBOX label.
        To delete an email, add the TRASH label.
    
        Args:
            user_google_email (str): The user's Google email address. Required.
            message_id (str): The ID of the message to modify.
            add_label_ids (Optional[List[str]]): List of label IDs to add to the message.
            remove_label_ids (Optional[List[str]]): List of label IDs to remove from the message.
    
        Returns:
            str: Confirmation message of the label changes applied to the message.
        """
        logger.info(
            f"[modify_gmail_message_labels] Invoked. Email: '{user_google_email}', Message ID: '{message_id}'"
        )
    
        if not add_label_ids and not remove_label_ids:
            raise Exception(
                "At least one of add_label_ids or remove_label_ids must be provided."
            )
    
        body = {}
        if add_label_ids:
            body["addLabelIds"] = add_label_ids
        if remove_label_ids:
            body["removeLabelIds"] = remove_label_ids
    
        await asyncio.to_thread(
            service.users().messages().modify(userId="me", id=message_id, body=body).execute
        )
    
        actions = []
        if add_label_ids:
            actions.append(f"Added labels: {', '.join(add_label_ids)}")
        if remove_label_ids:
            actions.append(f"Removed labels: {', '.join(remove_label_ids)}")
    
        return f"Message labels updated successfully!\nMessage ID: {message_id}\n{'; '.join(actions)}"
  • Pydantic schema definition for the tool's input parameters using Field for validation and descriptions.
        add_label_ids: List[str] = Field(default=[], description="Label IDs to add to the message."),
        remove_label_ids: List[str] = Field(default=[], description="Label IDs to remove from the message."),
    ) -> str:
  • Decorators that register the function as an MCP tool and apply necessary error handling and authentication scopes.
    @handle_http_errors("modify_gmail_message_labels", service_type="gmail")
  • Helper tool to list all Gmail labels, useful for obtaining label IDs needed for modify_gmail_message_labels.
    @handle_http_errors("list_gmail_labels", is_read_only=True, service_type="gmail")
    @require_google_service("gmail", "gmail_read")
    async def list_gmail_labels(service, user_google_email: str) -> str:
        """
        Lists all labels in the user's Gmail account.
    
        Args:
            user_google_email (str): The user's Google email address. Required.
    
        Returns:
            str: A formatted list of all labels with their IDs, names, and types.
        """
        logger.info(f"[list_gmail_labels] Invoked. Email: '{user_google_email}'")
    
        response = await asyncio.to_thread(
            service.users().labels().list(userId="me").execute
        )
        labels = response.get("labels", [])
    
        if not labels:
            return "No labels found."
    
        lines = [f"Found {len(labels)} labels:", ""]
    
        system_labels = []
        user_labels = []
    
        for label in labels:
            if label.get("type") == "system":
                system_labels.append(label)
            else:
                user_labels.append(label)
    
        if system_labels:
            lines.append("📂 SYSTEM LABELS:")
            for label in system_labels:
                lines.append(f"  • {label['name']} (ID: {label['id']})")
            lines.append("")
    
        if user_labels:
            lines.append("🏷️  USER LABELS:")
            for label in user_labels:
                lines.append(f"  • {label['name']} (ID: {label['id']})")
    
        return "\n".join(lines)
  • Helper tool to create, update, or delete Gmail labels, supporting label management workflow.
    @handle_http_errors("manage_gmail_label", service_type="gmail")
    @require_google_service("gmail", GMAIL_LABELS_SCOPE)
    async def manage_gmail_label(
        service,
        user_google_email: str,
        action: Literal["create", "update", "delete"],
        name: Optional[str] = None,
        label_id: Optional[str] = None,
        label_list_visibility: Literal["labelShow", "labelHide"] = "labelShow",
        message_list_visibility: Literal["show", "hide"] = "show",
    ) -> str:
        """
        Manages Gmail labels: create, update, or delete labels.
    
        Args:
            user_google_email (str): The user's Google email address. Required.
            action (Literal["create", "update", "delete"]): Action to perform on the label.
            name (Optional[str]): Label name. Required for create, optional for update.
            label_id (Optional[str]): Label ID. Required for update and delete operations.
            label_list_visibility (Literal["labelShow", "labelHide"]): Whether the label is shown in the label list.
            message_list_visibility (Literal["show", "hide"]): Whether the label is shown in the message list.
    
        Returns:
            str: Confirmation message of the label operation.
        """
        logger.info(
            f"[manage_gmail_label] Invoked. Email: '{user_google_email}', Action: '{action}'"
        )
    
        if action == "create" and not name:
            raise Exception("Label name is required for create action.")
    
        if action in ["update", "delete"] and not label_id:
            raise Exception("Label ID is required for update and delete actions.")
    
        if action == "create":
            label_object = {
                "name": name,
                "labelListVisibility": label_list_visibility,
                "messageListVisibility": message_list_visibility,
            }
            created_label = await asyncio.to_thread(
                service.users().labels().create(userId="me", body=label_object).execute
            )
            return f"Label created successfully!\nName: {created_label['name']}\nID: {created_label['id']}"
    
        elif action == "update":
            current_label = await asyncio.to_thread(
                service.users().labels().get(userId="me", id=label_id).execute
            )
    
            label_object = {
                "id": label_id,
                "name": name if name is not None else current_label["name"],
                "labelListVisibility": label_list_visibility,
                "messageListVisibility": message_list_visibility,
            }
    
            updated_label = await asyncio.to_thread(
                service.users()
                .labels()
                .update(userId="me", id=label_id, body=label_object)
                .execute
            )
            return f"Label updated successfully!\nName: {updated_label['name']}\nID: {updated_label['id']}"
    
        elif action == "delete":
            label = await asyncio.to_thread(
                service.users().labels().get(userId="me", id=label_id).execute
            )
            label_name = label["name"]
    
            await asyncio.to_thread(
                service.users().labels().delete(userId="me", id=label_id).execute
            )
            return f"Label '{label_name}' (ID: {label_id}) deleted successfully!"
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool modifies labels (implying mutation) but lacks critical details: required permissions (e.g., Gmail API scope), whether changes are reversible, error handling (e.g., invalid label IDs), or rate limits. The return value is mentioned but not elaborated (e.g., success/failure details).

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 well-structured and front-loaded: the first sentence states the core purpose, followed by a clear 'Args' and 'Returns' section. Every sentence adds value—no redundancy or fluff. It efficiently communicates essential information in a compact format.

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 tool's moderate complexity (mutation with 5 parameters), no annotations, and no output schema, the description is partially complete. It covers purpose and parameters well but lacks behavioral context (permissions, errors) and detailed output explanation. For a mutation tool, this leaves gaps that could hinder correct agent invocation.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate. It explicitly documents all 5 parameters with names, types, and brief semantics (e.g., 'user_google_email: The user's Google email address. Required.'). However, it doesn't explain the 'service' parameter (present in schema but missing from description), leaving one parameter undocumented. The coverage is high but incomplete.

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 ('Adds or removes labels') on a specific resource ('from a Gmail message'), distinguishing it from sibling tools like 'manage_gmail_label' (which likely manages label definitions) or 'get_gmail_message_content' (which reads content). The verb+resource combination is precise and unambiguous.

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. It doesn't mention prerequisites (e.g., needing valid label IDs from 'list_gmail_labels'), when to prefer it over other tools, or any constraints (e.g., cannot add and remove the same label simultaneously). Usage context is implied but not explicit.

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