calculator.py•3.89 kB
import math
import ast
import operator
from typing import Dict, Any, List, Union
from mcp_handler import Tool
class CalculatorTool(Tool):
"""Tool for performing mathematical calculations"""
def __init__(self):
# Define parameters for the calculator tool
parameters = [
{
"name": "expression",
"type": "string",
"description": "A mathematical expression to evaluate (e.g., '2 * (3 + 4)')",
"required": True
}
]
super().__init__(
name="calculator",
description="Perform mathematical calculations",
parameters=parameters
)
# Define allowed operators for the calculator
self.operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg, # Unary negation
ast.Mod: operator.mod,
}
# Define allowed functions
self.functions = {
"abs": abs,
"round": round,
"min": min,
"max": max,
"sum": sum,
"sqrt": math.sqrt,
"sin": math.sin,
"cos": math.cos,
"tan": math.tan,
"log": math.log,
"log10": math.log10,
"exp": math.exp,
"pi": math.pi,
"e": math.e
}
def execute(self, expression: str) -> Dict[str, Any]:
"""Evaluate a mathematical expression safely"""
try:
# Parse the expression
node = ast.parse(expression, mode='eval').body
# Evaluate the expression safely
result = self._eval_node(node)
return {
"expression": expression,
"result": result
}
except Exception as e:
return {
"expression": expression,
"error": f"Failed to evaluate expression: {str(e)}"
}
def _eval_node(self, node) -> Union[float, int, list]:
"""Recursively evaluate an AST node"""
# Constants (numbers)
if isinstance(node, ast.Constant):
return node.value
# Names (variables like pi or e)
elif isinstance(node, ast.Name):
if node.id in self.functions:
return self.functions[node.id]
raise NameError(f"Name '{node.id}' is not defined")
# Binary operations (like +, -, *, /)
elif isinstance(node, ast.BinOp):
if type(node.op) not in self.operators:
raise TypeError(f"Unsupported binary operator: {type(node.op).__name__}")
left = self._eval_node(node.left)
right = self._eval_node(node.right)
return self.operators[type(node.op)](left, right)
# Unary operations (like -x)
elif isinstance(node, ast.UnaryOp):
if type(node.op) not in self.operators:
raise TypeError(f"Unsupported unary operator: {type(node.op).__name__}")
operand = self._eval_node(node.operand)
return self.operators[type(node.op)](operand)
# Function calls (like sqrt(4))
elif isinstance(node, ast.Call):
func_name = node.func.id if isinstance(node.func, ast.Name) else None
if func_name not in self.functions:
raise NameError(f"Function '{func_name}' is not defined")
args = [self._eval_node(arg) for arg in node.args]
return self.functions[func_name](*args)
else:
raise TypeError(f"Unsupported node type: {type(node).__name__}")