UNPKG

@gebrai/gebrai

Version:

Model Context Protocol server for GeoGebra mathematical visualization

1,161 lines (1,160 loc) 124 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.geogebraTools = void 0; // import { MockGeoGebraInstance } from '../utils/geogebra-mock'; // Mock implementation for testing const geogebra_instance_1 = require("../utils/geogebra-instance"); // Real implementation (production) const logger_1 = __importDefault(require("../utils/logger")); const validation_1 = require("../utils/validation"); // Global instance pool for managing GeoGebra instances class GeoGebraInstancePool { instances = new Map(); defaultInstance; async getDefaultInstance() { if (!this.defaultInstance) { this.defaultInstance = new geogebra_instance_1.GeoGebraInstance({ appName: 'classic', // Use classic app for full functionality (from GEB-12 fixes) width: 800, height: 600, showMenuBar: false, showToolBar: false, showAlgebraInput: false }); try { await this.defaultInstance.initialize(true); // Initialize in headless mode for production logger_1.default.info('Default Real GeoGebra instance initialized'); } catch (error) { logger_1.default.error('Failed to initialize default GeoGebra instance', error); throw error; } } return this.defaultInstance; } async cleanup() { if (this.defaultInstance) { await this.defaultInstance.cleanup(); this.defaultInstance = undefined; } for (const instance of this.instances.values()) { await instance.cleanup(); } this.instances.clear(); } } const instancePool = new GeoGebraInstancePool(); // Cleanup on process exit process.on('exit', () => { instancePool.cleanup(); }); process.on('SIGINT', () => { instancePool.cleanup(); process.exit(0); }); /** * GeoGebra MCP Tools */ exports.geogebraTools = [ { tool: { name: 'geogebra_eval_command', description: 'Execute a GeoGebra command and return the result', inputSchema: { type: 'object', properties: { command: { type: 'string', description: 'The GeoGebra command to execute (e.g., "A = (1, 2)", "f(x) = x^2")' } }, required: ['command'] } }, handler: async (params) => { try { const command = params['command']; const instance = await instancePool.getDefaultInstance(); const result = await instance.evalCommand(command); return { content: [{ type: 'text', text: JSON.stringify({ success: result.success, command, result: result.result, error: result.error }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to execute GeoGebra command', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_create_point', description: 'Create a point in GeoGebra with specified coordinates', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the point (e.g., "A", "P1")' }, x: { type: 'number', description: 'X coordinate of the point' }, y: { type: 'number', description: 'Y coordinate of the point' } }, required: ['name', 'x', 'y'] } }, handler: async (params) => { try { const name = params['name']; const x = params['x']; const y = params['y']; // Validate parameters const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid name: ${nameValidation.error}`); } const coordValidation = (0, validation_1.validateCoordinates)(x, y); if (!coordValidation.isValid) { throw new Error(`Invalid coordinates: ${coordValidation.error}`); } const command = `${name} = (${x}, ${y})`; const instance = await instancePool.getDefaultInstance(); const result = await instance.evalCommand(command); if (result.success) { const pointInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, point: pointInfo }, null, 2) }] }; } else { throw new Error(result.error || 'Failed to create point'); } } catch (error) { logger_1.default.error('Failed to create point', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_create_line', description: 'Create a line in GeoGebra using two points or an equation. Provide either point1 and point2 parameters for two-point method, OR equation parameter for equation method.', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the line (e.g., "l", "line1")' }, point1: { type: 'string', description: 'Name of the first point (for two-point method). Optional if using equation method.' }, point2: { type: 'string', description: 'Name of the second point (for two-point method). Optional if using equation method.' }, equation: { type: 'string', description: 'Linear equation (e.g., "y = 2x + 3", "x + y = 5") for equation method. Optional if using two-point method.' } }, required: ['name'] } }, handler: async (params) => { try { const name = params['name']; const point1 = params['point1']; const point2 = params['point2']; const equation = params['equation']; // Validate line name const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid line name: ${nameValidation.error}`); } let command; // Validate parameters based on method if (point1 && point2) { // Two-point method - validate point names const point1Validation = (0, validation_1.validateObjectName)(point1); if (!point1Validation.isValid) { throw new Error(`Invalid first point name: ${point1Validation.error}`); } const point2Validation = (0, validation_1.validateObjectName)(point2); if (!point2Validation.isValid) { throw new Error(`Invalid second point name: ${point2Validation.error}`); } command = `${name} = Line(${point1}, ${point2})`; } else if (equation) { // Equation method - validate equation format const equationValidation = (0, validation_1.validateLinearEquation)(equation); if (!equationValidation.isValid) { throw new Error(`Invalid equation: ${equationValidation.error}`); } // For equation-based lines, we define them directly command = `${name}: ${equation}`; } else { throw new Error('Either provide two points or an equation for the line'); } const instance = await instancePool.getDefaultInstance(); const result = await instance.evalCommand(command); if (result.success) { const lineInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, line: lineInfo, method: point1 && point2 ? 'two-point' : 'equation' }, null, 2) }] }; } else { throw new Error(result.error || 'Failed to create line'); } } catch (error) { logger_1.default.error('Failed to create line', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_create_circle', description: 'Create a circle in GeoGebra with center and radius, or through three points. Provide either center and radius parameters for center-radius method, OR point1, point2, and point3 parameters for three-point method.', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the circle (e.g., "c", "circle1")' }, center: { type: 'string', description: 'Name of the center point (for center-radius method). Optional if using three-point method.' }, radius: { type: 'number', description: 'Radius of the circle (for center-radius method). Optional if using three-point method.', minimum: 0 }, point1: { type: 'string', description: 'First point for three-point circle method. Optional if using center-radius method.' }, point2: { type: 'string', description: 'Second point for three-point circle method. Optional if using center-radius method.' }, point3: { type: 'string', description: 'Third point for three-point circle method. Optional if using center-radius method.' } }, required: ['name'] } }, handler: async (params) => { try { const name = params['name']; const center = params['center']; const radius = params['radius']; const point1 = params['point1']; const point2 = params['point2']; const point3 = params['point3']; // Validate circle name const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid circle name: ${nameValidation.error}`); } let command; // Validate parameters based on method if (center && radius !== undefined) { // Center-radius method const centerValidation = (0, validation_1.validateObjectName)(center); if (!centerValidation.isValid) { throw new Error(`Invalid center point name: ${centerValidation.error}`); } const radiusValidation = (0, validation_1.validateRadius)(radius); if (!radiusValidation.isValid) { throw new Error(`Invalid radius: ${radiusValidation.error}`); } command = `${name} = Circle(${center}, ${radius})`; } else if (point1 && point2 && point3) { // Three-point method - validate all point names const points = [ { name: point1, label: 'first' }, { name: point2, label: 'second' }, { name: point3, label: 'third' } ]; for (const point of points) { const pointValidation = (0, validation_1.validateObjectName)(point.name); if (!pointValidation.isValid) { throw new Error(`Invalid ${point.label} point name: ${pointValidation.error}`); } } command = `${name} = Circle(${point1}, ${point2}, ${point3})`; } else { throw new Error('Either provide center and radius, or three points for the circle'); } const instance = await instancePool.getDefaultInstance(); const result = await instance.evalCommand(command); if (result.success) { const circleInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, circle: circleInfo, method: center && radius !== undefined ? 'center-radius' : 'three-point' }, null, 2) }] }; } else { throw new Error(result.error || 'Failed to create circle'); } } catch (error) { logger_1.default.error('Failed to create circle', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_create_polygon', description: 'Create a polygon in GeoGebra with variable number of vertices', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the polygon (e.g., "poly", "triangle1")' }, vertices: { type: 'array', items: { type: 'string' }, description: 'Array of point names that form the vertices of the polygon', minItems: 3 } }, required: ['name', 'vertices'] } }, handler: async (params) => { try { const name = params['name']; const vertices = params['vertices']; // Validate polygon name const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid polygon name: ${nameValidation.error}`); } // Validate vertices const verticesValidation = (0, validation_1.validatePolygonVertices)(vertices); if (!verticesValidation.isValid) { throw new Error(verticesValidation.error); } const verticesStr = vertices.join(', '); const command = `${name} = Polygon(${verticesStr})`; const instance = await instancePool.getDefaultInstance(); const result = await instance.evalCommand(command); if (result.success) { const polygonInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, polygon: polygonInfo, vertexCount: vertices.length }, null, 2) }] }; } else { throw new Error(result.error || 'Failed to create polygon'); } } catch (error) { logger_1.default.error('Failed to create polygon', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_get_objects', description: 'Get all objects in the current GeoGebra construction', inputSchema: { type: 'object', properties: { type: { type: 'string', description: 'Optional: filter by object type. Use "all" for all objects, or specific types like "point", "line", "circle", "polygon", "function", "conic", etc.' } }, required: [] } }, handler: async (params) => { try { const type = params['type']; const instance = await instancePool.getDefaultInstance(); // Handle the special case where type is "all" - GeoGebra API expects undefined for all objects const typeFilter = (type === 'all') ? undefined : type; const objectNames = await instance.getAllObjectNames(typeFilter); const objects = []; for (const name of objectNames) { const info = await instance.getObjectInfo(name); if (info) { objects.push(info); } } return { content: [{ type: 'text', text: JSON.stringify({ success: true, objectCount: objects.length, objects }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to get objects', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_clear_construction', description: 'Clear all objects from the GeoGebra construction', inputSchema: { type: 'object', properties: {}, required: [] } }, handler: async (_params) => { try { const instance = await instancePool.getDefaultInstance(); await instance.newConstruction(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Construction cleared successfully' }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to clear construction', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_instance_status', description: 'Get the status of the GeoGebra instance', inputSchema: { type: 'object', properties: {}, required: [] } }, handler: async (_params) => { try { const instance = await instancePool.getDefaultInstance(); const isReady = await instance.isReady(); const state = instance.getState(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, status: { isReady, instanceId: state.id, lastActivity: state.lastActivity, config: state.config } }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to get instance status', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_export_png', description: 'Export the current GeoGebra construction as PNG with configurable dimensions, quality, and view settings', inputSchema: { type: 'object', properties: { scale: { type: 'number', description: 'Scale factor for the exported image (default: 1)' }, width: { type: 'number', description: 'Width of the exported image in pixels' }, height: { type: 'number', description: 'Height of the exported image in pixels' }, transparent: { type: 'boolean', description: 'Whether the background should be transparent (default: false)' }, dpi: { type: 'number', description: 'Dots per inch for the exported image (default: 72)' }, xmin: { type: 'number', description: 'Minimum x-coordinate for view range' }, xmax: { type: 'number', description: 'Maximum x-coordinate for view range' }, ymin: { type: 'number', description: 'Minimum y-coordinate for view range' }, ymax: { type: 'number', description: 'Maximum y-coordinate for view range' }, showAxes: { type: 'boolean', description: 'Whether to show coordinate axes (default: true)' }, showGrid: { type: 'boolean', description: 'Whether to show coordinate grid (default: false)' } }, required: [] } }, handler: async (params) => { try { // Input validation with safe defaults let scale = params['scale'] || 1; let width = params['width']; let height = params['height']; const transparent = params['transparent'] || false; let dpi = params['dpi'] || 72; const xmin = params['xmin']; const xmax = params['xmax']; const ymin = params['ymin']; const ymax = params['ymax']; const showAxes = params['showAxes'] !== undefined ? params['showAxes'] : true; const showGrid = params['showGrid'] || false; // Validate and clamp values for safety if (scale !== undefined) { scale = Math.max(0.1, Math.min(10, scale)); } if (width !== undefined) { width = Math.max(100, Math.min(5000, width)); } if (height !== undefined) { height = Math.max(100, Math.min(5000, height)); } if (dpi !== undefined) { dpi = Math.max(72, Math.min(300, dpi)); } const instance = await instancePool.getDefaultInstance(); // Apply view settings if specified if (xmin !== undefined && xmax !== undefined && ymin !== undefined && ymax !== undefined) { await instance.setCoordSystem(xmin, xmax, ymin, ymax); } if (params['showAxes'] !== undefined) { await instance.setAxesVisible(showAxes, showAxes); } if (params['showGrid'] !== undefined) { await instance.setGridVisible(showGrid); } // Export with enhanced parameters const pngBase64 = await instance.exportPNG(scale, transparent, dpi, width, height); return { content: [{ type: 'text', text: JSON.stringify({ success: true, format: 'PNG', scale, width, height, transparent, dpi, viewSettings: { coordSystem: (xmin !== undefined && xmax !== undefined && ymin !== undefined && ymax !== undefined) ? { xmin, xmax, ymin, ymax } : undefined, showAxes, showGrid }, data: pngBase64, encoding: 'base64' }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to export PNG', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_export_svg', description: 'Export the current GeoGebra construction as SVG with configurable view settings', inputSchema: { type: 'object', properties: { xmin: { type: 'number', description: 'Minimum x-coordinate for view range' }, xmax: { type: 'number', description: 'Maximum x-coordinate for view range' }, ymin: { type: 'number', description: 'Minimum y-coordinate for view range' }, ymax: { type: 'number', description: 'Maximum y-coordinate for view range' }, showAxes: { type: 'boolean', description: 'Whether to show coordinate axes (default: true)' }, showGrid: { type: 'boolean', description: 'Whether to show coordinate grid (default: false)' } }, required: [] } }, handler: async (params) => { try { const xmin = params['xmin']; const xmax = params['xmax']; const ymin = params['ymin']; const ymax = params['ymax']; const showAxes = params['showAxes'] !== undefined ? params['showAxes'] : true; const showGrid = params['showGrid'] || false; const instance = await instancePool.getDefaultInstance(); // Apply view settings if specified if (xmin !== undefined && xmax !== undefined && ymin !== undefined && ymax !== undefined) { await instance.setCoordSystem(xmin, xmax, ymin, ymax); } if (params['showAxes'] !== undefined) { await instance.setAxesVisible(showAxes, showAxes); } if (params['showGrid'] !== undefined) { await instance.setGridVisible(showGrid); } const svg = await instance.exportSVG(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, format: 'SVG', viewSettings: { coordSystem: (xmin !== undefined && xmax !== undefined && ymin !== undefined && ymax !== undefined) ? { xmin, xmax, ymin, ymax } : undefined, showAxes, showGrid }, data: svg, encoding: 'utf8' }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to export SVG', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_export_pdf', description: 'Export the current GeoGebra construction as PDF (base64)', inputSchema: { type: 'object', properties: {}, required: [] } }, handler: async (_params) => { try { const instance = await instancePool.getDefaultInstance(); const pdfBase64 = await instance.exportPDF(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, format: 'PDF', data: pdfBase64, encoding: 'base64' }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to export PDF', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_plot_function', description: 'Plot a mathematical function f(x) = expression with configurable domain and styling', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the function (e.g., "f", "g", "func1")' }, expression: { type: 'string', description: 'Mathematical expression in terms of x (e.g., "x^2", "sin(x)", "2*x + 3")' }, xMin: { type: 'number', description: 'Minimum x value for the domain (optional)' }, xMax: { type: 'number', description: 'Maximum x value for the domain (optional)' }, color: { type: 'string', description: 'Color of the function graph (hex, rgb, or color name)' }, thickness: { type: 'number', description: 'Line thickness (1-10)', minimum: 1, maximum: 10 }, style: { type: 'string', description: 'Line style', enum: ['solid', 'dashed', 'dotted'] } }, required: ['name', 'expression'] } }, handler: async (params) => { try { const name = params['name']; const expression = params['expression']; const xMin = params['xMin']; const xMax = params['xMax']; const color = params['color']; const thickness = params['thickness']; const style = params['style']; // Validate function name const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid function name: ${nameValidation.error}`); } // Validate function expression const exprValidation = (0, validation_1.validateFunctionExpression)(expression); if (!exprValidation.isValid) { throw new Error(`Invalid expression: ${exprValidation.error}`); } // Validate domain if provided if (xMin !== undefined && xMax !== undefined) { const domainValidation = (0, validation_1.validateDomainRange)(xMin, xMax, 'x'); if (!domainValidation.isValid) { throw new Error(`Invalid domain: ${domainValidation.error}`); } } // Validate styling if provided const styleValidation = (0, validation_1.validateFunctionStyling)(color, thickness, style); if (!styleValidation.isValid) { throw new Error(`Invalid styling: ${styleValidation.error}`); } const instance = await instancePool.getDefaultInstance(); // Create the function let command; if (xMin !== undefined && xMax !== undefined) { // Function with domain restriction command = `${name}(x) = If(${xMin} <= x <= ${xMax}, ${expression}, ?)`; } else { // Standard function command = `${name}(x) = ${expression}`; } const result = await instance.evalCommand(command); if (!result.success) { throw new Error(result.error || 'Failed to create function'); } // Apply styling if provided const styleCommands = []; if (color) { styleCommands.push(`SetColor(${name}, "${color}")`); } if (thickness) { styleCommands.push(`SetLineThickness(${name}, ${thickness})`); } if (style && style !== 'solid') { const lineType = style === 'dashed' ? '10' : '20'; // GeoGebra line types styleCommands.push(`SetLineStyle(${name}, ${lineType})`); } // Execute styling commands for (const styleCmd of styleCommands) { await instance.evalCommand(styleCmd); } const functionInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, function: functionInfo, domain: (xMin !== undefined && xMax !== undefined) ? { xMin, xMax } : undefined, styling: { color, thickness, style } }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to plot function', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_plot_parametric', description: 'Plot a parametric curve defined by x(t) and y(t) with configurable parameter range and styling', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the parametric curve (e.g., "curve1", "c")' }, xExpression: { type: 'string', description: 'X-coordinate expression in terms of parameter (e.g., "cos(t)", "t")' }, yExpression: { type: 'string', description: 'Y-coordinate expression in terms of parameter (e.g., "sin(t)", "t^2")' }, parameter: { type: 'string', description: 'Parameter variable name (default: "t")', default: 't' }, tMin: { type: 'number', description: 'Minimum parameter value' }, tMax: { type: 'number', description: 'Maximum parameter value' }, color: { type: 'string', description: 'Color of the curve (hex, rgb, or color name)' }, thickness: { type: 'number', description: 'Line thickness (1-10)', minimum: 1, maximum: 10 }, style: { type: 'string', description: 'Line style', enum: ['solid', 'dashed', 'dotted'] } }, required: ['name', 'xExpression', 'yExpression', 'tMin', 'tMax'] } }, handler: async (params) => { try { const name = params['name']; const xExpression = params['xExpression']; const yExpression = params['yExpression']; const parameter = params['parameter'] || 't'; const tMin = params['tMin']; const tMax = params['tMax']; const color = params['color']; const thickness = params['thickness']; const style = params['style']; // Validate curve name const nameValidation = (0, validation_1.validateObjectName)(name); if (!nameValidation.isValid) { throw new Error(`Invalid curve name: ${nameValidation.error}`); } // Validate parametric expressions const exprValidation = (0, validation_1.validateParametricExpressions)(xExpression, yExpression, parameter); if (!exprValidation.isValid) { throw new Error(`Invalid expressions: ${exprValidation.error}`); } // Validate parameter range const rangeValidation = (0, validation_1.validateDomainRange)(tMin, tMax, parameter); if (!rangeValidation.isValid) { throw new Error(`Invalid parameter range: ${rangeValidation.error}`); } // Validate styling if provided const styleValidation = (0, validation_1.validateFunctionStyling)(color, thickness, style); if (!styleValidation.isValid) { throw new Error(`Invalid styling: ${styleValidation.error}`); } const instance = await instancePool.getDefaultInstance(); // Create the parametric curve using GeoGebra's Curve command const command = `${name} = Curve(${xExpression}, ${yExpression}, ${parameter}, ${tMin}, ${tMax})`; const result = await instance.evalCommand(command); if (!result.success) { throw new Error(result.error || 'Failed to create parametric curve'); } // Apply styling if provided const styleCommands = []; if (color) { styleCommands.push(`SetColor(${name}, "${color}")`); } if (thickness) { styleCommands.push(`SetLineThickness(${name}, ${thickness})`); } if (style && style !== 'solid') { const lineType = style === 'dashed' ? '10' : '20'; styleCommands.push(`SetLineStyle(${name}, ${lineType})`); } // Execute styling commands for (const styleCmd of styleCommands) { await instance.evalCommand(styleCmd); } const curveInfo = await instance.getObjectInfo(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, command, curve: curveInfo, parametric: { xExpression, yExpression, parameter, range: { tMin, tMax } }, styling: { color, thickness, style } }, null, 2) }] }; } catch (error) { logger_1.default.error('Failed to plot parametric curve', error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } } }, { tool: { name: 'geogebra_plot_implicit', description: 'Plot an implicit curve defined by F(x,y) = 0 with configurable styling', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the implicit curve (e.g., "implicit1", "curve")' }, expression: { type: 'string', description: 'Implicit expression in terms of x and y (e.g., "x^2 + y^2 - 4", "x^2/4 + y^2/9 - 1")'