Skip to main content
Glama

gmail_mark_as_unread_by_query

Mark Gmail emails matching a search query as unread. Use Gmail query syntax to filter emails, set maximum count, and preview before confirming changes.

Instructions

Mark emails matching a search query as unread. Use Gmail query syntax. Requires confirmation.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesGmail search query to find emails to mark as unread. Examples: 'from:newsletter@example.com', 'subject:important'.
max_emailsNoMaximum number of emails to mark as unread. Default 100, max 500.
confirmYesMust be true to actually mark as unread. Set false to preview what would be marked.

Implementation Reference

  • MCP tool handler for gmail_mark_as_unread_by_query. Handles input arguments, confirmation preview (searches read emails with query and shows list), and executes GmailClient.mark_as_unread_by_query on confirm.
    elif name == "gmail_mark_as_unread_by_query":
        query = arguments.get("query", "")
        max_emails = min(arguments.get("max_emails", 100), 500)
        confirm = arguments.get("confirm", False)
        
        if not query:
            return [TextContent(type="text", text="Error: query is required.")]
        
        if not confirm:
            # Preview mode - show what would be marked
            search_results = await client.search_emails(f"{query} -is:unread", max_emails)
            if not search_results:
                return [TextContent(type="text", text=f"No read emails match the query: {query}")]
            
            lines = [f"Preview: {len(search_results)} email(s) would be marked as unread:\n"]
            for email in search_results[:20]:
                lines.append(f"- {email.subject}")
                lines.append(f"  From: {email.sender.email}")
                lines.append(f"  Date: {email.date.strftime('%Y-%m-%d %H:%M')}")
                lines.append("")
            
            if len(search_results) > 20:
                lines.append(f"... and {len(search_results) - 20} more\n")
            
            lines.append("Set confirm=true to proceed.")
            return [TextContent(type="text", text="\n".join(lines))]
        else:
            result = await client.mark_as_unread_by_query(query, max_emails)
            return [TextContent(
                type="text",
                text=f"Success: {result['message']}\nMatched: {result['matched']}, Marked as unread: {result['success']}"
                + (f", Errors: {result['errors']}" if result['errors'] else "")
            )]
  • JSON Schema definition for the tool input parameters: query (required), max_emails (optional), confirm (required). Included in GMAIL_TOOLS list.
    Tool(
        name="gmail_mark_as_unread_by_query",
        description="Mark emails matching a search query as unread. Use Gmail query syntax. Requires confirmation.",
        inputSchema={
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Gmail search query to find emails to mark as unread. Examples: 'from:newsletter@example.com', 'subject:important'."
                },
                "max_emails": {
                    "type": "integer",
                    "description": "Maximum number of emails to mark as unread. Default 100, max 500."
                },
                "confirm": {
                    "type": "boolean",
                    "description": "Must be true to actually mark as unread. Set false to preview what would be marked."
                }
            },
            "required": ["query", "confirm"]
        },
    ),
  • GmailClient.mark_as_unread_by_query: Searches Gmail API for read emails (query -is:unread), gets message IDs, calls mark_as_unread to batch-add UNREAD label.
    async def mark_as_unread_by_query(self, query: str, max_emails: int = 100) -> dict:
        """Mark emails matching a query as unread.
        
        Args:
            query: Gmail search query (e.g., "from:newsletter@example.com", "older_than:7d")
            max_emails: Maximum number of emails to mark as unread (safety limit)
            
        Returns:
            Dict with success count, matched count, and any errors
        """
        try:
            # First, find matching emails (that are currently read)
            results = (
                self.service.users()
                .messages()
                .list(userId="me", q=f"{query} -is:unread", maxResults=max_emails)
                .execute()
            )
            
            messages = results.get("messages", [])
            if not messages:
                return {
                    "matched": 0,
                    "success": 0,
                    "errors": [],
                    "message": "No read emails matched the query"
                }
            
            message_ids = [msg["id"] for msg in messages]
            
            # Mark them as unread
            mark_result = await self.mark_as_unread(message_ids)
            mark_result["matched"] = len(messages)
            
            # Check if there might be more
            if len(messages) == max_emails:
                mark_result["message"] = f"Marked {mark_result['success']} emails as unread. There may be more matching emails (limit was {max_emails})."
            else:
                mark_result["message"] = f"Marked {mark_result['success']} emails as unread."
                
            return mark_result
            
        except HttpError as e:
            logger.error(f"Failed to search and mark emails as unread: {e}")
            return {"matched": 0, "success": 0, "errors": [str(e)], "message": f"Error: {e}"}
  • Low-level GmailClient.mark_as_unread: Uses Gmail API messages.batchModify to add 'UNREAD' label to given message IDs (batched for large lists).
    async def mark_as_unread(self, message_ids: list[str]) -> dict:
        """Mark one or more emails as unread by adding the UNREAD label.
        
        Args:
            message_ids: List of Gmail message IDs to mark as unread
            
        Returns:
            Dict with success count and any errors
        """
        if not message_ids:
            return {"success": 0, "errors": [], "message": "No message IDs provided"}
        
        results = {"success": 0, "errors": []}
        
        try:
            # Use batchModify for efficiency (up to 1000 at a time)
            if len(message_ids) <= 1000:
                self.service.users().messages().batchModify(
                    userId="me",
                    body={
                        "ids": message_ids,
                        "addLabelIds": ["UNREAD"]
                    }
                ).execute()
                results["success"] = len(message_ids)
            else:
                # Process in batches of 1000
                for i in range(0, len(message_ids), 1000):
                    batch = message_ids[i:i+1000]
                    self.service.users().messages().batchModify(
                        userId="me",
                        body={
                            "ids": batch,
                            "addLabelIds": ["UNREAD"]
                        }
                    ).execute()
                    results["success"] += len(batch)
                    
        except HttpError as e:
            logger.error(f"Failed to mark emails as unread: {e}")
            results["errors"].append(str(e))
            
        return results
  • MCP Server registration: Decorator registers list_tools handler that returns GMAIL_TOOLS containing the gmail_mark_as_unread_by_query tool schema.
    @server.list_tools()
    async def list_tools() -> list[Tool]:
        return GMAIL_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/murphy360/mcp_gmail'

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