calculator
Compute results of mathematical expressions with a safe parser. Handles arithmetic, grouping, common functions, and constants.
Instructions
Evaluate a mathematical expression and return the result.
Supported operations:
Arithmetic: +, -, *, /, %, **
Parentheses for grouping: (2 + 3) * 4
Common functions: abs, ceil, floor, round, sqrt, min, max
Constants: PI, E
Safety:
Does NOT use eval() — uses a safe expression parser
Rejects any non-mathematical input
Examples:
"2 + 3 * 4" → 14
"(2 + 3) * 4" → 20
"sqrt(144)" → 12
"round(3.14159, 2)" → 3.14
"max(10, 20, 30)" → 30
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| expression | Yes | The mathematical expression to evaluate (e.g., '2 + 3 * 4'). |
Implementation Reference
- src/tools/calculator.ts:38-140 (handler)The async handler function that evaluates mathematical expressions. It sanitizes input, checks against a whitelist of allowed math functions/constants, uses Function constructor with restricted scope (Math methods) for safe evaluation, and returns the result as JSON.
async ({ expression }) => { try { // Sanitize: only allow digits, operators, parens, commas, dots, spaces, and known function names const sanitized = expression.trim(); const allowedPattern = /^[\d\s+\-*/%.(),^a-zA-Z_]+$/; if (!allowedPattern.test(sanitized)) { return { content: [ { type: "text" as const, text: "Error: Expression contains disallowed characters.", }, ], isError: true, }; } // Whitelist of allowed functions const allowedWords = new Set([ "abs", "ceil", "floor", "round", "sqrt", "min", "max", "pow", "log", "sin", "cos", "tan", "PI", "E", ]); // Check for any word tokens that aren't in the whitelist const wordTokens = sanitized.match(/[a-zA-Z_]+/g) || []; for (const token of wordTokens) { if (!allowedWords.has(token)) { return { content: [ { type: "text" as const, text: `Error: Disallowed identifier '${token}' in expression.`, }, ], isError: true, }; } } // Safe evaluation using Function constructor with restricted scope const safeScope: Record<string, unknown> = { abs: Math.abs, ceil: Math.ceil, floor: Math.floor, round: Math.round, sqrt: Math.sqrt, min: Math.min, max: Math.max, pow: Math.pow, log: Math.log, sin: Math.sin, cos: Math.cos, tan: Math.tan, PI: Math.PI, E: Math.E, }; const paramNames = Object.keys(safeScope); const paramValues = Object.values(safeScope); // Replace ** with Math.pow for exponentiation let processedExpr = sanitized.replace(/\^/g, "**"); // Build a safe evaluator const fn = new Function( ...paramNames, `"use strict"; return (${processedExpr});` ); const result = fn(...paramValues); return { content: [ { type: "text" as const, text: JSON.stringify( { expression: sanitized, result: typeof result === "number" ? result : String(result), type: typeof result, }, null, 2 ), }, ], }; } catch (err: any) { return { content: [ { type: "text" as const, text: `Calculation Error: ${err.message}`, }, ], isError: true, }; } } ); } - src/tools/calculator.ts:31-37 (schema)Zod schema for the 'expression' input parameter — a string describing the mathematical expression to evaluate.
{ expression: z .string() .describe( "The mathematical expression to evaluate (e.g., '2 + 3 * 4')." ), }, - src/tools/calculator.ts:10-140 (registration)The registerCalculatorTool function that registers the 'calculator' tool on the MCP server via server.tool(), including description, schema, and handler.
export function registerCalculatorTool(server: McpServer): void { server.tool( "calculator", `Evaluate a mathematical expression and return the result. Supported operations: - Arithmetic: +, -, *, /, %, ** - Parentheses for grouping: (2 + 3) * 4 - Common functions: abs, ceil, floor, round, sqrt, min, max - Constants: PI, E Safety: - Does NOT use eval() — uses a safe expression parser - Rejects any non-mathematical input Examples: - "2 + 3 * 4" → 14 - "(2 + 3) * 4" → 20 - "sqrt(144)" → 12 - "round(3.14159, 2)" → 3.14 - "max(10, 20, 30)" → 30`, { expression: z .string() .describe( "The mathematical expression to evaluate (e.g., '2 + 3 * 4')." ), }, async ({ expression }) => { try { // Sanitize: only allow digits, operators, parens, commas, dots, spaces, and known function names const sanitized = expression.trim(); const allowedPattern = /^[\d\s+\-*/%.(),^a-zA-Z_]+$/; if (!allowedPattern.test(sanitized)) { return { content: [ { type: "text" as const, text: "Error: Expression contains disallowed characters.", }, ], isError: true, }; } // Whitelist of allowed functions const allowedWords = new Set([ "abs", "ceil", "floor", "round", "sqrt", "min", "max", "pow", "log", "sin", "cos", "tan", "PI", "E", ]); // Check for any word tokens that aren't in the whitelist const wordTokens = sanitized.match(/[a-zA-Z_]+/g) || []; for (const token of wordTokens) { if (!allowedWords.has(token)) { return { content: [ { type: "text" as const, text: `Error: Disallowed identifier '${token}' in expression.`, }, ], isError: true, }; } } // Safe evaluation using Function constructor with restricted scope const safeScope: Record<string, unknown> = { abs: Math.abs, ceil: Math.ceil, floor: Math.floor, round: Math.round, sqrt: Math.sqrt, min: Math.min, max: Math.max, pow: Math.pow, log: Math.log, sin: Math.sin, cos: Math.cos, tan: Math.tan, PI: Math.PI, E: Math.E, }; const paramNames = Object.keys(safeScope); const paramValues = Object.values(safeScope); // Replace ** with Math.pow for exponentiation let processedExpr = sanitized.replace(/\^/g, "**"); // Build a safe evaluator const fn = new Function( ...paramNames, `"use strict"; return (${processedExpr});` ); const result = fn(...paramValues); return { content: [ { type: "text" as const, text: JSON.stringify( { expression: sanitized, result: typeof result === "number" ? result : String(result), type: typeof result, }, null, 2 ), }, ], }; } catch (err: any) { return { content: [ { type: "text" as const, text: `Calculation Error: ${err.message}`, }, ], isError: true, }; } } ); } - src/index.ts:52-52 (registration)Call site where registerCalculatorTool is invoked during tool registration in McpToolkitServer.registerTools().
registerCalculatorTool(this.server); - src/index.ts:9-9 (registration)Import of registerCalculatorTool from the calculator module.
import { registerCalculatorTool } from "./tools/calculator.js";