Skip to main content
Glama
hyunjae-labs

xlwings Excel MCP Server

create_chart

Create charts in Excel worksheets using specified data ranges and chart types, placing them at target cells with optional titles and axis labels.

Instructions

Create chart in worksheet.

Args:
    sheet_name: Name of worksheet
    data_range: Data range for chart
    chart_type: Type of chart
    target_cell: Cell where chart will be placed
    session_id: Session ID from open_workbook (preferred)
    filepath: Path to Excel file (legacy, deprecated)
    title: Chart title (optional)
    x_axis: X-axis label (optional)
    y_axis: Y-axis label (optional)
    
Note: Use session_id for better performance. filepath parameter is deprecated.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sheet_nameYes
data_rangeYes
chart_typeYes
target_cellYes
session_idNo
filepathNo
titleNo
x_axisNo
y_axisNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • MCP tool registration and handler for 'create_chart'. Dispatches to session-based or legacy filepath-based implementations.
    @mcp.tool()
    def create_chart(
        sheet_name: str,
        data_range: str,
        chart_type: str,
        target_cell: str,
        session_id: Optional[str] = None,
        filepath: Optional[str] = None,
        title: str = "",
        x_axis: str = "",
        y_axis: str = ""
    ) -> str:
        """
        Create chart in worksheet.
        
        Args:
            sheet_name: Name of worksheet
            data_range: Data range for chart
            chart_type: Type of chart
            target_cell: Cell where chart will be placed
            session_id: Session ID from open_workbook (preferred)
            filepath: Path to Excel file (legacy, deprecated)
            title: Chart title (optional)
            x_axis: X-axis label (optional)
            y_axis: Y-axis label (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.advanced_xlw import create_chart_xlw_with_wb
                    result = create_chart_xlw_with_wb(
                        session.workbook,
                        sheet_name=sheet_name,
                        data_range=data_range,
                        chart_type=chart_type,
                        target_cell=target_cell,
                        title=title,
                        x_axis=x_axis,
                        y_axis=y_axis
                    )
            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.advanced_xlw import create_chart_xlw
                result = create_chart_xlw(
                    filepath=full_path,
                    sheet_name=sheet_name,
                    data_range=data_range,
                    chart_type=chart_type,
                    target_cell=target_cell,
                    title=title,
                    x_axis=x_axis,
                    y_axis=y_axis
                )
            else:
                return ERROR_TEMPLATES['PARAMETER_MISSING'].format(
                    param1='session_id',
                    param2='filepath'
                )
            
            return result.get("message", "Chart created successfully") if "error" not in result else f"Error: {result['error']}"
            
        except (ValidationError, ChartError) as e:
            return f"Error: {str(e)}"
        except Exception as e:
            logger.error(f"Error creating chart: {e}")
            raise
  • Core implementation of chart creation for session-based (with existing workbook) usage. Called by the main handler when session_id is provided.
    def create_chart_xlw_with_wb(
        wb,
        sheet_name: str,
        data_range: str,
        chart_type: str,
        target_cell: str,
        title: str = "",
        x_axis: str = "",
        y_axis: str = ""
    ) -> Dict[str, Any]:
        """Session-based version using existing workbook object.
        
        Args:
            wb: Workbook object from session
            sheet_name: Name of worksheet
            data_range: Range of data for chart (e.g., "A1:C10")
            chart_type: Type of chart (line, bar, pie, scatter, area, column)
            target_cell: Cell where chart will be positioned
            title: Chart title
            x_axis: X-axis label
            y_axis: Y-axis label
            
        Returns:
            Dict with success message or error
        """
        try:
            logger.info(f"📈 Creating {chart_type} chart 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]
            
            # Map chart types to Excel constants (Microsoft XlChartType enumeration)
            chart_type_map = {
                'column': -4100,    # xlColumnClustered
                'bar': -4099,       # xlBarClustered
                'line': 4,          # xlLine
                'pie': 5,           # xlPie
                'area': 1,          # xlArea
                'scatter': -4169,   # xlXYScatter
                'doughnut': -4120,  # xlDoughnut
                'radar': -4151,     # xlRadarMarkers
            }
            
            if chart_type.lower() not in chart_type_map:
                available_types = ', '.join(chart_type_map.keys())
                return {"error": f"CHART_TYPE_ERROR: '{chart_type}' is not supported. Available types: {available_types}"}
            
            excel_chart_type = chart_type_map[chart_type.lower()]
            
            # Get data range first
            data_range_obj = sheet.range(data_range)
            
            # Create chart using xlwings method
            chart = sheet.charts.add()
            
            # Set data source
            chart.set_source_data(data_range_obj)
            
            # Set chart type - use xlwings chart_type property or COM API
            try:
                # First try xlwings native method (accepts string)
                if hasattr(chart, 'chart_type'):
                    try:
                        # xlwings accepts the string directly
                        chart.chart_type = chart_type.lower()
                        logger.info(f"Set chart type to {chart_type} using xlwings method")
                    except:
                        # If string doesn't work, try the constant
                        chart.chart_type = excel_chart_type
                        logger.info(f"Set chart type to {chart_type} using constant {excel_chart_type}")
                else:
                    # Fallback to COM API
                    chart_api = chart.api
                    if hasattr(chart_api, 'ChartType'):
                        chart_api.ChartType = excel_chart_type
                        logger.info(f"Set chart type to {chart_type} via COM API (constant: {excel_chart_type})")
                    else:
                        # Last resort - chart may already have correct type from creation
                        logger.warning(f"Could not explicitly set chart type, using default")
            except Exception as e:
                # Non-fatal: log but continue (chart may still work with default type)
                logger.warning(f"Chart type setting had issues but continuing: {e}")
            
            # Set chart position
            target = sheet.range(target_cell)
            chart.top = target.top
            chart.left = target.left
            
            # Calculate chart size based on data range
            data_rows = data_range_obj.rows.count
            data_cols = data_range_obj.columns.count
            
            # Dynamic sizing based on data
            chart.width = min(600, max(400, data_cols * 80))  # Adjust width based on columns
            chart.height = min(450, max(300, data_rows * 15))  # Adjust height based on rows
            
            # Set chart properties safely
            try:
                chart_com = chart.api
                
                # Set title
                if title and hasattr(chart_com, 'HasTitle'):
                    chart_com.HasTitle = True
                    if hasattr(chart_com, 'ChartTitle'):
                        chart_com.ChartTitle.Text = title
                
                # Set axis labels
                if hasattr(chart_com, 'Axes'):
                    try:
                        if x_axis:
                            x_axis_obj = chart_com.Axes(1)  # xlCategory
                            x_axis_obj.HasTitle = True
                            x_axis_obj.AxisTitle.Text = x_axis
                            
                        if y_axis:
                            y_axis_obj = chart_com.Axes(2)  # xlValue
                            y_axis_obj.HasTitle = True
                            y_axis_obj.AxisTitle.Text = y_axis
                    except Exception as e:
                        logger.warning(f"Axis label setting failed: {e}")
            except:
                # Some chart types don't have axes
                pass
            
            # Save the workbook
            wb.save()
            
            logger.info(f"✅ Successfully created {chart_type} chart")
            return {
                "message": f"Successfully created {chart_type} chart",
                "chart_type": chart_type,
                "data_range": data_range,
                "position": target_cell,
                "sheet": sheet_name
            }
            
        except Exception as e:
            logger.error(f"❌ Error creating chart: {str(e)}")
            return {"error": str(e)}
  • Legacy filepath-based chart creation implementation. Called when no session_id is provided.
    def create_chart_xlw(
        filepath: str,
        sheet_name: str,
        data_range: str,
        chart_type: str,
        target_cell: str,
        title: str = "",
        x_axis: str = "",
        y_axis: str = ""
    ) -> Dict[str, Any]:
        """
        Create a chart in Excel using xlwings.
        
        Args:
            filepath: Path to Excel file
            sheet_name: Name of worksheet
            data_range: Range of data for chart (e.g., "A1:C10")
            chart_type: Type of chart (line, bar, pie, scatter, area, column)
            target_cell: Cell where chart will be positioned
            title: Chart title
            x_axis: X-axis label
            y_axis: Y-axis label
            
        Returns:
            Dict with success message or error
        """
        app = None
        wb = None
    
        # Initialize COM for thread safety (Windows)
        _com_initialize()
    
        try:
            logger.info(f"Creating {chart_type} chart 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]
            
            # Map chart types to Excel constants
            chart_type_map = {
                'line': 4,          # xlLine
                'bar': 57,          # xlBarClustered
                'column': 51,       # xlColumnClustered
                'pie': 5,           # xlPie
                'scatter': 74,      # xlXYScatter
                'area': 1,          # xlArea
            }
            
            if chart_type.lower() not in chart_type_map:
                return {"error": f"Unsupported chart type: {chart_type}"}
            
            excel_chart_type = chart_type_map[chart_type.lower()]
            
            # Get data range first
            data_range_obj = sheet.range(data_range)
            
            # Create chart using xlwings method
            chart = sheet.charts.add()
            
            # Set data source
            chart.set_source_data(data_range_obj)
            
            # Set chart type - handle COM API properly
            try:
                if hasattr(chart, 'chart_type'):
                    # Use xlwings built-in chart type property
                    chart.chart_type = chart_type.lower()
                else:
                    # Use COM API more carefully
                    chart_api = chart.api
                    if hasattr(chart_api, 'ChartType'):
                        chart_api.ChartType = excel_chart_type
                    else:
                        logger.warning("Cannot set chart type - using default")
            except Exception as e:
                logger.warning(f"Chart type setting failed: {e}, using default")
            
            # Set chart position
            target = sheet.range(target_cell)
            chart.top = target.top
            chart.left = target.left
            
            # Calculate chart size based on data range
            data_rows = data_range_obj.rows.count
            data_cols = data_range_obj.columns.count
            
            # Dynamic sizing based on data
            chart.width = min(600, max(400, data_cols * 80))  # Adjust width based on columns
            chart.height = min(450, max(300, data_rows * 15))  # Adjust height based on rows
            
            # Set chart properties safely
            try:
                chart_com = chart.api
                
                # Set title
                if title and hasattr(chart_com, 'HasTitle'):
                    chart_com.HasTitle = True
                    if hasattr(chart_com, 'ChartTitle'):
                        chart_com.ChartTitle.Text = title
                
                # Set axis labels
                if hasattr(chart_com, 'Axes'):
                    try:
                        if x_axis:
                            x_axis_obj = chart_com.Axes(1)  # xlCategory
                            x_axis_obj.HasTitle = True
                            x_axis_obj.AxisTitle.Text = x_axis
                            
                        if y_axis:
                            y_axis_obj = chart_com.Axes(2)  # xlValue
                            y_axis_obj.HasTitle = True
                            y_axis_obj.AxisTitle.Text = y_axis
                    except Exception as e:
                        logger.warning(f"Axis label setting failed: {e}")
            except:
                # Some chart types don't have axes
                pass
            
            # Save the workbook
            wb.save()
            
            logger.info(f"✅ Successfully created {chart_type} chart")
            return {
                "message": f"Successfully created {chart_type} chart",
                "chart_type": chart_type,
                "data_range": data_range,
                "position": target_cell,
                "sheet": sheet_name
            }
            
        except Exception as e:
            logger.error(f"❌ Error creating chart: {str(e)}")
            return {"error": str(e)}
            
        finally:
            if wb:
                wb.close()
            if app:
                app.quit()
  • Import of the session-based helper functions into the advanced_xlw module.
    from .advanced_xlw_with_wb import (
        create_chart_xlw_with_wb,
        create_pivot_table_xlw_with_wb,
        create_table_xlw_with_wb
    )
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states this is a creation operation (implies mutation) and provides performance guidance about session_id versus filepath. However, it doesn't disclose important behavioral traits like whether this modifies the workbook permanently, what permissions are required, error conditions, or what the output contains. The description adds some context but leaves significant gaps.

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 a clear purpose statement followed by organized parameter documentation and a usage note. Every sentence earns its place. It could be slightly more front-loaded by moving the performance note earlier, but overall it's efficient and well-organized.

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 no annotations, 9 parameters, and an output schema exists, the description does an adequate job. The parameter documentation is excellent, and the output schema will handle return values. However, for a tool that modifies workbooks, the description should ideally mention more about behavioral implications, error handling, or prerequisites beyond just parameter guidance.

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

Parameters5/5

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

With 0% schema description coverage, the description fully compensates by providing a comprehensive parameter list with clear explanations for all 9 parameters. It distinguishes required vs optional parameters, provides guidance on deprecated parameters (filepath), and explains parameter relationships (session_id from open_workbook). This adds substantial value beyond the bare schema.

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

Purpose4/5

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

The description clearly states 'Create chart in worksheet' which specifies the verb (create) and resource (chart in worksheet). It distinguishes from siblings like create_table or create_pivot_table by focusing specifically on charts. However, it doesn't explicitly differentiate from other chart-related tools (none exist in siblings), so it's not a perfect 5.

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 guidance on parameter usage: 'Use session_id for better performance. filepath parameter is deprecated.' This gives explicit when-to-use recommendations for two parameters. However, it doesn't provide broader context about when to use this tool versus alternatives like create_table or create_pivot_table for data visualization needs.

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

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