Skip to main content
Glama
hyunjae-labs

xlwings Excel MCP Server

by hyunjae-labs

format_range

Apply formatting to Excel cell ranges including font styles, colors, borders, alignment, and conditional formatting to enhance spreadsheet readability and presentation.

Instructions

Apply formatting to a range of cells.

Args:
    sheet_name: Name of worksheet
    start_cell: Starting cell
    session_id: Session ID from open_workbook (preferred)
    filepath: Path to Excel file (legacy, deprecated)
    end_cell: Ending cell (optional)
    bold: Apply bold formatting
    italic: Apply italic formatting
    underline: Apply underline formatting
    font_size: Font size
    font_color: Font color
    bg_color: Background color
    border_style: Border style
    border_color: Border color
    number_format: Number format
    alignment: Text alignment
    wrap_text: Enable text wrapping
    merge_cells: Merge cells in range
    protection: Cell protection settings (optional)
    conditional_format: Conditional formatting settings (optional)
    
Note: Use session_id for better performance. filepath parameter is deprecated.

Input Schema

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

Implementation Reference

  • MCP tool registration and primary handler for 'format_range'. Dispatches to session-based or legacy filepath-based implementations based on parameters provided.
    @mcp.tool()
    def format_range(
        sheet_name: str,
        start_cell: str,
        session_id: Optional[str] = None,
        filepath: Optional[str] = None,
        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.
        
        Args:
            sheet_name: Name of worksheet
            start_cell: Starting cell
            session_id: Session ID from open_workbook (preferred)
            filepath: Path to Excel file (legacy, deprecated)
            end_cell: Ending cell (optional)
            bold: Apply bold formatting
            italic: Apply italic formatting
            underline: Apply underline formatting
            font_size: Font size
            font_color: Font color
            bg_color: Background color
            border_style: Border style
            border_color: Border color
            number_format: Number format
            alignment: Text alignment
            wrap_text: Enable text wrapping
            merge_cells: Merge cells in range
            protection: Cell protection settings (optional)
            conditional_format: Conditional formatting settings (optional)
            
        Note: Use session_id for better performance. filepath parameter is deprecated.
        """
        try:
            # Support both new (session_id) and old (filepath) API
            if session_id:
                # New API: use session
                session = SESSION_MANAGER.get_session(session_id)
                if not session:
                    return ERROR_TEMPLATES['SESSION_NOT_FOUND'].format(
                        session_id=session_id, 
                        ttl=10  # Default TTL is 10 minutes (600 seconds)
                    )
                
                with session.lock:
                    from xlwings_mcp.xlwings_impl.formatting_xlw import format_range_xlw_with_wb
                    result = format_range_xlw_with_wb(
                        session.workbook,
                        sheet_name=sheet_name,
                        start_cell=start_cell,
                        end_cell=end_cell,
                        bold=bold,
                        italic=italic,
                        underline=underline,
                        font_size=font_size,
                        font_color=font_color,
                        bg_color=bg_color,
                        border_style=border_style,
                        border_color=border_color,
                        number_format=number_format,
                        alignment=alignment,
                        wrap_text=wrap_text,
                        merge_cells=merge_cells
                    )
            elif filepath:
                # Legacy API: backwards compatibility
                logger.warning("Using deprecated filepath parameter. Please use session_id instead.")
                full_path = get_excel_path(filepath)
                from xlwings_mcp.xlwings_impl.formatting_xlw import format_range_xlw
                result = format_range_xlw(
                    filepath=full_path,
                    sheet_name=sheet_name,
                    start_cell=start_cell,
                    end_cell=end_cell,
                    bold=bold,
                    italic=italic,
                    underline=underline,
                    font_size=font_size,
                    font_color=font_color,
                    bg_color=bg_color,
                    border_style=border_style,
                    border_color=border_color,
                    number_format=number_format,
                    alignment=alignment,
                    wrap_text=wrap_text,
                    merge_cells=merge_cells
                )
            else:
                return ERROR_TEMPLATES['PARAMETER_MISSING'].format(
                    param1='session_id',
                    param2='filepath'
                )
            
            return result.get("message", "Range formatted successfully") if "error" not in result else f"Error: {result['error']}"
        except (ValidationError, FormattingError) as e:
            return f"Error: {str(e)}"
        except Exception as e:
            logger.error(f"Error formatting range: {e}")
            raise
  • Core implementation of range formatting using xlwings for legacy filepath mode. Applies font, color, borders, alignment, etc. to specified range.
    def format_range_xlw(
        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
    ) -> Dict[str, Any]:
        """
        Apply formatting to a range of cells using xlwings.
        
        Args:
            filepath: Path to Excel file
            sheet_name: Name of worksheet
            start_cell: Starting cell for formatting
            end_cell: Ending cell for formatting (optional, defaults to start_cell)
            bold: Apply bold formatting
            italic: Apply italic formatting
            underline: Apply underline formatting
            font_size: Font size in points
            font_color: Font color (hex code or color name)
            bg_color: Background color (hex code or color name)
            border_style: Border style (thin, medium, thick, double)
            border_color: Border color (hex code or color name)
            number_format: Number format string (e.g., "0.00", "#,##0", "mm/dd/yyyy")
            alignment: Text alignment (left, center, right, justify)
            wrap_text: Enable text wrapping
            merge_cells: Merge the cell range
            
        Returns:
            Dict with success message or error
        """
        app = None
        wb = None
    
        # Initialize COM for thread safety (Windows)
        _com_initialize()
    
        try:
            logger.info(f"Applying formatting to range {start_cell}:{end_cell or start_cell} in {sheet_name}")
            
            # Check if file exists
            if not os.path.exists(filepath):
                return {"error": f"File not found: {filepath}"}
            
            # Open Excel app and workbook
            app = xw.App(visible=False, add_book=False)
            wb = app.books.open(filepath)
            
            # Check if sheet exists
            sheet_names = [s.name for s in wb.sheets]
            if sheet_name not in sheet_names:
                return {"error": f"Sheet '{sheet_name}' not found"}
            
            sheet = wb.sheets[sheet_name]
            
            # Get the range to format
            if end_cell:
                range_obj = sheet.range(f"{start_cell}:{end_cell}")
            else:
                range_obj = sheet.range(start_cell)
            
            # Apply font formatting
            if bold:
                range_obj.font.bold = True
            if italic:
                range_obj.font.italic = True
            if underline:
                range_obj.font.underline = True
            if font_size:
                range_obj.font.size = font_size
            
            # Apply font color
            if font_color:
                try:
                    rgb = parse_color(font_color)
                    range_obj.font.color = rgb
                    logger.info(f"Applied font color: {font_color} -> RGB{rgb}")
                except ValueError as e:
                    return {"error": str(e)}
            
            # Apply background color
            if bg_color:
                try:
                    rgb = parse_color(bg_color)
                    range_obj.color = rgb
                    logger.info(f"Applied background color: {bg_color} -> RGB{rgb}")
                except ValueError as e:
                    return {"error": str(e)}
            
            # Apply borders using COM API
            if border_style:
                range_com = range_obj.api
                
                # Map border styles to Excel constants
                border_map = {
                    'thin': 1,      # xlThin
                    'medium': -4138, # xlMedium
                    'thick': 4,     # xlThick
                    'double': -4119, # xlDouble
                    'dotted': -4118, # xlDot
                    'dashed': -4115  # xlDash
                }
                
                style_constant = border_map.get(border_style.lower(), 1)
                
                # Apply to all borders
                for border_index in [7, 8, 9, 10]:  # xlEdgeLeft, xlEdgeTop, xlEdgeBottom, xlEdgeRight
                    border = range_com.Borders(border_index)
                    border.LineStyle = style_constant
                    
                    if border_color:
                        try:
                            if border_color.startswith('#'):
                                hex_color = border_color.lstrip('#')
                                rgb_val = int(hex_color[:2], 16) + (int(hex_color[2:4], 16) << 8) + (int(hex_color[4:6], 16) << 16)
                                border.Color = rgb_val
                        except:
                            pass
            
            # Apply number format
            if number_format:
                range_obj.number_format = number_format
            
            # Apply alignment
            if alignment:
                alignment_map = {
                    'left': -4131,    # xlLeft
                    'center': -4108,  # xlCenter
                    'right': -4152,   # xlRight
                    'justify': -4130  # xlJustify
                }
                
                if alignment.lower() in alignment_map:
                    range_obj.api.HorizontalAlignment = alignment_map[alignment.lower()]
            
            # Apply text wrapping
            if wrap_text:
                range_obj.api.WrapText = True
            
            # Merge cells if requested
            if merge_cells:
                range_obj.merge()
            
            # Save the workbook
            wb.save()
            
            logger.info(f"βœ… Successfully applied formatting to range")
            return {
                "message": f"Successfully applied formatting to range {start_cell}:{end_cell or start_cell}",
                "range": f"{start_cell}:{end_cell or start_cell}",
                "sheet": sheet_name,
                "formatting_applied": {
                    "bold": bold,
                    "italic": italic,
                    "underline": underline,
                    "font_size": font_size,
                    "font_color": font_color,
                    "bg_color": bg_color,
                    "border_style": border_style,
                    "number_format": number_format,
                    "alignment": alignment,
                    "wrap_text": wrap_text,
                    "merged": merge_cells
                }
            }
            
        except Exception as e:
            logger.error(f"❌ Error applying formatting: {str(e)}")
            return {"error": str(e)}
            
        finally:
            if wb:
                wb.close()
            if app:
                app.quit()
  • Core implementation of range formatting using xlwings for session (workbook object) mode. Identical logic to legacy but takes existing workbook.
    def format_range_xlw_with_wb(
        wb,
        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
    ) -> Dict[str, Any]:
        """
        Session-based range formatting using existing workbook object.
        
        Args:
            wb: Workbook object from session
            sheet_name: Name of worksheet
            start_cell: Starting cell for formatting
            end_cell: Ending cell for formatting (optional, defaults to start_cell)
            bold: Apply bold formatting
            italic: Apply italic formatting
            underline: Apply underline formatting
            font_size: Font size in points
            font_color: Font color (hex code or color name)
            bg_color: Background color (hex code or color name)
            border_style: Border style (thin, medium, thick, double)
            border_color: Border color (hex code or color name)
            number_format: Number format string (e.g., "0.00", "#,##0", "mm/dd/yyyy")
            alignment: Text alignment (left, center, right, justify)
            wrap_text: Enable text wrapping
            merge_cells: Merge the cell range
            
        Returns:
            Dict with success message or error
        """
        try:
            logger.info(f"🎨 Applying formatting to range {start_cell}:{end_cell or start_cell} in {sheet_name}")
            
            # Check if sheet exists
            sheet_names = [s.name for s in wb.sheets]
            if sheet_name not in sheet_names:
                return {"error": f"Sheet '{sheet_name}' not found"}
            
            sheet = wb.sheets[sheet_name]
            
            # Get the range to format
            if end_cell:
                range_obj = sheet.range(f"{start_cell}:{end_cell}")
            else:
                range_obj = sheet.range(start_cell)
            
            # Apply font formatting
            if bold:
                range_obj.font.bold = True
            if italic:
                range_obj.font.italic = True
            if underline:
                range_obj.font.underline = True
            if font_size:
                range_obj.font.size = font_size
            
            # Apply font color
            if font_color:
                try:
                    rgb = parse_color(font_color)
                    range_obj.font.color = rgb
                    logger.info(f"Applied font color: {font_color} -> RGB{rgb}")
                except ValueError as e:
                    return {"error": str(e)}
            
            # Apply background color
            if bg_color:
                try:
                    rgb = parse_color(bg_color)
                    range_obj.color = rgb
                    logger.info(f"Applied background color: {bg_color} -> RGB{rgb}")
                except ValueError as e:
                    return {"error": str(e)}
            
            # Apply borders using COM API
            if border_style:
                range_com = range_obj.api
                
                # Map border styles to Excel constants
                border_map = {
                    'thin': 1,      # xlThin
                    'medium': -4138, # xlMedium
                    'thick': 4,     # xlThick
                    'double': -4119, # xlDouble
                    'dotted': -4118, # xlDot
                    'dashed': -4115  # xlDash
                }
                
                style_constant = border_map.get(border_style.lower(), 1)
                
                # Apply to all borders
                for border_index in [7, 8, 9, 10]:  # xlEdgeLeft, xlEdgeTop, xlEdgeBottom, xlEdgeRight
                    border = range_com.Borders(border_index)
                    border.LineStyle = style_constant
                    
                    if border_color:
                        try:
                            if border_color.startswith('#'):
                                hex_color = border_color.lstrip('#')
                                rgb_val = int(hex_color[:2], 16) + (int(hex_color[2:4], 16) << 8) + (int(hex_color[4:6], 16) << 16)
                                border.Color = rgb_val
                        except:
                            pass
            
            # Apply number format
            if number_format:
                range_obj.number_format = number_format
            
            # Apply alignment
            if alignment:
                alignment_map = {
                    'left': -4131,    # xlLeft
                    'center': -4108,  # xlCenter
                    'right': -4152,   # xlRight
                    'justify': -4130  # xlJustify
                }
                
                if alignment.lower() in alignment_map:
                    range_obj.api.HorizontalAlignment = alignment_map[alignment.lower()]
            
            # Apply text wrapping
            if wrap_text:
                range_obj.api.WrapText = True
            
            # Merge cells if requested
            if merge_cells:
                range_obj.merge()
            
            # Save the workbook
            wb.save()
            
            logger.info(f"βœ… Successfully applied formatting to range")
            return {
                "message": f"Successfully applied formatting to range {start_cell}:{end_cell or start_cell}",
                "range": f"{start_cell}:{end_cell or start_cell}",
                "sheet": sheet_name,
                "formatting_applied": {
                    "bold": bold,
                    "italic": italic,
                    "underline": underline,
                    "font_size": font_size,
                    "font_color": font_color,
                    "bg_color": bg_color,
                    "border_style": border_style,
                    "number_format": number_format,
                    "alignment": alignment,
                    "wrap_text": wrap_text,
                    "merged": merge_cells
                }
            }
            
        except Exception as e:
            logger.error(f"❌ Error applying formatting: {str(e)}")
            return {"error": str(e)}
  • Utility function to parse various color formats (hex, standard names, RGB strings) into RGB tuples used by both format_range handlers.
    def parse_color(color_input: str) -> Tuple[int, int, int]:
        """
        Parse color input to RGB tuple.
        
        Args:
            color_input: Color as hex (#RRGGBB), name (yellow), or RGB string
            
        Returns:
            RGB tuple (r, g, b)
            
        Raises:
            ValueError: If color format is invalid
        """
        if not color_input:
            raise ValueError("Color input is empty")
        
        # Handle hex colors
        if color_input.startswith('#'):
            hex_color = color_input.lstrip('#')
            if len(hex_color) != 6:
                raise ValueError(f"Invalid hex color format: {color_input}. Use #RRGGBB")
            try:
                return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
            except ValueError:
                raise ValueError(f"Invalid hex color values: {color_input}")
        
        # Handle standard color names
        color_lower = color_input.lower()
        if color_lower in STANDARD_COLORS:
            return STANDARD_COLORS[color_lower]
        
        # Try to parse as RGB tuple string "(r,g,b)"
        if ',' in color_input:
            try:
                # Remove parentheses and spaces
                clean = color_input.strip('() ')
                parts = [int(x.strip()) for x in clean.split(',')]
                if len(parts) == 3 and all(0 <= p <= 255 for p in parts):
                    return tuple(parts)
            except:
                pass
        
        # If nothing worked, provide helpful error
        available_colors = ', '.join(STANDARD_COLORS.keys())
        raise ValueError(
            f"COLOR_FORMAT_ERROR: '{color_input}' is not recognized. "
            f"Use hex format (#RRGGBB) or standard colors: {available_colors}"
        )
  • Input schema and documentation for the format_range tool, defining all parameters, their types, and descriptions.
    """
    Apply formatting to a range of cells.
    
    Args:
        sheet_name: Name of worksheet
        start_cell: Starting cell
        session_id: Session ID from open_workbook (preferred)
        filepath: Path to Excel file (legacy, deprecated)
        end_cell: Ending cell (optional)
        bold: Apply bold formatting
        italic: Apply italic formatting
        underline: Apply underline formatting
        font_size: Font size
        font_color: Font color
        bg_color: Background color
        border_style: Border style
        border_color: Border color
        number_format: Number format
        alignment: Text alignment
        wrap_text: Enable text wrapping
        merge_cells: Merge cells in range
        protection: Cell protection settings (optional)
        conditional_format: Conditional formatting settings (optional)
        
    Note: Use session_id for better performance. filepath parameter is deprecated.
    """

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/hyunjae-labs/xlwings-mcp-server'

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