Skip to main content
Glama

generate_boxplot_chart

Generate a boxplot chart to display statistical summaries across categories. Input data with categories and values, optionally grouped. Customize titles, dimensions, theme, and export as PNG, SVG, or ECharts option.

Instructions

Generate a boxplot chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
axisXTitleNoSet the x-axis title of chart.
axisYTitleNoSet the y-axis title of chart.
dataYesData for boxplot chart, such as, [{ category: 'Category A', value: 10 }, { category: 'Category B', value: 20, group: 'Group A' }].
heightNoSet the height of the chart, default is 600px.
themeNoSet the theme for the chart, optional, default is 'default'.default
titleNoSet the title of the chart.
widthNoSet the width of the chart, default is 800px.
outputTypeNoThe output type of the diagram. Can be 'png', 'svg' or 'option'. Default is 'png', 'png' will return the rendered PNG image, 'svg' will return the rendered SVG string, and 'option' will return the valid ECharts option.png

Implementation Reference

  • The main tool definition and handler function for generate_boxplot_chart. Contains the `run` async function that processes input data, computes boxplot statistics (min, Q1, median, Q3, max) using the inline `calculateBoxplotStats` helper, builds ECharts boxplot series options, and calls `generateChartImage` to produce the output.
    export const generateBoxplotChartTool = {
      name: "generate_boxplot_chart",
      description:
        "Generate a boxplot chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.",
      inputSchema: z.object({
        axisXTitle: AxisXTitleSchema,
        axisYTitle: AxisYTitleSchema,
        data: z
          .array(data)
          .describe(
            "Data for boxplot chart, such as, [{ category: 'Category A', value: 10 }, { category: 'Category B', value: 20, group: 'Group A' }].",
          )
          .nonempty({ message: "Boxplot chart data cannot be empty." }),
        height: HeightSchema,
        theme: ThemeSchema,
        title: TitleSchema,
        width: WidthSchema,
        outputType: OutputTypeSchema,
      }),
      run: async (params: {
        axisXTitle?: string;
        axisYTitle?: string;
        data: Array<{ category: string; value: number; group?: string }>;
        height: number;
        theme?: "default" | "dark";
        title?: string;
        width: number;
        outputType?: "png" | "svg" | "option";
      }) => {
        const {
          axisXTitle,
          axisYTitle,
          data,
          height,
          theme,
          title,
          width,
          outputType,
        } = params;
    
        // Group data by category and optional group
        const hasGroups = data.some((item) => item.group);
    
        let categories: string[] = [];
        const boxplotData: { name: string; value: number[] }[] = [];
    
        if (hasGroups) {
          // Group by category and group
          const groupMap = new Map<
            string,
            Array<{ category: string; value: number }>
          >();
          const categorySet = new Set<string>();
    
          for (const item of data) {
            const groupName = item.group || "Default";
            const key = `${item.category}_${groupName}`;
            if (!groupMap.has(key)) {
              groupMap.set(key, []);
            }
            groupMap.get(key)?.push(item);
            categorySet.add(item.category);
          }
    
          categories = Array.from(categorySet).sort();
    
          // Calculate boxplot statistics for each group
          groupMap.forEach((values, key) => {
            const [category, group] = key.split("_");
            const sortedValues = values.map((v) => v.value).sort((a, b) => a - b);
            const boxplotStats = calculateBoxplotStats(sortedValues);
            boxplotData.push({
              name: `${category}-${group}`,
              value: boxplotStats,
            });
          });
        } else {
          // Group by category only
          const categoryMap = new Map<string, number[]>();
    
          for (const item of data) {
            if (!categoryMap.has(item.category)) {
              categoryMap.set(item.category, []);
            }
            categoryMap.get(item.category)?.push(item.value);
          }
    
          categories = Array.from(categoryMap.keys()).sort();
    
          // Calculate boxplot statistics for each category
          for (const category of categories) {
            const values = categoryMap.get(category)?.sort((a, b) => a - b) || [];
            const boxplotStats = calculateBoxplotStats(values);
            boxplotData.push({
              name: category,
              value: boxplotStats,
            });
          }
        }
    
        const series: Array<SeriesOption> = [
          {
            type: "boxplot",
            data: boxplotData,
            itemStyle: {
              borderWidth: 2,
            },
            emphasis: {
              itemStyle: {
                borderWidth: 3,
                shadowBlur: 5,
                shadowColor: "rgba(0, 0, 0, 0.3)",
              },
            },
          },
        ];
    
        const echartsOption: EChartsOption = {
          series,
          title: {
            left: "center",
            text: title,
          },
          tooltip: {
            trigger: "item",
          },
          xAxis: {
            type: "category",
            data: categories,
            name: axisXTitle,
            boundaryGap: true,
            nameGap: 30,
            splitArea: {
              show: false,
            },
            splitLine: {
              show: false,
            },
          },
          yAxis: {
            type: "value",
            name: axisYTitle,
            splitArea: {
              show: true,
            },
          },
        };
    
        // Helper function to calculate boxplot statistics
        function calculateBoxplotStats(values: number[]): number[] {
          const len = values.length;
          const min = values[0];
          const max = values[len - 1];
    
          const median =
            len % 2 === 0
              ? (values[len / 2 - 1] + values[len / 2]) / 2
              : values[Math.floor(len / 2)];
    
          const q1Index = Math.floor(len / 4);
          const q3Index = Math.floor((3 * len) / 4);
          const Q1 = values[q1Index];
          const Q3 = values[q3Index];
    
          return [min, Q1, median, Q3, max];
        }
    
        return await generateChartImage(
          echartsOption,
          width,
          height,
          theme,
          outputType,
          "generate_boxplot_chart",
        );
      },
    };
  • Zod schema for boxplot input data items: `category` (string), `value` (number), and optional `group` (string). Used within the tool's inputSchema.
    const data = z.object({
      category: z
        .string()
        .describe("Category of the data point, such as 'Category A'."),
      value: z.number().describe("Value of the data point, such as 10."),
      group: z
        .string()
        .optional()
        .describe(
          "Optional group for the data point, used for grouping in the boxplot.",
        ),
    });
  • The full input schema for the tool: arrays of data items, axis titles, dimensions, theme, title, and output type.
    inputSchema: z.object({
      axisXTitle: AxisXTitleSchema,
      axisYTitle: AxisYTitleSchema,
      data: z
        .array(data)
        .describe(
          "Data for boxplot chart, such as, [{ category: 'Category A', value: 10 }, { category: 'Category B', value: 20, group: 'Group A' }].",
        )
        .nonempty({ message: "Boxplot chart data cannot be empty." }),
      height: HeightSchema,
      theme: ThemeSchema,
      title: TitleSchema,
      width: WidthSchema,
      outputType: OutputTypeSchema,
    }),
  • src/tools/index.ts:3-3 (registration)
    Import of generateBoxplotChartTool from the boxplot module.
    import { generateBoxplotChartTool } from "./boxplot";
  • Registration of generateBoxplotChartTool in the `tools` array, making it available as an MCP tool.
    generateBoxplotChartTool,
Behavior2/5

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

With no annotations, the description must fully disclose behavior but only says 'generate a boxplot chart'. It does not mention that the tool computes statistics, handles outliers, or returns rendered images (outputType is in schema but not description). The description adds minimal behavioral context beyond the name.

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 sentence, front-loaded with the core action ('Generate a boxplot chart'), and contains no redundant text. Every word contributes to stating the tool's purpose efficiently.

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?

The tool has 8 parameters and no output schema, yet the description only explains basic purpose. It fails to note that boxplots require multiple values per category for meaningful statistics, the output format (image/option), or chart customization options. Incomplete for effective use.

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 coverage is 100%, so baseline is 3. The description does not add meaning to the parameters; it only restates that data is for the chart. The example in the description mirrors schema examples, providing no extra clarity. No additional semantics beyond schema.

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

Purpose5/5

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

The description clearly states the tool generates a boxplot chart for statistical summaries across categories, which distinguishes it from sibling chart tools like bar or line charts. The verb 'generate' and resource 'boxplot chart' are specific.

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. It does not mention that boxplots are for distribution comparison, nor does it exclude scenarios where other chart types would be more appropriate. Explicit usage criteria are absent.

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/hustcc/mcp-echarts'

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