Skip to main content
Glama

delete_table_records_tool

Remove specific rows from Google Sheets tables while maintaining table structure. Delete records by row number to clean data without affecting remaining entries.

Instructions

Delete specific records (rows) from a table.

This tool removes specific records from a table while preserving the table structure.
Record numbers are 1-based and exclude the header row. Records are deleted in descending order
(bigger numbers first) to avoid index shifting issues.

Args:
    spreadsheet_name: Name of the spreadsheet
    sheet_name: Name of the sheet containing the table
    table_name: Name of the table to delete records from
    record_numbers: List of record numbers to delete (1-based, excluding header)

Returns:
    JSON string with success status and deletion details

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
spreadsheet_nameYesThe name of the Google Spreadsheet
sheet_nameYesThe name of the sheet containing the table
table_nameYesName of the table to delete records from
record_numbersYesList of record numbers to delete (1-based, excluding header)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core handler function that performs validation, retrieves table information, constructs DeleteRangeRequests for specified rows (in descending order), executes batchUpdate on Google Sheets API, and returns JSON response.
    def delete_table_records_handler(
        drive_service,
        sheets_service,
        spreadsheet_name: str,
        sheet_name: str,
        table_name: str,
        record_numbers: List[int]
    ) -> str:
        """
        Delete specific records (rows) from a table in Google Sheets using DeleteRangeRequest and UpdateTableRequest.
        
        According to the official Google Sheets API documentation, to delete records from a table:
        1. Use DeleteRangeRequest to remove specific rows from the sheet
        2. Use UpdateTableRequest to shrink the table's range to exclude deleted rows
        3. Delete records in descending order (bigger numbers first) to avoid index shifting
        
        Args:
            drive_service: Google Drive service instance
            sheets_service: Google Sheets service instance
            spreadsheet_name: Name of the spreadsheet
            sheet_name: Name of the sheet containing the table
            table_name: Name of the table to delete records from
            record_numbers: List of record numbers to delete (1-based, excluding header)
        
        Returns:
            str: Success message with deletion details or error message
        """
        try:
            # Validate inputs
            if not table_name or table_name.strip() == "":
                return compact_json_response({
                    "success": False,
                    "message": "Table name is required."
                })
            
            if not record_numbers or not isinstance(record_numbers, list) or len(record_numbers) == 0:
                return compact_json_response({
                    "success": False,
                    "message": "Record numbers are required. Please provide at least one record number to delete."
                })
            
            # Validate record numbers
            validated_numbers = []
            invalid_numbers = []
            
            for i, record_number in enumerate(record_numbers):
                if not isinstance(record_number, int):
                    invalid_numbers.append({"index": i, "value": record_number, "error": "Record number must be an integer"})
                    continue
                
                if record_number < 1:
                    invalid_numbers.append({"index": i, "value": record_number, "error": "Record number must be 1 or greater"})
                    continue
                
                validated_numbers.append(record_number)
            
            if invalid_numbers:
                error_messages = [f"Record {item['index']+1} ({item['value']}): {item['error']}" for item in invalid_numbers]
                return compact_json_response({
                    "success": False,
                    "message": f"Invalid record numbers: {'; '.join(error_messages)}",
                    "invalid_numbers": invalid_numbers
                })
            
            if not validated_numbers:
                return compact_json_response({
                    "success": False,
                    "message": "No valid record numbers provided after validation."
                })
            
            # Remove duplicates and sort in descending order (to avoid index shifting)
            unique_numbers = list(set(validated_numbers))
            unique_numbers.sort(reverse=True)  # Delete bigger numbers first
            
            # Get spreadsheet ID
            spreadsheet_id = get_spreadsheet_id_by_name(drive_service, spreadsheet_name)
            if not spreadsheet_id:
                return compact_json_response({
                    "success": False,
                    "message": f"Spreadsheet '{spreadsheet_name}' not found."
                })
    
            # Get sheet ID
            sheet_ids = get_sheet_ids_by_names(sheets_service, spreadsheet_id, [sheet_name])
            sheet_id = sheet_ids.get(sheet_name)
            if sheet_id is None:
                return compact_json_response({
                    "success": False,
                    "message": f"Sheet '{sheet_name}' not found in spreadsheet '{spreadsheet_name}'."
                })
            
            # Get table ID
            table_ids = get_table_ids_by_names(sheets_service, spreadsheet_id, sheet_name, [table_name])
            table_id = table_ids.get(table_name)
            if not table_id:
                return compact_json_response({
                    "success": False,
                    "message": f"Table '{table_name}' not found in sheet '{sheet_name}'."
                })
            
            # Get table information
            try:
                table_info = get_table_info(sheets_service, spreadsheet_id, table_id)
                table_range = table_info.get("range", {})
            except Exception as e:
                return compact_json_response({
                    "success": False,
                    "message": f"Could not retrieve information for table '{table_name}': {str(e)}"
                })
            
            # Get table boundaries
            table_start_row = table_range.get("startRowIndex", 0)
            table_end_row = table_range.get("endRowIndex", 0)
            table_start_col = table_range.get("startColumnIndex", 0)
            table_end_col = table_range.get("endColumnIndex", 0)
            
            # Validate row indices are within table range
            invalid_range_indices = []
            valid_delete_requests = []
            
            # Convert 1-based user index to 0-based API index (accounting for header)
            # and then to 0-based API index for deletion (accounting for header)
            for row_index in unique_numbers:
                api_row_index = table_start_row + row_index
                
                if api_row_index < table_start_row or api_row_index >= table_end_row:
                    invalid_range_indices.append({
                        "row_index": row_index,
                        "error": f"Row {row_index} is outside table range (1 to {table_end_row - table_start_row - 1})"
                    })
                else:
                    # Create DeleteRangeRequest for this row
                    delete_range_request = {
                        "deleteRange": {
                            "range": {
                                "sheetId": sheet_id,
                                "startRowIndex": api_row_index,
                                "endRowIndex": api_row_index + 1,
                                "startColumnIndex": table_start_col,
                                "endColumnIndex": table_end_col
                            },
                            "shiftDimension": "ROWS"
                        }
                    }
                    valid_delete_requests.append(delete_range_request)
            
            if invalid_range_indices:
                error_messages = [f"Row {item['row_index']}: {item['error']}" for item in invalid_range_indices]
                return compact_json_response({
                    "success": False,
                    "message": f"Invalid row indices: {'; '.join(error_messages)}",
                    "invalid_range_indices": invalid_range_indices
                })
            
            if not valid_delete_requests:
                return compact_json_response({
                    "success": False,
                    "message": "No valid rows to delete after range validation."
                })
            
            # Use only delete range requests
            all_requests = valid_delete_requests
            
            # Execute the batch update requests
            response = sheets_service.spreadsheets().batchUpdate(
                spreadsheetId=spreadsheet_id,
                body={"requests": all_requests}
            ).execute()
            
            # Extract response information
            replies = response.get("replies", [])
            delete_count = len(valid_delete_requests)
            
            response_data = {
                "success": True,
                "spreadsheet_name": spreadsheet_name,
                "sheet_name": sheet_name,
                "table_name": table_name,
                "rows_deleted": delete_count,
                "deleted_row_indices": unique_numbers,
                "message": f"Successfully deleted {delete_count} row(s) from table '{table_name}' in '{sheet_name}'"
            }
            
            return compact_json_response(response_data)
            
        except HttpError as error:
            return compact_json_response({
                "success": False,
                "message": f"Google Sheets API error: {str(error)}"
            })
        except Exception as e:
            return compact_json_response({
                "success": False,
                "message": f"Error deleting table rows: {str(e)}"
            }) 
  • MCP tool registration with @mcp.tool() decorator, Pydantic input schema via Field descriptions, and wrapper that initializes services and calls the handler function.
    @mcp.tool()
    def delete_table_records_tool(
        spreadsheet_name: str = Field(..., description="The name of the Google Spreadsheet"),
        sheet_name: str = Field(..., description="The name of the sheet containing the table"),
        table_name: str = Field(..., description="Name of the table to delete records from"),
        record_numbers: List[int] = Field(..., description="List of record numbers to delete (1-based, excluding header)")
    ) -> str:
        """
        Delete specific records (rows) from a table.
        
        This tool removes specific records from a table while preserving the table structure.
        Record numbers are 1-based and exclude the header row. Records are deleted in descending order
        (bigger numbers first) to avoid index shifting issues.
        
        Args:
            spreadsheet_name: Name of the spreadsheet
            sheet_name: Name of the sheet containing the table
            table_name: Name of the table to delete records from
            record_numbers: List of record numbers to delete (1-based, excluding header)
        
        Returns:
            JSON string with success status and deletion details
        """
        sheets_service, drive_service = _get_google_services()
        return delete_table_records_handler(drive_service, sheets_service, spreadsheet_name, sheet_name, table_name, record_numbers)
  • Pydantic Field definitions providing input schema validation and descriptions for the tool parameters.
        spreadsheet_name: str = Field(..., description="The name of the Google Spreadsheet"),
        sheet_name: str = Field(..., description="The name of the sheet containing the table"),
        table_name: str = Field(..., description="Name of the table to delete records from"),
        record_numbers: List[int] = Field(..., description="List of record numbers to delete (1-based, excluding header)")
    ) -> str:
