Skip to main content
Glama

format_range

Apply custom formatting to a specified range of cells in Excel, including font styles, colors, borders, alignment, and advanced options like conditional formatting and cell protection.

Instructions

Apply formatting to a range of cells.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
alignmentNo
bg_colorNo
boldNo
border_colorNo
border_styleNo
conditional_formatNo
end_cellNo
filepathYes
font_colorNo
font_sizeNo
italicNo
merge_cellsNo
number_formatNo
protectionNo
sheet_nameYes
start_cellYes
underlineNo
wrap_textNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • MCP tool registration and wrapper handler for 'format_range'. Uses @mcp.tool() decorator to register the tool and delegates to the core implementation in formatting.py.
    @mcp.tool()
    def format_range(
        filepath: str,
        sheet_name: str,
        start_cell: str,
        end_cell: Optional[str] = None,
        bold: bool = False,
        italic: bool = False,
        underline: bool = False,
        font_size: Optional[int] = None,
        font_color: Optional[str] = None,
        bg_color: Optional[str] = None,
        border_style: Optional[str] = None,
        border_color: Optional[str] = None,
        number_format: Optional[str] = None,
        alignment: Optional[str] = None,
        wrap_text: bool = False,
        merge_cells: bool = False,
        protection: Optional[Dict[str, Any]] = None,
        conditional_format: Optional[Dict[str, Any]] = None
    ) -> str:
        """Apply formatting to a range of cells."""
        try:
            full_path = get_excel_path(filepath)
            from excel_mcp.formatting import format_range as format_range_func
            
            # Convert None values to appropriate defaults for the underlying function
            format_range_func(
                filepath=full_path,
                sheet_name=sheet_name,
                start_cell=start_cell,
                end_cell=end_cell,  # This can be None
                bold=bold,
                italic=italic,
                underline=underline,
                font_size=font_size,  # This can be None
                font_color=font_color,  # This can be None
                bg_color=bg_color,  # This can be None
                border_style=border_style,  # This can be None
                border_color=border_color,  # This can be None
                number_format=number_format,  # This can be None
                alignment=alignment,  # This can be None
                wrap_text=wrap_text,
                merge_cells=merge_cells,
                protection=protection,  # This can be None
                conditional_format=conditional_format  # This can be None
            )
            return "Range formatted successfully"
        except (ValidationError, FormattingError) as e:
            return f"Error: {str(e)}"
        except Exception as e:
            logger.error(f"Error formatting range: {e}")
            raise
  • Core handler function implementing the formatting logic for cell ranges using openpyxl. Applies font, background, borders, alignment, number formats, merging, protection, and conditional formatting.
    def format_range(
        filepath: str,
        sheet_name: str,
        start_cell: str,
        end_cell: Optional[str] = None,
        bold: bool = False,
        italic: bool = False,
        underline: bool = False,
        font_size: Optional[int] = None,
        font_color: Optional[str] = None,
        bg_color: Optional[str] = None,
        border_style: Optional[str] = None,
        border_color: Optional[str] = None,
        number_format: Optional[str] = None,
        alignment: Optional[str] = None,
        wrap_text: bool = False,
        merge_cells: bool = False,
        protection: Optional[Dict[str, Any]] = None,
        conditional_format: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Apply formatting to a range of cells.
        
        This function handles all Excel formatting operations including:
        - Font properties (bold, italic, size, color, etc.)
        - Cell fill/background color
        - Borders (style and color)
        - Number formatting
        - Alignment and text wrapping
        - Cell merging
        - Protection
        - Conditional formatting
        
        Args:
            filepath: Path to Excel file
            sheet_name: Name of worksheet
            start_cell: Starting cell reference
            end_cell: Optional ending cell reference
            bold: Whether to make text bold
            italic: Whether to make text italic
            underline: Whether to underline text
            font_size: Font size in points
            font_color: Font color (hex code)
            bg_color: Background color (hex code)
            border_style: Border style (thin, medium, thick, double)
            border_color: Border color (hex code)
            number_format: Excel number format string
            alignment: Text alignment (left, center, right, justify)
            wrap_text: Whether to wrap text
            merge_cells: Whether to merge the range
            protection: Cell protection settings
            conditional_format: Conditional formatting rules
            
        Returns:
            Dictionary with operation status
        """
        try:
            # Validate cell references
            if not validate_cell_reference(start_cell):
                raise ValidationError(f"Invalid start cell reference: {start_cell}")
                
            if end_cell and not validate_cell_reference(end_cell):
                raise ValidationError(f"Invalid end cell reference: {end_cell}")
                
            wb = get_or_create_workbook(filepath)
            if sheet_name not in wb.sheetnames:
                raise ValidationError(f"Sheet '{sheet_name}' not found")
                
            sheet = wb[sheet_name]
            
            # Get cell range coordinates
            try:
                start_row, start_col, end_row, end_col = parse_cell_range(start_cell, end_cell)
            except ValueError as e:
                raise ValidationError(f"Invalid cell range: {str(e)}")
            
            # If no end cell specified, use start cell coordinates
            if end_row is None:
                end_row = start_row
            if end_col is None:
                end_col = start_col
                
            # Apply font formatting
            font_args = {
                "bold": bold,
                "italic": italic,
                "underline": 'single' if underline else None,
            }
            if font_size is not None:
                font_args["size"] = font_size
            if font_color is not None:
                try:
                    # Ensure color has FF prefix for full opacity
                    font_color = font_color if font_color.startswith('FF') else f'FF{font_color}'
                    font_args["color"] = Color(rgb=font_color)
                except ValueError as e:
                    raise FormattingError(f"Invalid font color: {str(e)}")
            font = Font(**font_args)
            
            # Apply fill
            fill = None
            if bg_color is not None:
                try:
                    # Ensure color has FF prefix for full opacity
                    bg_color = bg_color if bg_color.startswith('FF') else f'FF{bg_color}'
                    fill = PatternFill(
                        start_color=Color(rgb=bg_color),
                        end_color=Color(rgb=bg_color),
                        fill_type='solid'
                    )
                except ValueError as e:
                    raise FormattingError(f"Invalid background color: {str(e)}")
            
            # Apply borders
            border = None
            if border_style is not None:
                try:
                    border_color = border_color if border_color else "000000"
                    border_color = border_color if border_color.startswith('FF') else f'FF{border_color}'
                    side = Side(
                        style=border_style,
                        color=Color(rgb=border_color)
                    )
                    border = Border(
                        left=side,
                        right=side,
                        top=side,
                        bottom=side
                    )
                except ValueError as e:
                    raise FormattingError(f"Invalid border settings: {str(e)}")
                
            # Apply alignment
            align = None
            if alignment is not None or wrap_text:
                try:
                    align = Alignment(
                        horizontal=alignment,
                        vertical='center',
                        wrap_text=wrap_text
                    )
                except ValueError as e:
                    raise FormattingError(f"Invalid alignment settings: {str(e)}")
                
            # Apply protection
            protect = None
            if protection is not None:
                try:
                    protect = Protection(**protection)
                except ValueError as e:
                    raise FormattingError(f"Invalid protection settings: {str(e)}")
                
            # Apply formatting to range
            for row in range(start_row, end_row + 1):
                for col in range(start_col, end_col + 1):
                    cell = sheet.cell(row=row, column=col)
                    cell.font = font
                    if fill is not None:
                        cell.fill = fill
                    if border is not None:
                        cell.border = border
                    if align is not None:
                        cell.alignment = align
                    if protect is not None:
                        cell.protection = protect
                    if number_format is not None:
                        cell.number_format = number_format
                        
            # Merge cells if requested
            if merge_cells and end_cell:
                try:
                    range_str = f"{start_cell}:{end_cell}"
                    sheet.merge_cells(range_str)
                except ValueError as e:
                    raise FormattingError(f"Failed to merge cells: {str(e)}")
                
            # Apply conditional formatting
            if conditional_format is not None:
                range_str = f"{start_cell}:{end_cell}" if end_cell else start_cell
                rule_type = conditional_format.get('type')
                if not rule_type:
                    raise FormattingError("Conditional format type not specified")
                    
                params = conditional_format.get('params', {})
                
                # Handle fill parameter for cell_is rule
                if rule_type == 'cell_is' and 'fill' in params:
                    fill_params = params['fill']
                    if isinstance(fill_params, dict):
                        try:
                            fill_color = fill_params.get('fgColor', 'FFC7CE')  # Default to light red
                            fill_color = fill_color if fill_color.startswith('FF') else f'FF{fill_color}'
                            params['fill'] = PatternFill(
                                start_color=fill_color,
                                end_color=fill_color,
                                fill_type='solid'
                            )
                        except ValueError as e:
                            raise FormattingError(f"Invalid conditional format fill color: {str(e)}")
                
                try:
                    if rule_type == 'color_scale':
                        rule = ColorScaleRule(**params)
                    elif rule_type == 'data_bar':
                        rule = DataBarRule(**params)
                    elif rule_type == 'icon_set':
                        rule = IconSetRule(**params)
                    elif rule_type == 'formula':
                        rule = FormulaRule(**params)
                    elif rule_type == 'cell_is':
                        rule = CellIsRule(**params)
                    else:
                        raise FormattingError(f"Invalid conditional format type: {rule_type}")
                        
                    sheet.conditional_formatting.add(range_str, rule)
                except Exception as e:
                    raise FormattingError(f"Failed to apply conditional formatting: {str(e)}")
                
            wb.save(filepath)
            
            range_str = f"{start_cell}:{end_cell}" if end_cell else start_cell
            return {
                "message": f"Applied formatting to range {range_str}",
                "range": range_str
            }
            
        except (ValidationError, FormattingError) as e:
            logger.error(str(e))
            raise
        except Exception as e:
            logger.error(f"Failed to apply formatting: {e}")
            raise FormattingError(str(e))
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. 'Apply formatting' implies a write/mutation operation, but the description doesn't specify whether this overwrites existing formatting, requires file permissions, has side effects, or what happens on errors. It mentions nothing about the output schema that exists, leaving the agent to guess about return values or success indicators.

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 a single, efficient sentence that states the core function without unnecessary words. It's front-loaded with the essential action and target, making it immediately understandable at a basic level. Every word earns its place in conveying the fundamental purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with 18 parameters, 0% schema description coverage, no annotations, but with an output schema, the description is severely inadequate. It covers only the most basic purpose while ignoring parameter explanations, behavioral context, usage guidance, and how it relates to sibling tools. The existence of an output schema reduces the need to describe return values, but other gaps remain substantial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 18 parameters with 0% description coverage, and the tool description provides absolutely no information about any parameters. It doesn't mention required parameters like 'filepath', 'sheet_name', and 'start_cell', nor does it explain what formatting options are available or how they work. The description fails completely to compensate for the schema's lack of parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Apply formatting to a range of cells' clearly states the action (apply formatting) and target (range of cells), which is specific enough to understand the basic function. However, it doesn't distinguish this tool from potential formatting-related siblings like 'apply_formula' or 'merge_cells', nor does it specify what types of formatting are available beyond the general concept.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. There are no mentions of prerequisites, constraints, or comparisons to sibling tools like 'apply_formula' or 'merge_cells' that might handle overlapping functionality. The agent must infer usage solely from the tool name and parameters.

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

Related 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/haris-musa/excel-mcp-server'

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