Skip to main content
Glama
hyunjae-labs

xlwings Excel MCP Server

by hyunjae-labs

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
    )

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed5 schema fields changed
    • addedInput schema / properties / filepath / anyOf
      Added value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • addedInput schema / properties / filepath / default
      Added value: +null
    • removedInput schema / properties / filepath / type
      Removed value: -"string"
    • addedInput schema / properties / session_id
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "title": "Session Id"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "filepath",
      -  "sheet_name",
      -  "data_range",
      -  "chart_type",
      -  "target_cell"
      -]New value: +[
      +  "sheet_name",
      +  "data_range",
      +  "chart_type",
      +  "target_cell"
      +]
  2. First observed

TDQS

B3.1/5.0
Behavior2/5

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

No annotations are provided, so the description must disclose behavioral traits. It only says 'create chart' without mentioning side effects (e.g., overwriting existing charts), permission needs, or error states. This is insufficient for a creation tool.

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 succinct with a clear first sentence followed by a parameter list. No unnecessary information. It could be more structured (e.g., grouping optional params), but it's acceptably concise.

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?

Given 9 parameters and the presence of an output schema, the description is incomplete. It does not explain return values, prerequisites (e.g., workbook must be open), or error handling. The output schema exists but is unmentioned.

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 0%, so the description adds meaning by labeling each parameter (e.g., 'Name of worksheet'). However, the descriptions are very brief and lack validation details, types, or examples. Baseline 3 due to low coverage.

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' with a verb and resource. It distinguishes from siblings (no other chart creation tool) and lists parameters. However, it does not elaborate on different chart types or variations.

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

Usage Guidelines3/5

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

The description advises using session_id for better performance and notes filepath is deprecated. However, it provides no guidance on when to choose this tool over other tools (e.g., create_pivot_table, create_table) or context for chart creation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.