Behavior4/5

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

With no annotations provided, the description carries full burden and does well by disclosing key behavioral traits: it's destructive (removes records), preserves table structure, uses 1-based indexing excluding headers, and deletes in descending order to avoid index shifting. It doesn't mention permission requirements, rate limits, or what happens with invalid record numbers, but provides substantial operational context.

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 efficiently structured with a clear purpose statement first, followed by important behavioral details, then parameter and return value sections. Every sentence earns its place by providing essential information without redundancy. The formatting with clear sections enhances readability.

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?

For a destructive operation with no annotations and an output schema, the description provides good context about the deletion behavior, indexing rules, and ordering. With an output schema handling return values, the description focuses appropriately on operational semantics. It could be more complete by mentioning error handling or permission requirements, but covers the essential destructive nature well.

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%, so the schema already fully documents all four parameters. The description repeats the record_numbers parameter explanation but doesn't add meaningful semantic context beyond what's in the schema. The baseline of 3 is appropriate when the schema does the heavy lifting for parameter documentation.

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 tool's purpose with specific verb ('Delete') and resource ('specific records (rows) from a table'), distinguishing it from sibling tools like delete_table_tool (which deletes entire tables) and delete_table_column_tool (which deletes columns). It precisely defines the scope as record-level deletion while preserving table structure.

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

Usage Guidelines3/5

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

The description implies usage context through the explanation of 1-based indexing and descending deletion order, but doesn't explicitly state when to use this tool versus alternatives like delete_table_tool or delete_sheets_tool. No guidance is provided about prerequisites, error conditions, or specific scenarios where this tool is preferred over other deletion methods.

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/henilcalagiya/google-sheets-mcp'

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