evaluate_expression
Evaluate mathematical expressions using arithmetic operators, trigonometric and logarithmic functions, constants, and scientific notation. Supports parentheses and explicit operators.
Instructions
Evaluate a mathematical expression. PRIMARY tool for ALL math: +, -, *, /, %, ^. Functions: sqrt, sin, cos, tan, asin, acos, atan, log10, ln, log(x,base), abs, round, floor, ceil, min, max. Constants: pi, e, tau, phi, sqrt2, euler_mascheroni, c, g, G, h, k, R, NA, e_charge, m_e, m_p. Parentheses and scientific notation (1e6) supported. Use explicit operators: 2 * pi, not 2pi.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| expression | Yes |
Implementation Reference
- cruncher.js:1762-1857 (handler)The handler function for the evaluate_expression tool. Validates input length, preprocesses the expression (converts ^ to **, handles scientific notation, replaces function names with Math.* equivalents, substitutes constants), applies a security whitelist check, then evaluates the expression using the Function constructor and returns the result.
evaluate_expression: ({ expression }) => { // Reject oversized expressions BEFORE any regex processing (DoS prevention) if (typeof expression !== "string") { throw new Error( "Type Error: expression must be a string, got " + typeof expression ); } if (expression.length > MAX_EXPR_LENGTH) { throw new Error( `Expression too long (${expression.length} chars). Maximum is ${MAX_EXPR_LENGTH}.` ); } // Pre-compiled regexes for expression preprocessing // 1. Convert mathematical ^ to JavaScript's ** operator let parsedExpr = expression.replace(RE_NOTATION_CARAT, "**"); // 2. Convert scientific notation (1e6, 2.5e-3) to safe multiplication parsedExpr = parsedExpr.replace( RE_SCIENTIFIC_NOTATION, "($1 * Math.pow(10, $2))" ); // 3. Convert built-in functions to Math.* equivalents parsedExpr = parsedExpr .replace(RE_FUNC_ABS, "Math.abs(") .replace(RE_FUNC_ROUND, "Math.round(") .replace(RE_FUNC_FLOOR, "Math.floor(") .replace(RE_FUNC_CEIL, "Math.ceil(") .replace(RE_FUNC_MIN_FUNC, "Math.min(") .replace(RE_FUNC_MAX_FUNC, "Math.max(") // Trigonometric (radians — standard math convention) .replace(RE_FUNC_SIN, "Math.sin(") .replace(RE_FUNC_COS, "Math.cos(") .replace(RE_FUNC_TAN, "Math.tan(") .replace(RE_FUNC_ASIN, "Math.asin(") .replace(RE_FUNC_ACOS, "Math.acos(") .replace(RE_FUNC_ATAN, "Math.atan(") // Math helpers .replace(RE_FUNC_SQRT, "Math.sqrt(") .replace(RE_FUNC_LOG, "Math.log10(") .replace(RE_FUNC_LN, "Math.log("); // 3.1. Handle log(x, base) → Math.log(x) / Math.log(base) parsedExpr = parsedExpr.replace(RE_FUNC_LOG_BASE, "Math.log($1) / Math.log($2)"); // 3.5. Substitute constant names with their numeric values // 3.5. Substitute constant names with their numeric values // e.g., "pi * 2" → "3.141592653589793 * 2" // Use word boundaries so "pi" doesn't match inside other identifiers. // Longest constant names are matched first to avoid partial collisions. // Required explicit operator: "2 * pi", not "2pi". parsedExpr = parsedExpr.replace(RE_CONSTANTS, (match) => CONSTANTS[match].toString()); // 4. SECURITY CHECK: Strict Whitelist if (RE_DISALLOWED_CHARS.test(parsedExpr)) { throw new Error( "Security Error: Expression contains invalid characters. " + "Only numbers, basic operators (+, -, *, /, %, ^), parentheses, commas, " + "functions (abs, round, floor, ceil, min, max, sin, cos, tan, asin, acos, atan, sqrt, log10, ln, log) " + "and constants (pi, e, tau, phi, sqrt2, c, g, G, h, k, R, NA, euler_mascheroni) " + "are allowed.", ); } // Verify only valid Math.* functions remain const sanitizedExpr = parsedExpr.replace(RE_VALID_MATH_CALLS, ""); if (sanitizedExpr.includes("Math.")) { throw new Error( "Security Error: Invalid Math function. " + "Only abs, round, floor, ceil, min, max, pow, sin, cos, tan, asin, acos, atan, sqrt, log10, log are allowed.", ); } try { // 5. Evaluate safely // Because we strictly verified the contents above, this is now safe to run. const result = new Function("return (" + parsedExpr + ")")(); if (result === Infinity || result === -Infinity) { throw new Error( "Domain Error: Expression evaluated to infinity. " + "Check for division by zero or overflow (e.g., exp(1000)).", ); } if (isNaN(result)) { // Try to give a more helpful message by re-evaluating sub-expressions throw new Error( "Domain Error: Expression evaluated to NaN. " + "Check for: sqrt(negative), log(negative/zero), asin/acos out of [-1,1], or 0/0.", ); } return result; } catch (error) { throw new Error("Failed to evaluate expression: " + error.message); } }, - cruncher.js:903-918 (schema)The tool definition/schema for evaluate_expression, listing it as a tool with input schema requiring a single 'expression' string parameter. Describes supported operators, functions, and constants.
name: "evaluate_expression", annotations: { title: "Evaluate Expression", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, description: "Evaluate a mathematical expression. PRIMARY tool for ALL math: +, -, *, /, %, ^. Functions: sqrt, sin, cos, tan, asin, acos, atan, log10, ln, log(x,base), abs, round, floor, ceil, min, max. Constants: pi, e, tau, phi, sqrt2, euler_mascheroni, c, g, G, h, k, R, NA, e_charge, m_e, m_p. Parentheses and scientific notation (1e6) supported. Use explicit operators: 2 * pi, not 2pi.", inputSchema: { type: "object", properties: { expression: { type: "string" } }, required: ["expression"], }, }, - cruncher.js:1051-1051 (registration)The tool list is filtered by tool tier (minimal/standard/full) which includes evaluate_expression. It's registered in the TOOLS array and exposed via the tools/list method.
const TOOLS = filterToolsByTier(toolsAll, TOOL_SET); - cruncher.js:69-92 (registration)evaluate_expression is listed in both the 'minimal' and 'standard' tool tiers, making it available in all tier configurations.
const TOOL_TIERS = { // Minimal: math primitives + evaluate_expression (5 tools) minimal: [ "evaluate_expression", "add", "subtract", "multiply", "divide", ], // Standard: core math + trig, common stats, constants, unit conversion (34 tools) standard: [ "evaluate_expression", "add", "subtract", "multiply", "divide", "sqrt", "power", "absolute", "modulo", "factorial", "logarithm", "natural_log", "get_constant", "sine", "cosine", "tangent", "asin", "acos", "atan", "set_angle_mode", "get_angle_mode", "sum", "avg", "min", "max", "count", "variance", "std_dev", "percentage_of", "percentage_change", "percentage_reverse", "median", "range", "convert_unit", ], // Full: standard + base conversion, advanced stats, memory ops, admin tools full: null, }; /** Filter master tool list by active tier. */ function filterToolsByTier(allTools, tier) { - cruncher.js:200-227 (helper)Pre-compiled regular expressions used by the evaluate_expression handler for transforming expressions (caret to power, scientific notation, function names, constants) and security validation (whitelist check).
// --- Pre-compiled Regex for evaluate_expression --- const RE_NOTATION_CARAT = /\^/g; const RE_SCIENTIFIC_NOTATION = /(\d+\.?\d*)e([+-]?\d+)/gi; const RE_FUNC_ABS = /\babs\s*\(/g; const RE_FUNC_ROUND = /\bround\s*\(/g; const RE_FUNC_FLOOR = /\bfloor\s*\(/g; const RE_FUNC_CEIL = /\bceil\s*\(/g; const RE_FUNC_MIN_FUNC = /\bmin\s*\(/g; const RE_FUNC_MAX_FUNC = /\bmax\s*\(/g; // Trigonometric functions (radians only — same as standard math) const RE_FUNC_SIN = /\bsin\s*\(/g; const RE_FUNC_COS = /\bcos\s*\(/g; const RE_FUNC_TAN = /\btan\s*\(/g; const RE_FUNC_ASIN = /\basin\s*\(/g; const RE_FUNC_ACOS = /\bacos\s*\(/g; const RE_FUNC_ATAN = /\batan\s*\(/g; // Math helpers const RE_FUNC_SQRT = /\bsqrt\s*\(/g; const RE_FUNC_LOG = /\blog10\s*\(/g; // log10() → Math.log10() const RE_FUNC_LN = /\bln\s*\(/g; // ln() → Math.log() const RE_FUNC_LOG_BASE = /\blog\s*\(([^,)]+)\s*,\s*([^)]+)\)/g; // log(x,base) // Constants pattern: longest names first to avoid partial matches (e_charge before e, // euler_mascheroni before e, sqrt2 before pi, tau before tau). Built dynamically. const RE_CONSTANTS = (() => { const names = Object.keys(CONSTANTS).sort((a, b) => b.length - a.length); const escaped = names.map(n => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); return new RegExp(`\\b(${escaped.join('|')})\\b`, 'g'); })();