generate_dual_axes_chart
Visualize trends and comparisons by creating a dual axes chart combining a bar chart and a line chart, ideal for displaying sales and profit data over time.
Instructions
Generate a dual axes chart which is a combination chart that integrates two different chart types, typically combining a bar chart with a line chart to display both the trend and comparison of data, such as, the trend of sales and profit over time.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| axisXTitle | No | Set the x-axis title of chart. | |
| categories | Yes | Categories for dual axes chart, such as, ['2015', '2016', '2017']. | |
| height | No | Set the height of chart, default is 400. | |
| series | Yes | Series for dual axes chart, such as, [{ type: 'column', data: [91.9, 99.1, 101.6, 114.4, 121], axisYTitle: '销售额' }, { type: 'line', data: [0.055, 0.06, 0.062, 0.07, 0.075], 'axisYTitle': '利润率' }]. | |
| style | No | Custom style configuration for the chart. | |
| theme | No | Set the theme for the chart, optional, default is 'default'. | default |
| title | No | Set the title of chart. | |
| width | No | Set the width of chart, default is 600. |
Implementation Reference
- src/utils/callTool.ts:43-110 (handler)Generic handler for chart generation tools. For 'generate_dual_axes_chart', maps to chartType 'dual-axes', validates args using dual-axes schema, calls generateChartUrl to get chart URL, and returns MCP tool result.export async function callTool(tool: string, args: object = {}) { logger.info(`Calling tool: ${tool}`); const chartType = CHART_TYPE_MAP[tool as keyof typeof CHART_TYPE_MAP]; if (!chartType) { logger.error(`Unknown tool: ${tool}`); throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${tool}.`); } try { // Validate input using Zod before sending to API. // Select the appropriate schema based on the chart type. const schema = Charts[chartType].schema; if (schema) { // Use safeParse instead of parse and try-catch. const result = z.object(schema).safeParse(args); if (!result.success) { logger.error(`Invalid parameters: ${result.error.message}`); throw new McpError( ErrorCode.InvalidParams, `Invalid parameters: ${result.error.message}`, ); } } const isMapChartTool = [ "generate_district_map", "generate_path_map", "generate_pin_map", ].includes(tool); if (isMapChartTool) { // For map charts, we use the generateMap function, and return the mcp result. const { metadata, ...result } = await generateMap(tool, args); return result; } const url = await generateChartUrl(chartType, args); logger.info(`Generated chart URL: ${url}`); return { content: [ { type: "text", text: url, }, ], _meta: { description: "This is the chart's spec and configuration, which can be renderred to corresponding chart by AntV GPT-Vis chart components.", spec: { type: chartType, ...args }, }, }; // biome-ignore lint/suspicious/noExplicitAny: <explanation> } catch (error: any) { logger.error( `Failed to generate chart: ${error.message || "Unknown error"}.`, ); if (error instanceof McpError) throw error; if (error instanceof ValidateError) throw new McpError(ErrorCode.InvalidParams, error.message); throw new McpError( ErrorCode.InternalError, `Failed to generate chart: ${error?.message || "Unknown error."}`, ); } }
- src/utils/generate.ts:12-39 (helper)Posts the chart type ('dual-axes') and configuration options to the visualization server API, returning the rendered chart URL.export async function generateChartUrl( type: string, // biome-ignore lint/suspicious/noExplicitAny: <explanation> options: Record<string, any>, ): Promise<string> { const url = getVisRequestServer(); const response = await axios.post( url, { type, ...options, source: "mcp-server-chart", }, { headers: { "Content-Type": "application/json", }, }, ); const { success, errorMessage, resultObj } = response.data; if (!success) { throw new Error(errorMessage); } return resultObj; }
- src/charts/dual-axes.ts:15-62 (schema)Zod schema definitions for validating input to generate_dual_axes_chart: DualAxesSeriesSchema and main schema object.const DualAxesSeriesSchema = z.object({ type: z .enum(["column", "line"]) .describe("The optional value can be 'column' or 'line'."), data: z .array(z.number()) .describe( "When type is column, the data represents quantities, such as [91.9, 99.1, 101.6, 114.4, 121]. When type is line, the data represents ratios and its values are recommended to be less than 1, such as [0.055, 0.06, 0.062, 0.07, 0.075].", ), axisYTitle: z .string() .default("") .describe( "Set the y-axis title of the chart series, such as, axisYTitle: '销售额'.", ) .optional(), }); // Dual axes chart input schema const schema = { categories: z .array(z.string()) .describe( "Categories for dual axes chart, such as, ['2015', '2016', '2017'].", ) .nonempty({ message: "Dual axes chart categories cannot be empty." }), series: z .array(DualAxesSeriesSchema) .describe( "Series for dual axes chart, such as, [{ type: 'column', data: [91.9, 99.1, 101.6, 114.4, 121], axisYTitle: '销售额' }, { type: 'line', data: [0.055, 0.06, 0.062, 0.07, 0.075], 'axisYTitle': '利润率' }].", ) .nonempty({ message: "Dual axes chart series cannot be empty." }), style: z .object({ backgroundColor: BackgroundColorSchema, palette: PaletteSchema, texture: TextureSchema, }) .optional() .describe( "Style configuration for the chart with a JSON object, optional.", ), theme: ThemeSchema, width: WidthSchema, height: HeightSchema, title: TitleSchema, axisXTitle: AxisXTitleSchema, };
- src/server.ts:64-77 (registration)Registers the MCP tool handlers: listTools returns array of tool descriptors from all enabled chart modules (including generate_dual_axes_chart), callTool handler delegates to callTool utility.function setupToolHandlers(server: Server): void { logger.info("setting up tool handlers..."); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: getEnabledTools().map((chart) => chart.tool), })); // biome-ignore lint/suspicious/noExplicitAny: <explanation> server.setRequestHandler(CallToolRequestSchema, async (request: any) => { logger.info("calling tool", request.params.name, request.params.arguments); return await callTool(request.params.name, request.params.arguments); }); logger.info("tool handlers set up"); }
- src/utils/callTool.ts:9-35 (registration)Maps tool name 'generate_dual_axes_chart' to internal chart type 'dual-axes' for dispatch.const CHART_TYPE_MAP = { generate_area_chart: "area", generate_bar_chart: "bar", generate_boxplot_chart: "boxplot", generate_column_chart: "column", generate_district_map: "district-map", generate_dual_axes_chart: "dual-axes", generate_fishbone_diagram: "fishbone-diagram", generate_flow_diagram: "flow-diagram", generate_funnel_chart: "funnel", generate_histogram_chart: "histogram", generate_line_chart: "line", generate_liquid_chart: "liquid", generate_mind_map: "mind-map", generate_network_graph: "network-graph", generate_organization_chart: "organization-chart", generate_path_map: "path-map", generate_pie_chart: "pie", generate_pin_map: "pin-map", generate_radar_chart: "radar", generate_sankey_chart: "sankey", generate_scatter_chart: "scatter", generate_treemap_chart: "treemap", generate_venn_chart: "venn", generate_violin_chart: "violin", generate_word_cloud_chart: "word-cloud", } as const;