Skip to main content
Glama

get_table_metadata_tool

Retrieve detailed metadata about Google Sheets tables, including structure, columns, data types, formatting, and statistics for data analysis and management.

Instructions

Get comprehensive metadata for tables in Google Sheets.

This tool provides detailed information about table structure, columns, data types,
formatting, statistics, and other properties. If no table name is provided, returns 
metadata for all tables.

Args:
    spreadsheet_name: Name of the spreadsheet
    sheet_name: Name of the sheet containing the table
    table_name: Name of the table to get metadata for (optional)
    include_sample_data: Whether to include sample data rows
    include_formulas: Whether to include formula information
    include_formatting: Whether to include cell formatting details
    include_statistics: Whether to include data statistics
    max_sample_rows: Maximum number of sample rows to return
    specific_columns: List of column names to get metadata for (optional)
    exclude_metadata_types: List of metadata types to exclude

Returns:
    JSON string containing table metadata or list of all tables

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
spreadsheet_nameYesThe name of the Google Spreadsheet
sheet_nameYesThe name of the sheet containing the table
table_nameNoName of the table to get metadata for. If not provided, returns metadata for all tables in the sheet.
include_sample_dataNoWhether to include sample data rows
include_formulasNoWhether to include formula information
include_formattingNoWhether to include cell formatting details
include_statisticsNoWhether to include data statistics
max_sample_rowsNoMaximum number of sample rows to return
specific_columnsNoList of column names to get metadata for (optional)
exclude_metadata_typesNoList of metadata types to exclude (e.g., ['sample_data', 'formatting', 'statistics', 'merges', 'conditional_formatting', 'filters'])

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Tool registration with @mcp.tool() decorator, input schema via Pydantic Fields, and wrapper calling the handler.
    @mcp.tool()
    def get_table_metadata_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(default=None, description="Name of the table to get metadata for. If not provided, returns metadata for all tables in the sheet."),
        include_sample_data: bool = Field(default=False, description="Whether to include sample data rows"),
        include_formulas: bool = Field(default=False, description="Whether to include formula information"),
        include_formatting: bool = Field(default=False, description="Whether to include cell formatting details"),
        include_statistics: bool = Field(default=False, description="Whether to include data statistics"),
        max_sample_rows: int = Field(default=5, description="Maximum number of sample rows to return"),
        specific_columns: List[str] = Field(default=None, description="List of column names to get metadata for (optional)"),
        exclude_metadata_types: List[str] = Field(default=None, description="List of metadata types to exclude (e.g., ['sample_data', 'formatting', 'statistics', 'merges', 'conditional_formatting', 'filters'])")
    ) -> str:
        """
        Get comprehensive metadata for tables in Google Sheets.
        
        This tool provides detailed information about table structure, columns, data types,
        formatting, statistics, and other properties. If no table name is provided, returns 
        metadata for all tables.
        
        Args:
            spreadsheet_name: Name of the spreadsheet
            sheet_name: Name of the sheet containing the table
            table_name: Name of the table to get metadata for (optional)
            include_sample_data: Whether to include sample data rows
            include_formulas: Whether to include formula information
            include_formatting: Whether to include cell formatting details
            include_statistics: Whether to include data statistics
            max_sample_rows: Maximum number of sample rows to return
            specific_columns: List of column names to get metadata for (optional)
            exclude_metadata_types: List of metadata types to exclude
        
        Returns:
            JSON string containing table metadata or list of all tables
        """
        sheets_service, drive_service = _get_google_services()
        return get_table_metadata_handler(
            drive_service,
            sheets_service,
            spreadsheet_name,
            sheet_name,
            table_name,
            include_sample_data,
            include_formulas,
            include_formatting,
            include_statistics,
            max_sample_rows,
            specific_columns,
            exclude_metadata_types
        )
  • Core handler function executing the tool logic: fetches spreadsheet/sheet/table info via Google APIs, computes metadata including dimensions, columns, sample data, formatting, statistics, merges, conditional formatting, filters; handles single table or all tables; includes input validation and error handling.
    def get_table_metadata_handler(
        drive_service,
        sheets_service,
        spreadsheet_name: str,
        sheet_name: str,
        table_name: str = None,
        include_sample_data: bool = False,
        include_formulas: bool = False,
        include_formatting: bool = False,
        include_statistics: bool = False,
        max_sample_rows: int = 5,
        specific_columns: List[str] = None,
        exclude_metadata_types: List[str] = None
    ) -> str:
        """
        Get comprehensive metadata for a specific table in Google Sheets.
        If table_name is not provided, returns a list of all tables in the sheet.
        
        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 get metadata for (optional)
            include_sample_data: Whether to include sample data rows
            include_formulas: Whether to include formula information
            include_formatting: Whether to include cell formatting details
            include_statistics: Whether to include data statistics
            max_sample_rows: Maximum number of sample rows to return
            specific_columns: List of column names to get metadata for (optional)
            exclude_metadata_types: List of metadata types to exclude (optional)
        
        Returns:
            str: JSON-formatted string containing table metadata or list of all tables
        """
        try:
            # Validate inputs
            if not spreadsheet_name or spreadsheet_name.strip() == "":
                return compact_json_response({
                    "success": False,
                    "message": "Spreadsheet name is required."
                })
            
            if not sheet_name or sheet_name.strip() == "":
                return compact_json_response({
                    "success": False,
                    "message": "Sheet name is required."
                })
            
            # Validate table name if provided
            if table_name:
                table_validation = validate_table_name(table_name)
                if not table_validation["valid"]:
                    return compact_json_response({
                        "success": False,
                        "message": table_validation["error"]
                    })
                validated_table_name = table_validation["cleaned_name"]
            else:
                validated_table_name = None
            
            # Get spreadsheet ID and metadata
            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 spreadsheet metadata for modification info
            spreadsheet_metadata = drive_service.files().get(
                fileId=spreadsheet_id,
                fields="modifiedTime,lastModifyingUser"
            ).execute()
    
            # Get sheet ID and metadata
            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 sheet metadata
            sheet_metadata = sheets_service.spreadsheets().get(
                spreadsheetId=spreadsheet_id,
                ranges=[f"{sheet_name}"],
                includeGridData=include_formatting,
                fields="sheets(properties,merges,protectedRanges,basicFilter,conditionalFormats)"
            ).execute()
            
            sheet_props = sheet_metadata.get("sheets", [])[0].get("properties", {})
            
            # If table_name is not provided, get all tables in the sheet
            if not validated_table_name:
                return _get_all_tables_metadata(
                    sheets_service, 
                    spreadsheet_id, 
                    sheet_id, 
                    sheet_name, 
                    spreadsheet_name,
                    sheet_props,
                    spreadsheet_metadata,
                    include_sample_data,
                    include_formulas,
                    include_formatting,
                    include_statistics,
                    max_sample_rows,
                    specific_columns,
                    exclude_metadata_types
                )
            
            # Get specific table metadata
            # Get table ID and check for both displayName and name fields
            table_id = None
            result = sheets_service.spreadsheets().get(
                spreadsheetId=spreadsheet_id,
                fields="sheets.tables"
            ).execute()
            
            for sheet in result.get("sheets", []):
                tables = sheet.get("tables", [])
                for table in tables:
                    table_display_name = table.get("displayName")
                    table_name = table.get("name")
                    if validated_table_name in [table_display_name, table_name]:
                        table_id = table.get("tableId")
                        break
                if table_id:
                    break
            if not table_id:
                return compact_json_response({
                    "success": False,
                    "message": f"Table '{validated_table_name}' not found in sheet '{sheet_name}'."
                })
            
            try:
                # Get comprehensive table metadata
                table_metadata = get_table_info(sheets_service, spreadsheet_id, table_id)
                
                # Get table range for additional data
                start_col = table_metadata.get('start_col', 0)
                end_col = table_metadata.get('end_col', 0)
                start_row = table_metadata.get('start_row', 0)
                end_row = table_metadata.get('end_row', 0)
                table_range = f"{sheet_name}!{column_index_to_letter(start_col)}{start_row + 1}:{column_index_to_letter(end_col - 1)}{end_row}"
                
                # Get sample data if requested
                sample_data = None
                if include_sample_data:
                    data_response = sheets_service.spreadsheets().values().get(
                        spreadsheetId=spreadsheet_id,
                        range=table_range,
                        valueRenderOption="UNFORMATTED_VALUE" if not include_formulas else "FORMULA"
                    ).execute()
                    sample_data = data_response.get("values", [])[:max_sample_rows]
                
                # Get formatting if requested
                formatting_data = None
                if include_formatting:
                    format_response = sheets_service.spreadsheets().get(
                        spreadsheetId=spreadsheet_id,
                        ranges=[table_range],
                        fields="sheets(data(rowData(values(userEnteredFormat))))"
                    ).execute()
                    formatting_data = format_response.get("sheets", [])[0].get("data", [])[0].get("rowData", [])
                
                # Calculate statistics if requested
                statistics = None
                if include_statistics and sample_data:
                    statistics = _calculate_statistics(sample_data)
                
                # Filter columns if specific ones requested
                columns = table_metadata.get("columns", [])
                if specific_columns:
                    columns = [col for col in columns if col.get("name") in specific_columns]
                
                # Get frozen dimensions
                grid_properties = sheet_props.get("gridProperties", {})
                frozen_rows = grid_properties.get("frozenRowCount", 0)
                frozen_columns = grid_properties.get("frozenColumnCount", 0)
                
                # Get column dimensions
                column_metadata = sheet_props.get("columnMetadata", [])
                column_dimensions = []
                for i, col in enumerate(columns):
                    col_index = table_metadata.get("start_col", 0) + i
                    if col_index < len(column_metadata):
                        width = column_metadata[col_index].get("pixelSize")
                        column_dimensions.append({"index": i, "width": width})
                
                # Get merged ranges
                merges = sheet_metadata.get("sheets", [])[0].get("merges", [])
                table_merges = _filter_merges_for_table(merges, table_metadata)
                
                # Get conditional formatting
                conditional_formats = sheet_metadata.get("sheets", [])[0].get("conditionalFormats", [])
                table_conditional_formats = _filter_conditional_formats_for_table(conditional_formats, table_metadata)
                
                # Get filters
                basic_filter = sheet_metadata.get("sheets", [])[0].get("basicFilter", {})
                table_filters = _filter_basic_filter_for_table(basic_filter, table_metadata)
                
                # Format the response
                formatted_metadata = {
                    "table_name": validated_table_name,
                    "table_id": table_id,
                    "spreadsheet_name": spreadsheet_name,
                    "sheet_name": sheet_name,
                    "dimensions": {
                        "column_count": table_metadata.get("column_count"),
                        "row_count": table_metadata.get("row_count"),
                        "frozen_rows": frozen_rows,
                        "frozen_columns": frozen_columns
                    },
                    "range": {
                        "start_row": table_metadata.get("start_row"),
                        "end_row": table_metadata.get("end_row"),
                        "start_column": table_metadata.get("start_col"),
                        "end_column": table_metadata.get("end_col")
                    },
                    "range_notation": table_metadata.get("range_notation"),
                    "columns": columns,
                    "column_dimensions": column_dimensions,
                    "header_row": {
                        "index": table_metadata.get("start_row"),
                        "values": sample_data[0] if sample_data else None
                    },
                    "modification_info": {
                        "last_modified": spreadsheet_metadata.get("modifiedTime"),
                        "last_modified_by": spreadsheet_metadata.get("lastModifyingUser", {}).get("displayName")
                    }
                }
                
                # Add optional data based on parameters and exclusions
                if not exclude_metadata_types or "sample_data" not in exclude_metadata_types:
                    if include_sample_data:
                        formatted_metadata["sample_data"] = sample_data[1:] if sample_data else []
                
                if not exclude_metadata_types or "formatting" not in exclude_metadata_types:
                    if include_formatting:
                        formatted_metadata["formatting"] = formatting_data
                
                if not exclude_metadata_types or "statistics" not in exclude_metadata_types:
                    if include_statistics:
                        formatted_metadata["statistics"] = statistics
                
                if not exclude_metadata_types or "merges" not in exclude_metadata_types:
                    formatted_metadata["merges"] = table_merges
                
                if not exclude_metadata_types or "conditional_formatting" not in exclude_metadata_types:
                    formatted_metadata["conditional_formatting"] = table_conditional_formats
                
                if not exclude_metadata_types or "filters" not in exclude_metadata_types:
                    formatted_metadata["filters"] = table_filters
                
                response_data = {
                    "success": True,
                    "message": f"Successfully retrieved metadata for table '{validated_table_name}'",
                    "data": formatted_metadata
                }
                
                return compact_json_response(response_data)
                
            except RuntimeError as e:
                return compact_json_response({
                    "success": False,
                    "message": f"Could not retrieve metadata for table '{validated_table_name}': {str(e)}"
                })
            
        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 getting table metadata: {str(e)}"
            })
  • Pydantic-based input schema for tool parameters including descriptions and defaults.
    def get_table_metadata_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(default=None, description="Name of the table to get metadata for. If not provided, returns metadata for all tables in the sheet."),
        include_sample_data: bool = Field(default=False, description="Whether to include sample data rows"),
        include_formulas: bool = Field(default=False, description="Whether to include formula information"),
        include_formatting: bool = Field(default=False, description="Whether to include cell formatting details"),
        include_statistics: bool = Field(default=False, description="Whether to include data statistics"),
        max_sample_rows: int = Field(default=5, description="Maximum number of sample rows to return"),
        specific_columns: List[str] = Field(default=None, description="List of column names to get metadata for (optional)"),
        exclude_metadata_types: List[str] = Field(default=None, description="List of metadata types to exclude (e.g., ['sample_data', 'formatting', 'statistics', 'merges', 'conditional_formatting', 'filters'])")
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It describes the tool's behavior well: it's a read-only operation (implied by 'Get'), returns JSON, and handles optional parameters like table_name. However, it lacks details on error conditions, rate limits, authentication needs, or performance implications, which are important for a tool with 10 parameters. The description doesn't contradict any annotations since none exist.

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 and appropriately sized: it starts with a clear purpose statement, followed by parameter explanations and return information. Every sentence earns its place, but the Args section is somewhat redundant with the schema. It could be more concise by omitting the parameter list since the schema covers it, but overall it's efficient and front-loaded.

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?

Given the complexity (10 parameters, no annotations, but 100% schema coverage and an output schema), the description is mostly complete. It explains the tool's purpose, parameter behavior, and return format. The output schema existence means the description doesn't need to detail return values. However, it could improve by adding more behavioral context like error handling or usage examples, especially since no annotations are provided.

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 parameters thoroughly. The description adds minimal value beyond the schema: it lists parameters in the Args section but mostly repeats what's in the schema descriptions. It does clarify that table_name is optional and what happens when omitted, but this is also covered in the schema. Baseline 3 is appropriate as the schema does the heavy lifting.

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 verbs ('Get comprehensive metadata') and resources ('tables in Google Sheets'), distinguishing it from siblings like get_table_data_tool (which retrieves actual data) or analyze_sheet_structure_tool (which might analyze broader sheet properties). It explicitly mentions what metadata includes: structure, columns, data types, formatting, statistics, and other properties.

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

Usage Guidelines4/5

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

The description provides clear context for usage: it explains that if no table name is provided, it returns metadata for all tables, which helps the agent understand when to omit this parameter. However, it does not explicitly state when to use this tool versus alternatives like get_table_data_tool or analyze_sheet_structure_tool, nor does it mention any prerequisites or exclusions.

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