Skip to main content
Glama

create_chart

Generate charts in Excel worksheets by specifying data range, chart type, and target cell. Use this tool to visualize data with customizable titles and axis labels directly within Excel files.

Instructions

Create chart in worksheet.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chart_typeYes
data_rangeYes
filepathYes
sheet_nameYes
target_cellYes
titleNo
x_axisNo
y_axisNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • MCP tool handler for 'create_chart'. Registers the tool with FastMCP and handles input parameters, path resolution, and delegates execution to the implementation while catching specific exceptions.
    @mcp.tool()
    def create_chart(
        filepath: str,
        sheet_name: str,
        data_range: str,
        chart_type: str,
        target_cell: str,
        title: str = "",
        x_axis: str = "",
        y_axis: str = ""
    ) -> str:
        """Create chart in worksheet."""
        try:
            full_path = get_excel_path(filepath)
            result = create_chart_impl(
                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
            )
            return result["message"]
        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. Loads the workbook, parses data range, creates the specified chart type (line, bar, pie, etc.) using openpyxl, configures titles, axes, data series, styles, and anchors the chart at the target cell.
    def create_chart_in_sheet(
        filepath: str,
        sheet_name: str,
        data_range: str,
        chart_type: str,
        target_cell: str,
        title: str = "",
        x_axis: str = "",
        y_axis: str = "",
        style: Optional[Dict] = None
    ) -> dict[str, Any]:
        """Create chart in sheet with enhanced styling options"""
        # Ensure style dict exists and defaults to showing data labels
        if style is None:
            style = {"show_data_labels": True}
        else:
            # If caller omitted the flag, default to True
            style.setdefault("show_data_labels", True)
        try:
            wb = load_workbook(filepath)
            if sheet_name not in wb.sheetnames:
                logger.error(f"Sheet '{sheet_name}' not found")
                raise ValidationError(f"Sheet '{sheet_name}' not found")
    
            worksheet = wb[sheet_name]
    
            # Initialize collections if they don't exist
            if not hasattr(worksheet, '_drawings'):
                worksheet._drawings = []
            if not hasattr(worksheet, '_charts'):
                worksheet._charts = []
    
            # Parse the data range
            if "!" in data_range:
                range_sheet_name, cell_range = data_range.split("!")
                if range_sheet_name not in wb.sheetnames:
                    logger.error(f"Sheet '{range_sheet_name}' referenced in data range not found")
                    raise ValidationError(f"Sheet '{range_sheet_name}' referenced in data range not found")
            else:
                cell_range = data_range
    
            try:
                start_cell, end_cell = cell_range.split(":")
                start_row, start_col, end_row, end_col = parse_cell_range(start_cell, end_cell)
            except ValueError as e:
                logger.error(f"Invalid data range format: {e}")
                raise ValidationError(f"Invalid data range format: {str(e)}")
    
            # Validate chart type
            chart_classes = {
                "line": LineChart,
                "bar": BarChart,
                "pie": PieChart,
                "scatter": ScatterChart,
                "area": AreaChart
            }
            
            chart_type_lower = chart_type.lower()
            ChartClass = chart_classes.get(chart_type_lower)
            if not ChartClass:
                logger.error(f"Unsupported chart type: {chart_type}")
                raise ValidationError(
                    f"Unsupported chart type: {chart_type}. "
                    f"Supported types: {', '.join(chart_classes.keys())}"
                )
                
            chart = ChartClass()
            
            # Basic chart settings
            chart.title = title
            if hasattr(chart, "x_axis"):
                chart.x_axis.title = x_axis
            if hasattr(chart, "y_axis"):
                chart.y_axis.title = y_axis
    
            try:
                # Create data references
                if chart_type_lower == "scatter":
                    # For scatter charts, create series for each pair of columns
                    for col in range(start_col + 1, end_col + 1):
                        x_values = Reference(
                            worksheet,
                            min_row=start_row + 1,
                            max_row=end_row,
                            min_col=start_col
                        )
                        y_values = Reference(
                            worksheet,
                            min_row=start_row + 1,
                            max_row=end_row,
                            min_col=col
                        )
                        series = Series(y_values, x_values, title_from_data=True)
                        chart.series.append(series)
                else:
                    # For other chart types
                    data = Reference(
                        worksheet,
                        min_row=start_row,
                        max_row=end_row,
                        min_col=start_col + 1,
                        max_col=end_col
                    )
                    cats = Reference(
                        worksheet,
                        min_row=start_row + 1,
                        max_row=end_row,
                        min_col=start_col
                    )
                    chart.add_data(data, titles_from_data=True)
                    chart.set_categories(cats)
            except Exception as e:
                logger.error(f"Failed to create chart data references: {e}")
                raise ChartError(f"Failed to create chart data references: {str(e)}")
    
            # Apply style if provided
            try:
                if style.get("show_legend", True):
                    chart.legend = Legend()
                    chart.legend.position = style.get("legend_position", "r")
                else:
                    chart.legend = None
    
                if style.get("show_data_labels", False):
                    data_labels = DataLabelList()
                    # Gather optional overrides
                    dlo = style.get("data_label_options", {}) if isinstance(style.get("data_label_options", {}), dict) else {}
    
                    # Helper to read bool with fallback
                    def _opt(name: str, default: bool) -> bool:
                        return bool(dlo.get(name, default))
    
                    # Apply options – Excel will concatenate any that are set to True
                    data_labels.showVal = _opt("show_val", True)
                    data_labels.showCatName = _opt("show_cat_name", False)
                    data_labels.showSerName = _opt("show_ser_name", False)
                    data_labels.showLegendKey = _opt("show_legend_key", False)
                    data_labels.showPercent = _opt("show_percent", False)
                    data_labels.showBubbleSize = _opt("show_bubble_size", False)
    
                    chart.dataLabels = data_labels
    
                if style.get("grid_lines", False):
                    if hasattr(chart, "x_axis"):
                        chart.x_axis.majorGridlines = ChartLines()
                    if hasattr(chart, "y_axis"):
                        chart.y_axis.majorGridlines = ChartLines()
            except Exception as e:
                logger.error(f"Failed to apply chart style: {e}")
                raise ChartError(f"Failed to apply chart style: {str(e)}")
    
            # Set chart size
            chart.width = 15
            chart.height = 7.5
    
            # Create drawing and anchor
            try:
                drawing = SpreadsheetDrawing()
                drawing.chart = chart
    
                # Validate target cell format
                if not target_cell or not any(c.isalpha() for c in target_cell) or not any(c.isdigit() for c in target_cell):
                    raise ValidationError(f"Invalid target cell format: {target_cell}")
    
                # Create anchor
                col = column_index_from_string(target_cell[0]) - 1
                row = int(target_cell[1:]) - 1
                anchor = OneCellAnchor()
                anchor._from = AnchorMarker(col=col, row=row)
                drawing.anchor = anchor
    
                # Add to worksheet
                worksheet._drawings.append(drawing)
                worksheet._charts.append(chart)
            except ValueError as e:
                logger.error(f"Invalid target cell: {e}")
                raise ValidationError(f"Invalid target cell: {str(e)}")
            except Exception as e:
                logger.error(f"Failed to create chart drawing: {e}")
                raise ChartError(f"Failed to create chart drawing: {str(e)}")
    
            try:
                wb.save(filepath)
            except Exception as e:
                logger.error(f"Failed to save workbook: {e}")
                raise ChartError(f"Failed to save workbook with chart: {str(e)}")
    
            return {
                "message": f"{chart_type.capitalize()} chart created successfully",
                "details": {
                    "type": chart_type,
                    "location": target_cell,
                    "data_range": data_range
                }
            }
            
        except (ValidationError, ChartError):
            raise
        except Exception as e:
            logger.error(f"Unexpected error creating chart: {e}")
            raise ChartError(f"Unexpected error creating chart: {str(e)}")
  • Enum defining valid chart types for input validation in the create_chart tool.
    class ChartType(str, Enum):
        """Supported chart types"""
        LINE = "line"
        BAR = "bar"
        PIE = "pie"
        SCATTER = "scatter"
        AREA = "area"
        BUBBLE = "bubble"
        STOCK = "stock"
        SURFACE = "surface"
        RADAR = "radar"
  • Import of the chart implementation function aliased for use in the handler.
    from excel_mcp.chart import create_chart_in_sheet as create_chart_impl
