Skip to content

Commit a07908b

Browse files
committed
Support the python math library
1 parent 5b828b9 commit a07908b

File tree

4 files changed

+36
-2
lines changed

4 files changed

+36
-2
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-server-calculator"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
description = "A Model Context Protocol server for calculating"
55
readme = "README.md"
66
requires-python = ">=3.10"

src/mcp_server_calculator/calculator.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ast
22
import operator
3+
import math
34
from mcp.server.fastmcp import FastMCP
45

56
def evaluate(expression: str) -> str:
@@ -13,17 +14,34 @@ def evaluate(expression: str) -> str:
1314
ast.Pow: operator.pow,
1415
ast.USub: operator.neg,
1516
}
17+
allowed_names = {
18+
k: getattr(math, k)
19+
for k in dir(math)
20+
if not k.startswith("__")
21+
}
22+
allowed_names.update({
23+
"pi": math.pi,
24+
"e": math.e,
25+
})
1626

1727
def eval_expr(node):
1828
if isinstance(node, ast.Constant):
1929
return node.value
30+
elif isinstance(node, ast.Name):
31+
if node.id in allowed_names:
32+
return allowed_names[node.id]
33+
raise ValueError(f"Unknown identifier: {node.id}")
2034
elif isinstance(node, ast.BinOp):
2135
left = eval_expr(node.left)
2236
right = eval_expr(node.right)
2337
if type(node.op) in allowed_operators:
2438
return allowed_operators[type(node.op)](left, right)
2539
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
2640
return -eval_expr(node.operand)
41+
elif isinstance(node, ast.Call):
42+
func = eval_expr(node.func)
43+
args = [eval_expr(arg) for arg in node.args]
44+
return func(*args)
2745
raise ValueError(f"Unsupported operation: {ast.dump(node)}")
2846

2947
expression = expression.replace('^', '**').replace('×', '*').replace('÷', '/')

tests/test_calculator.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
import unittest
23
from src.mcp_server_calculator.calculator import evaluate
34

@@ -79,5 +80,20 @@ def test_modulus_by_zero(self):
7980
with self.assertRaises(ZeroDivisionError):
8081
evaluate("1 % 0")
8182

83+
def test_math_functions(self):
84+
self.assertAlmostEqual(float(evaluate("sin(pi/2)")), math.sin(math.pi/2), places=7)
85+
self.assertAlmostEqual(float(evaluate("cos(0)")), math.cos(0), places=7)
86+
self.assertAlmostEqual(float(evaluate("sqrt(16)")), math.sqrt(16), places=7)
87+
self.assertAlmostEqual(float(evaluate("log(e)")), math.log(math.e), places=7)
88+
self.assertAlmostEqual(float(evaluate("exp(1)")), math.exp(1), places=7)
89+
self.assertAlmostEqual(float(evaluate("tan(0)")), math.tan(0), places=7)
90+
self.assertAlmostEqual(float(evaluate("fabs(-5)")), math.fabs(-5), places=7)
91+
self.assertAlmostEqual(float(evaluate("factorial(5)")), math.factorial(5), places=7)
92+
self.assertAlmostEqual(float(evaluate("pow(2, 5)")), math.pow(2, 5), places=7)
93+
self.assertAlmostEqual(float(evaluate("degrees(pi)")), math.degrees(math.pi), places=7)
94+
self.assertAlmostEqual(float(evaluate("radians(180)")), math.radians(180), places=7)
95+
self.assertAlmostEqual(float(evaluate("pi")), math.pi, places=7)
96+
self.assertAlmostEqual(float(evaluate("e")), math.e, places=7)
97+
8298
if __name__ == '__main__':
8399
unittest.main()

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)