Skip to main content
Glama
ZatesloFL

Google Workspace MCP Server

by ZatesloFL

create_table_with_data

Generate and populate a table in Google Docs using a 2D list of data. Requires index from inspect_doc_structure for accurate document positioning. Ensures proper formatting and structure for reliable table creation.

Instructions

Creates a table and populates it with data in one reliable operation.

CRITICAL: YOU MUST CALL inspect_doc_structure FIRST TO GET THE INDEX!

MANDATORY WORKFLOW - DO THESE STEPS IN ORDER:

Step 1: ALWAYS call inspect_doc_structure first Step 2: Use the 'total_length' value from inspect_doc_structure as your index Step 3: Format data as 2D list: [["col1", "col2"], ["row1col1", "row1col2"]] Step 4: Call this function with the correct index and data

EXAMPLE DATA FORMAT: table_data = [ ["Header1", "Header2", "Header3"], # Row 0 - headers ["Data1", "Data2", "Data3"], # Row 1 - first data row ["Data4", "Data5", "Data6"] # Row 2 - second data row ]

CRITICAL INDEX REQUIREMENTS:

  • NEVER use index values like 1, 2, 10 without calling inspect_doc_structure first

  • ALWAYS get index from inspect_doc_structure 'total_length' field

  • Index must be a valid insertion point in the document

DATA FORMAT REQUIREMENTS:

  • Must be 2D list of strings only

  • Each inner list = one table row

  • All rows MUST have same number of columns

  • Use empty strings "" for empty cells, never None

  • Use debug_table_structure after creation to verify results

Args: user_google_email: User's Google email address document_id: ID of the document to update table_data: 2D list of strings - EXACT format: [["col1", "col2"], ["row1col1", "row1col2"]] index: Document position (MANDATORY: get from inspect_doc_structure 'total_length') bold_headers: Whether to make first row bold (default: true)

Returns: str: Confirmation with table details and link

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bold_headersNo
document_idYes
indexYes
table_dataYes
user_google_emailYes

Implementation Reference

  • Main MCP tool handler for 'create_table_with_data'. Includes @server.tool() registration, input validation, error handling, and delegates core logic to TableOperationManager. Input schema defined by function parameters and type hints.
    @server.tool()
    @handle_http_errors("create_table_with_data", service_type="docs")
    @require_google_service("docs", "docs_write")
    async def create_table_with_data(
        service,
        user_google_email: str,
        document_id: str,
        table_data: list,
        index: int,
        bold_headers: bool = True,
    ) -> str:
        """
        Creates a table and populates it with data in one reliable operation.
    
        CRITICAL: YOU MUST CALL inspect_doc_structure FIRST TO GET THE INDEX!
    
        MANDATORY WORKFLOW - DO THESE STEPS IN ORDER:
    
        Step 1: ALWAYS call inspect_doc_structure first
        Step 2: Use the 'total_length' value from inspect_doc_structure as your index
        Step 3: Format data as 2D list: [["col1", "col2"], ["row1col1", "row1col2"]]
        Step 4: Call this function with the correct index and data
    
        EXAMPLE DATA FORMAT:
        table_data = [
            ["Header1", "Header2", "Header3"],    # Row 0 - headers
            ["Data1", "Data2", "Data3"],          # Row 1 - first data row
            ["Data4", "Data5", "Data6"]           # Row 2 - second data row
        ]
    
        CRITICAL INDEX REQUIREMENTS:
        - NEVER use index values like 1, 2, 10 without calling inspect_doc_structure first
        - ALWAYS get index from inspect_doc_structure 'total_length' field
        - Index must be a valid insertion point in the document
    
        DATA FORMAT REQUIREMENTS:
        - Must be 2D list of strings only
        - Each inner list = one table row
        - All rows MUST have same number of columns
        - Use empty strings "" for empty cells, never None
        - Use debug_table_structure after creation to verify results
    
        Args:
            user_google_email: User's Google email address
            document_id: ID of the document to update
            table_data: 2D list of strings - EXACT format: [["col1", "col2"], ["row1col1", "row1col2"]]
            index: Document position (MANDATORY: get from inspect_doc_structure 'total_length')
            bold_headers: Whether to make first row bold (default: true)
    
        Returns:
            str: Confirmation with table details and link
        """
        logger.debug(f"[create_table_with_data] Doc={document_id}, index={index}")
    
        # Input validation
        validator = ValidationManager()
    
        is_valid, error_msg = validator.validate_document_id(document_id)
        if not is_valid:
            return f"ERROR: {error_msg}"
    
        is_valid, error_msg = validator.validate_table_data(table_data)
        if not is_valid:
            return f"ERROR: {error_msg}"
    
        is_valid, error_msg = validator.validate_index(index, "Index")
        if not is_valid:
            return f"ERROR: {error_msg}"
    
        # Use TableOperationManager to handle the complex logic
        table_manager = TableOperationManager(service)
    
        # Try to create the table, and if it fails due to index being at document end, retry with index-1
        success, message, metadata = await table_manager.create_and_populate_table(
            document_id, table_data, index, bold_headers
        )
    
        # If it failed due to index being at or beyond document end, retry with adjusted index
        if not success and "must be less than the end index" in message:
            logger.debug(f"Index {index} is at document boundary, retrying with index {index - 1}")
            success, message, metadata = await table_manager.create_and_populate_table(
                document_id, table_data, index - 1, bold_headers
            )
    
        if success:
            link = f"https://docs.google.com/document/d/{document_id}/edit"
            rows = metadata.get('rows', 0)
            columns = metadata.get('columns', 0)
    
            return f"SUCCESS: {message}. Table: {rows}x{columns}, Index: {index}. Link: {link}"
        else:
            return f"ERROR: {message}"
  • Core helper method in TableOperationManager that implements the table creation and population logic. Handles multi-step process: create empty table, refresh structure, populate cells one-by-one with index correction to avoid shifting issues.
    async def create_and_populate_table(
        self,
        document_id: str,
        table_data: List[List[str]],
        index: int,
        bold_headers: bool = True
    ) -> Tuple[bool, str, Dict[str, Any]]:
        """
        Creates a table and populates it with data in a reliable multi-step process.
        
        This method extracts the complex logic from create_table_with_data tool function.
        
        Args:
            document_id: ID of the document to update
            table_data: 2D list of strings for table content
            index: Position to insert the table
            bold_headers: Whether to make the first row bold
            
        Returns:
            Tuple of (success, message, metadata)
        """
        logger.debug(f"Creating table at index {index}, dimensions: {len(table_data)}x{len(table_data[0]) if table_data and len(table_data) > 0 else 0}")
        
        # Validate input data
        is_valid, error_msg = validate_table_data(table_data)
        if not is_valid:
            return False, f"Invalid table data: {error_msg}", {}
            
        rows = len(table_data)
        cols = len(table_data[0])
        
        try:
            # Step 1: Create empty table
            await self._create_empty_table(document_id, index, rows, cols)
            
            # Step 2: Get fresh document structure to find actual cell positions
            fresh_tables = await self._get_document_tables(document_id)
            if not fresh_tables:
                return False, "Could not find table after creation", {}
                
            # Step 3: Populate each cell with proper index refreshing
            population_count = await self._populate_table_cells(
                document_id, table_data, bold_headers
            )
            
            metadata = {
                'rows': rows,
                'columns': cols,
                'populated_cells': population_count,
                'table_index': len(fresh_tables) - 1
            }
            
            return True, f"Successfully created {rows}x{cols} table and populated {population_count} cells", metadata
            
        except Exception as e:
            logger.error(f"Failed to create and populate table: {str(e)}")
            return False, f"Table creation failed: {str(e)}", {}
  • Input validation function for table_data parameter. Checks for 2D list format, consistent dimensions, size limits, no None values. Provides LLM-friendly error messages with examples.
    def validate_table_data(data: List[List[str]]) -> Tuple[bool, str]:
        """
        Validates table data format and provides specific error messages for LLMs.
        
        WHAT THIS CHECKS:
        - Data is a 2D list (list of lists)
        - All rows have consistent column counts
        - Dimensions are within Google Docs limits
        - No None or undefined values
        
        VALID FORMAT EXAMPLE:
        [
            ["Header1", "Header2"],     # Row 0 - 2 columns
            ["Data1", "Data2"],        # Row 1 - 2 columns  
            ["Data3", "Data4"]         # Row 2 - 2 columns
        ]
        
        INVALID FORMATS:
        - [["col1"], ["col1", "col2"]]  # Inconsistent column counts
        - ["col1", "col2"]              # Not 2D (missing inner lists)
        - [["col1", None]]              # Contains None values
        - [] or [[]]                    # Empty data
        
        Args:
            data: 2D array of data to validate
        
        Returns:
            Tuple of (is_valid, error_message_with_examples)
        """
        if not data:
            return False, "Data is empty. Use format: [['col1', 'col2'], ['row1col1', 'row1col2']]"
        
        if not isinstance(data, list):
            return False, f"Data must be a list, got {type(data).__name__}. Use format: [['col1', 'col2'], ['row1col1', 'row1col2']]"
        
        if not all(isinstance(row, list) for row in data):
            return False, f"Data must be a 2D list (list of lists). Each row must be a list. Check your format: {data}"
        
        # Check for consistent column count
        col_counts = [len(row) for row in data]
        if len(set(col_counts)) > 1:
            return False, f"All rows must have same number of columns. Found: {col_counts}. Fix your data format."
        
        # Check for reasonable size
        rows = len(data)
        cols = col_counts[0] if col_counts else 0
        
        if rows > 1000:
            return False, f"Too many rows ({rows}). Google Docs limit is 1000 rows."
        
        if cols > 20:
            return False, f"Too many columns ({cols}). Google Docs limit is 20 columns."
        
        return True, f"Valid table data: {rows}x{cols} table format"
  • MCP tool registration via @server.tool() decorator on the handler function, with error handling and auth requirements.
    @server.tool()
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/ZatesloFL/google_workspace_mcp'

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