Behavior2/5

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

No annotations are provided, so the description carries full burden. It states 'Create chart' which implies a write/mutation operation, but doesn't disclose behavioral traits like whether it overwrites existing charts, requires specific permissions, or has side effects. The description is minimal and lacks crucial behavioral context for a mutation tool.

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 extremely concise with just one sentence ('Create chart in worksheet.'). It's front-loaded and wastes no words, though this brevity contributes to its inadequacy in other dimensions. Every word earns its place, even if insufficient overall.

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 high complexity (8 parameters, mutation tool) with no annotations and 0% schema description coverage, the description is incomplete. While an output schema exists (which helps with return values), the description doesn't address the tool's behavior, parameter meanings, or usage context, leaving significant gaps for agent understanding.

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

Parameters2/5

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

Schema description coverage is 0%, so the description must compensate. It mentions no parameters at all, failing to add meaning beyond the schema. With 8 parameters (5 required), this is a significant gap as users wouldn't know what inputs are needed or their purposes from the description alone.

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 states the basic action ('Create chart in worksheet') which is clear but vague. It specifies the resource (chart) and location (worksheet) but lacks specificity about what kind of chart or how it differs from sibling tools like create_table or create_pivot_table. It's not tautological but doesn't provide enough differentiation.

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?

No guidance on when to use this tool versus alternatives is provided. The description doesn't mention prerequisites, context, or comparisons to sibling tools like create_table or create_pivot_table. It's a bare statement without usage context.

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