Skip to main content
Glama

update_table_cells_by_range_tool

Modify cell data within a specific range in Google Sheets tables using A1 notation to update multiple values at once.

Instructions

Update table cells by range.

This tool updates cell data in a specific range within a table.
You can specify the exact cell range using A1 notation.

Args:
    spreadsheet_name: Name of the spreadsheet
    sheet_name: Name of the sheet containing the table
    table_name: Name of the table to update
    start_cell: Starting cell reference (e.g., 'A1', 'B2')
    end_cell: Ending cell reference (e.g., 'C5', 'D10')
    cell_values: 2D array of values to update (rows x columns)

Returns:
    JSON string with update 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 update
start_cellYesStarting cell reference (e.g., 'A1', 'B2')
end_cellYesEnding cell reference (e.g., 'C5', 'D10')
cell_valuesYes2D array of values to update (rows x columns) Must match the range dimensions based on start_cell and end_cell. EXAMPLE: For range 'A1:C2', you need 2 rows x 3 columns: [ ['Value1', 'Value2', 'Value3'], ['Value4', 'Value5', 'Value6'] ]

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core implementation of the tool logic. Validates inputs, resolves table-relative range to absolute sheet range, validates and cleans cell values, then updates the range using Google Sheets API spreadsheets.values.update().
    def update_table_cells_by_range_handler(
        drive_service,
        sheets_service,
        spreadsheet_name: str,
        sheet_name: str,
        table_name: str,
        start_cell: str,
        end_cell: str,
        cell_values: List[List[Union[str, int, float, bool, None]]]
    ) -> str:
        """
        Update table cells by range in Google Sheets using the official updateCells operation.
        
        According to the official Google Sheets API documentation, to update cells by range:
        1. Use UpdateCellsRequest to update cells in the specified range
        2. Apply proper formatting based on column types
        3. Validate cell values before updating
        
        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 update
            start_cell: Starting cell reference (e.g., 'A1', 'B2')
            end_cell: Ending cell reference (e.g., 'C5', 'D10')
            cell_values: 2D array of values to update (rows x columns)
        
        Returns:
            str: Success message with update 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 start_cell or not end_cell:
                return compact_json_response({
                    "success": False,
                    "message": "Both start_cell and end_cell are required."
                })
            
            if not cell_values or not isinstance(cell_values, list):
                return compact_json_response({
                    "success": False,
                    "message": "Cell values are required and must be a 2D array."
                })
            
            # Parse cell references
            try:
                start_row, start_col = parse_cell_reference(start_cell)
                end_row, end_col = parse_cell_reference(end_cell)
            except ValueError as e:
                return compact_json_response({
                    "success": False,
                    "message": f"Invalid cell reference: {str(e)}"
                })
            
            # Validate range
            if start_row > end_row or start_col > end_col:
                return compact_json_response({
                    "success": False,
                    "message": "Invalid range. start_cell should be top-left and end_cell should be bottom-right."
                })
            
            expected_rows = end_row - start_row + 1
            expected_cols = end_col - start_col + 1
            
            if len(cell_values) != expected_rows:
                return compact_json_response({
                    "success": False,
                    "message": f"Expected {expected_rows} rows, but got {len(cell_values)} rows."
                })
            
            # Validate cell values structure
            validated_values = []
            invalid_cells = []
            
            for row_idx, row in enumerate(cell_values):
                if not isinstance(row, list):
                    invalid_cells.append({"row": start_row + row_idx + 1, "error": "Row must be a list"})
                    continue
                
                if len(row) != expected_cols:
                    invalid_cells.append({
                        "row": start_row + row_idx + 1, 
                        "error": f"Expected {expected_cols} columns, but got {len(row)} columns"
                    })
                    continue
                
                validated_row = []
                for col_idx, value in enumerate(row):
                    # Validate cell value
                    value_validation = validate_cell_value(value)
                    if not value_validation["valid"]:
                        invalid_cells.append({
                            "row": start_row + row_idx + 1,
                            "column": start_col + col_idx + 1,
                            "error": value_validation["error"]
                        })
                        continue
                    
                    validated_row.append(value_validation["cleaned_value"])
                
                validated_values.append(validated_row)
            
            if invalid_cells:
                error_messages = [f"Row {item['row']}, Col {item.get('column', 'N/A')}: {item['error']}" for item in invalid_cells]
                return compact_json_response({
                    "success": False,
                    "message": f"Invalid cell values: {'; '.join(error_messages)}",
                    "invalid_cells": invalid_cells
                })
            
            # 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', {})
                columns = table_info.get('columns', [])
            except Exception as e:
                return compact_json_response({
                    "success": False,
                    "message": f"Failed to get table information: {str(e)}"
                })
            
            if not table_range:
                return compact_json_response({
                    "success": False,
                    "message": f"Table '{table_name}' has no valid range."
                })
            
            # Convert table coordinates to sheet coordinates
            table_start_row = table_range.get('startRowIndex', 0)
            table_start_col = table_range.get('startColumnIndex', 0)
            
            # Calculate sheet coordinates (end indices are exclusive)
            sheet_start_row = table_start_row + start_row
            sheet_end_row = table_start_row + end_row + 1  # Add 1 for exclusive
            sheet_start_col = table_start_col + start_col
            sheet_end_col = table_start_col + end_col + 1  # Add 1 for exclusive
            
            # Convert to A1 notation
            start_cell_sheet = f"{column_index_to_letter(sheet_start_col)}{sheet_start_row + 1}"
            end_cell_sheet = f"{column_index_to_letter(sheet_end_col)}{sheet_end_row + 1}"
            range_notation = f"{sheet_name}!{start_cell_sheet}:{end_cell_sheet}"
            
            # Update cell values
            try:
                body = {
                    'values': validated_values
                }
                
                result = sheets_service.spreadsheets().values().update(
                    spreadsheetId=spreadsheet_id,
                    range=range_notation,
                    valueInputOption='RAW',
                    body=body
                ).execute()
                
                updated_cells = result.get('updatedCells', 0)
                
                return compact_json_response({
                    "success": True,
                    "message": f"Successfully updated {updated_cells} cells in table '{table_name}'",
                    "data": {
                        "table_name": table_name,
                        "range": {
                            "start_cell": start_cell,
                            "end_cell": end_cell,
                            "start_row": start_row + 1,  # Convert back to 1-based
                            "end_row": end_row + 1,
                            "start_column": start_col + 1,  # Convert back to 1-based
                            "end_column": end_col + 1
                        },
                        "updated_cells": updated_cells,
                        "updated_range": result.get('updatedRange', range_notation)
                    }
                })
                
            except HttpError as e:
                error_details = e.error_details[0] if e.error_details else {}
                return compact_json_response({
                    "success": False,
                    "message": f"Failed to update cell data: {error_details.get('message', str(e))}",
                    "error_code": e.resp.status
                })
                
        except Exception as e:
            return compact_json_response({
                "success": False,
                "message": f"Unexpected error: {str(e)}"
            })
  • MCP tool registration using @mcp.tool() decorator. Defines the tool entrypoint function with input schema via Pydantic Fields. Delegates execution to the core handler function.
    @mcp.tool()
    def update_table_cells_by_range_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 update"),
        start_cell: str = Field(..., description="Starting cell reference (e.g., 'A1', 'B2')"),
        end_cell: str = Field(..., description="Ending cell reference (e.g., 'C5', 'D10')"),
        cell_values: List[List[Union[str, int, float, bool, None]]] = Field(..., description="""2D array of values to update (rows x columns)
        
        Must match the range dimensions based on start_cell and end_cell.
        
        EXAMPLE: For range 'A1:C2', you need 2 rows x 3 columns:
        [
            ['Value1', 'Value2', 'Value3'],
            ['Value4', 'Value5', 'Value6']
        ]
        """)
    ) -> str:
        """
        Update table cells by range.
        
        This tool updates cell data in a specific range within a table.
        You can specify the exact cell range using A1 notation.
        
        Args:
            spreadsheet_name: Name of the spreadsheet
            sheet_name: Name of the sheet containing the table
            table_name: Name of the table to update
            start_cell: Starting cell reference (e.g., 'A1', 'B2')
            end_cell: Ending cell reference (e.g., 'C5', 'D10')
            cell_values: 2D array of values to update (rows x columns)
        
        Returns:
            JSON string with update details
        """
        sheets_service, drive_service = _get_google_services()
        return update_table_cells_by_range_handler(
            drive_service, 
            sheets_service, 
            spreadsheet_name, 
            sheet_name, 
            table_name, 
            start_cell, 
            end_cell, 
            cell_values
        )
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure. It states this is an update operation (implying mutation) and mentions the return format ('JSON string with update details'), but lacks critical behavioral information like permission requirements, whether updates overwrite existing data, error handling for mismatched dimensions, or rate limits. For a mutation tool with 6 parameters and no annotations, this is insufficient.

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 (purpose, usage note, args, returns) and front-loads the core functionality. At 7 sentences, it's appropriately sized without unnecessary elaboration. The 'Args' and 'Returns' sections could be slightly more integrated with the main description, but overall it's efficient.

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 this is a mutation tool with 6 parameters, no annotations, but with 100% schema coverage and an output schema (implied by 'Returns' statement), the description is moderately complete. It covers the basic operation and parameters but lacks important behavioral context about permissions, data overwriting, error conditions, and comparison to sibling tools that would be needed for safe and effective use.

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 documents all 6 parameters thoroughly. The description adds minimal value beyond the schema - it mentions A1 notation for cell references (implied in schema) and the 2D array structure (detailed in schema). The description doesn't provide additional semantic context like parameter relationships or usage examples beyond what's in the schema.

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 ('updates cell data'), target resource ('in a specific range within a table'), and method ('using A1 notation'). It distinguishes itself from sibling tools like 'update_table_cells_by_notation_tool' by specifying range-based updates rather than notation-based ones, and from read-only siblings like 'get_table_data_tool' by its write operation.

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 for updating cell data in tables with A1 notation, but doesn't explicitly state when to use this tool versus alternatives like 'update_table_cells_by_notation_tool' or 'update_table_column_type_tool'. It provides basic context about range specification but lacks explicit guidance on tool selection criteria or exclusion scenarios.

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