generate_gauge_chart
Create a gauge chart to monitor a single indicator's current status, such as CPU usage, progress, or score. Configure min, max, title, theme, and output as PNG, SVG, or ECharts option for easy embedding.
Instructions
Generate a gauge chart to display single indicator's current status, such as, CPU usage rate, completion progress, or performance scores.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| data | Yes | Data for gauge chart, such as, [{ name: 'CPU Usage', value: 75 }]. Multiple gauges can be displayed. | |
| height | No | Set the height of the chart, default is 600px. | |
| max | No | Maximum value of the gauge, default is 100. | |
| min | No | Minimum value of the gauge, default is 0. | |
| theme | No | Set the theme for the chart, optional, default is 'default'. | default |
| title | No | Set the title of the chart. | |
| width | No | Set the width of the chart, default is 800px. | |
| outputType | No | The 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
- src/tools/gauge.ts:45-158 (handler)The run handler function that executes generate_gauge_chart tool logic. It builds ECharts gauge series options from input data and calls generateChartImage to render the chart.
run: async (params: { data: Array<{ name: string; value: number }>; height: number; max?: number; min?: number; theme?: "default" | "dark"; title?: string; width: number; outputType?: "png" | "svg" | "option"; }) => { const { data, height, max = 100, min = 0, theme, title, width, outputType, } = params; // For multiple gauges, arrange them horizontally const series: Array<SeriesOption> = data.map((item, index) => { const isMultiple = data.length > 1; return { name: item.name, type: "gauge", data: [{ name: item.name, value: item.value }], center: isMultiple ? [`${(100 / (data.length + 1)) * (index + 1)}%`, "60%"] : ["50%", "55%"], radius: isMultiple ? `${Math.min(80 / data.length, 30)}%` : "80%", min: min, max: max, startAngle: 180, endAngle: 0, axisLine: { lineStyle: { width: 6, color: [ [0.3, "#67e0e3"], [0.7, "#37a2da"], [1, "#fd666d"], ], }, }, pointer: { itemStyle: { color: "inherit", }, }, axisTick: { distance: -30, length: 8, lineStyle: { color: "#fff", width: 2, }, }, splitLine: { distance: -30, length: 30, lineStyle: { color: "#fff", width: 4, }, }, axisLabel: { color: "inherit", distance: 40, fontSize: isMultiple ? 10 : 12, }, detail: { valueAnimation: true, formatter: "{value}", color: "inherit", fontSize: isMultiple ? 16 : 20, offsetCenter: [0, "30%"], }, title: { offsetCenter: [0, "50%"], fontSize: isMultiple ? 12 : 14, }, }; }); const echartsOption: EChartsOption = { legend: data.length > 1 ? { bottom: 10, left: "center", orient: "horizontal", data: data.map((item) => item.name), } : undefined, series, title: { left: "center", text: title, top: data.length > 1 ? "5%" : undefined, }, }; return await generateChartImage( echartsOption, width, height, theme, outputType, "generate_gauge_chart", ); }, - src/tools/gauge.ts:22-44 (schema)Input schema for generate_gauge_chart tool using Zod validations: array of {name, value} data objects, min/max range, height, width, theme, title, and outputType.
inputSchema: z.object({ data: z .array(data) .describe( "Data for gauge chart, such as, [{ name: 'CPU Usage', value: 75 }]. Multiple gauges can be displayed.", ) .nonempty({ message: "Gauge chart data cannot be empty." }), height: HeightSchema, max: z .number() .optional() .default(100) .describe("Maximum value of the gauge, default is 100."), min: z .number() .optional() .default(0) .describe("Minimum value of the gauge, default is 0."), theme: ThemeSchema, title: TitleSchema, width: WidthSchema, outputType: OutputTypeSchema, }), - src/tools/index.ts:30-31 (registration)Tool registration in the tools array that gets passed to McpServer.tool() in src/index.ts.
generateGaugeChartTool, generateTreemapChartTool, - src/utils/imageHandler.ts:39-170 (helper)generateChartImage helper function called by the handler to render and return the chart as PNG (Base64 or MinIO URL), SVG, or raw ECharts option.
export async function generateChartImage( echartsOption: EChartsOption, width = 800, height = 600, theme: "default" | "dark" = "default", outputType: ImageOutputFormat = "png", toolName = "unknown", ): Promise<ImageHandlerResult> { // Debug logging if (process.env.DEBUG_MCP_ECHARTS) { console.error(`[DEBUG] ${toolName} generating chart:`, { width, height, theme, outputType, optionKeys: Object.keys(echartsOption), }); } try { // Render chart const result = await renderECharts( echartsOption, width, height, theme, outputType, ); // Determine output type const isImage = outputType !== "svg" && outputType !== "option"; if (!isImage) { // SVG or configuration options, return text directly const response = { content: [ { type: "text" as const, text: result as string, }, ], }; if (process.env.DEBUG_MCP_ECHARTS) { console.error(`[DEBUG] ${toolName} chart generated successfully:`, { contentType: "text", textLength: (result as string).length, }); } return response; } // PNG image type const buffer = result as Buffer; if (isMinIOConfigured()) { try { // Use MinIO storage, return URL const url = await storeBufferToMinIO(buffer, "png", "image/png"); const response = { content: [ { type: "text" as const, text: url, }, ], }; if (process.env.DEBUG_MCP_ECHARTS) { console.error(`[DEBUG] ${toolName} chart generated successfully:`, { contentType: "text", url: url, }); } return response; } catch (minioError) { // MinIO failed, log warning and fallback to Base64 if (process.env.DEBUG_MCP_ECHARTS) { console.error( `[DEBUG] ${toolName} MinIO storage failed, falling back to Base64:`, { error: minioError instanceof Error ? minioError.message : String(minioError), }, ); } // Continue to Base64 fallback below } } // Fallback to Base64 const base64Data = buffer.toString("base64"); const response = { content: [ { type: "image" as const, data: base64Data, mimeType: "image/png", }, ], }; if (process.env.DEBUG_MCP_ECHARTS) { console.error(`[DEBUG] ${toolName} chart generated successfully:`, { contentType: "image", dataLength: base64Data.length, }); } return response; } catch (error) { // Error logging if (process.env.DEBUG_MCP_ECHARTS) { console.error(`[DEBUG] ${toolName} chart generation failed:`, { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); } throw new Error( `Chart rendering failed: ${ error instanceof Error ? error.message : String(error) }`, ); } } - src/utils/render.ts:20-78 (helper)renderECharts helper that uses napi-rs canvas and server-side ECharts to render the chart option into a Buffer (PNG) or string (SVG/JSON).
export async function renderECharts( echartsOption: EChartsOption, width = 800, height = 600, theme = "default", outputType: "png" | "svg" | "option" = "png", ): Promise<Buffer | string> { if (outputType === "svg" || outputType === "option") { const chart = echarts.init(null, theme, { renderer: "svg", ssr: true, width, height, }); chart.setOption({ ...echartsOption, animation: false, }); // Output string const svgStr = chart.renderToSVGString(); // If the chart is no longer needed, call dispose to free memory chart.dispose(); // Return SVG string or validated ECharts configuration options return outputType === "svg" ? svgStr : JSON.stringify(echartsOption, null, 2); } // Other output types (such as PNG) need to use Canvas const canvas = createCanvas(width, height) as unknown as HTMLCanvasElement; const chart = echarts.init(canvas, theme, { devicePixelRatio: 3, }); echarts.setPlatformAPI({ loadImage(src, onload, onerror) { const img = new Image(); img.onload = onload.bind(img); img.onerror = onerror.bind(img); img.src = src; return img; }, }); chart.setOption({ ...echartsOption, animation: false, }); // @ts-ignore const buffer = canvas.toBuffer("image/png"); chart.dispose(); return buffer; }