create_line_chart
Generate text-based line charts for visualizing temporal data directly in terminal environments. Configure data points, axis labels, chart dimensions, and colors for clear, ASCII-based data representation.
Instructions
Generate ASCII line charts for temporal data visualization
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| color | No | ANSI color name (red, green, blue, yellow, etc.) | |
| data | Yes | Array of numeric values to plot | |
| height | No | Chart height (5-50, default: 15) | |
| labels | No | Optional labels for x-axis (must match data length) | |
| title | No | Optional chart title | |
| width | No | Chart width (10-200, default: 60) |
Implementation Reference
- src/charts/line-chart.ts:5-113 (handler)The primary handler function that executes the create_line_chart tool logic. It processes ChartData, generates an ASCII art line chart using grid-based rendering, Bresenham line drawing, and returns a ChartResult.export function createLineChart(data: ChartData): ChartResult { const { data: values, labels, title, width = 60, height = 15, color = 'white' } = data; if (values.length === 0) { throw new Error('Data array cannot be empty'); } const chartWidth = width - 10; // Reserve space for y-axis labels const chartHeight = height - 2; // Reserve space for x-axis const minValue = Math.min(...values); const maxValue = Math.max(...values); const valueRange = maxValue - minValue; // Create the chart grid const grid = createGrid(width, height); // Draw y-axis labels and grid lines for (let y = 0; y < chartHeight; y++) { const value = maxValue - (y / (chartHeight - 1)) * valueRange; const label = value.toFixed(1); const labelStr = padLeft(label, 8); // Place y-axis label for (let i = 0; i < Math.min(labelStr.length, 8); i++) { if (8 - i < width) { grid[y][8 - i] = labelStr[i]; } } // Draw y-axis line if (9 < width) { grid[y][9] = y === chartHeight - 1 ? ASCII_CHARS.bottomLeft : y === 0 ? ASCII_CHARS.topLeft : ASCII_CHARS.teeRight; } // Draw horizontal grid lines (optional light lines) for (let x = 10; x < width; x++) { if (y === chartHeight - 1) { grid[y][x] = ASCII_CHARS.horizontal; } else if (y % 2 === 0) { grid[y][x] = '·'; // Light grid dots } } } // Draw x-axis labels if (labels && labels.length === values.length) { // const labelSpacing = Math.max(1, Math.floor(chartWidth / Math.min(labels.length, 8))); for (let i = 0; i < labels.length && i < 8; i++) { const x = 10 + Math.floor((i / (labels.length - 1)) * (chartWidth - 1)); const label = labels[i].substring(0, 6); // Truncate long labels if (x + label.length <= width && height - 1 >= 0) { for (let j = 0; j < label.length && x + j < width; j++) { grid[height - 1][x + j] = label[j]; } } } } // Plot the line const plotPoints: { x: number; y: number }[] = []; for (let i = 0; i < values.length; i++) { const x = values.length === 1 ? 10 + Math.floor(chartWidth / 2) : 10 + Math.floor((i / (values.length - 1)) * (chartWidth - 1)); const normalizedValue = valueRange === 0 ? 0.5 : normalize(values[i], minValue, maxValue); const y = Math.floor((1 - normalizedValue) * (chartHeight - 1)); plotPoints.push({ x: clamp(x, 10, width - 1), y: clamp(y, 0, chartHeight - 1) }); } // Draw line segments between points for (let i = 0; i < plotPoints.length; i++) { const point = plotPoints[i]; // Draw the point if (point.x < width && point.y < chartHeight) { grid[point.y][point.x] = '●'; } // Draw line to next point if (i < plotPoints.length - 1) { const nextPoint = plotPoints[i + 1]; drawLine(grid, point.x, point.y, nextPoint.x, nextPoint.y, chartWidth, chartHeight); } } // Convert grid to string and apply coloring let chart = gridToString(grid); if (color !== 'white') { chart = colorize(chart, color); } // Add title if provided if (title) { const titleLine = center(title, width); chart = titleLine + '\n' + chart; } return { chart, title, dimensions: { width, height } }; }
- src/types/index.ts:1-8 (schema)TypeScript interface defining the input structure (ChartData) for the create_line_chart tool, used for type safety and validation.export interface ChartData { data: number[]; labels?: string[]; title?: string; width?: number; height?: number; color?: string; }
- src/index.ts:58-102 (registration)MCP tool registration in the ListTools response, defining the tool name, description, and JSON schema for inputs.{ name: 'create_line_chart', description: 'Generate ASCII line charts for temporal data visualization', inputSchema: { type: 'object', properties: { data: { type: 'array', items: { type: 'number' }, description: 'Array of numeric values to plot' }, labels: { type: 'array', items: { type: 'string' }, description: 'Optional labels for x-axis (must match data length)', optional: true }, title: { type: 'string', description: 'Optional chart title', optional: true }, width: { type: 'number', description: 'Chart width (10-200, default: 60)', minimum: 10, maximum: 200, optional: true }, height: { type: 'number', description: 'Chart height (5-50, default: 15)', minimum: 5, maximum: 50, optional: true }, color: { type: 'string', description: 'ANSI color name (red, green, blue, yellow, etc.)', optional: true } }, required: ['data'], examples: getToolExamples('create_line_chart') }
- src/index.ts:339-346 (handler)Dispatch handler in the MCP CallToolRequest that invokes the createLineChart function after validation.case 'create_line_chart': { progress.nextStep('Generating line chart'); result = await withRequestTracking( () => Promise.resolve(createLineChart(chartData)), 'create_line_chart' )(); break; }
- src/charts/line-chart.ts:115-160 (helper)Supporting helper function that draws line segments between plotted points using an efficient line rasterization algorithm.function drawLine( grid: string[][], x1: number, y1: number, x2: number, y2: number, maxWidth: number, maxHeight: number ): void { const dx = Math.abs(x2 - x1); const dy = Math.abs(y2 - y1); const sx = x1 < x2 ? 1 : -1; const sy = y1 < y2 ? 1 : -1; let err = dx - dy; let x = x1; let y = y1; // eslint-disable-next-line no-constant-condition while (true) { // Draw line character based on direction if (x >= 10 && x < maxWidth + 10 && y >= 0 && y < maxHeight) { if (grid[y][x] === ' ' || grid[y][x] === '·') { if (dx > dy) { grid[y][x] = ASCII_CHARS.horizontal; } else if (dy > dx) { grid[y][x] = ASCII_CHARS.vertical; } else { grid[y][x] = x < x2 ? ASCII_CHARS.curveUpRight : ASCII_CHARS.curveUpLeft; } } } if (x === x2 && y === y2) break; const e2 = 2 * err; if (e2 > -dy) { err -= dy; x += sx; } if (e2 < dx) { err += dx; y += sy; } } }