# Directory Structure ``` ├── .gitignore ├── .python-version ├── Dockerfile ├── LICENSE ├── pyproject.toml ├── README_CN.md ├── README.md ├── server.py ├── smithery.yaml └── test └── test_server.py ``` # Files -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- ``` 1 | 3.11 2 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | __pycache__/ 2 | *.pyc 3 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Calculate Server 2 | 3 | [](https://smithery.ai/server/@611711Dark/mcp_calculate_server) 4 | 5 | A mathematical calculation service based on MCP protocol and SymPy library, providing powerful symbolic computation capabilities. 6 | 7 | ## Key Features 8 | 9 | - **Basic Operations**: Addition, subtraction, multiplication, division, exponentiation 10 | - **Algebraic Operations**: Expression expansion, factorization, simplification 11 | - **Calculus**: Differentiation, integration (definite/indefinite), limit calculation 12 | - **Equation Solving**: Algebraic equations, systems of equations 13 | - **Matrix Operations**: Matrix inversion, eigenvalues/eigenvectors calculation 14 | - **Series Expansion**: Taylor series expansion 15 | - **Special Functions**: Trigonometric, logarithmic, exponential functions 16 | 17 | ## Usage Examples 18 | 19 | ```python 20 | # Basic operations 21 | "2 + 3*5" → 17 22 | 23 | # Algebraic operations 24 | "expand((x + 1)**2)" → x² + 2x + 1 25 | "factor(x**2 - 2*x - 15)" → (x - 5)(x + 3) 26 | 27 | # Calculus 28 | "diff(sin(x), x)" → cos(x) 29 | "integrate(exp(x), (x, 0, 1))" → E - 1 30 | "limit(tan(x)/x, x, 0)" → 1 31 | 32 | # Equation solving 33 | "solve(x**2 - 4, x)" → [-2, 2] 34 | "solve([x**2 + y**2 - 1, x + y - 1], [x, y])" → [(0, 1), (1, 0)] 35 | 36 | # Matrix operations 37 | "Matrix([[1, 2], [3, 4]]).inv()" → [[-2, 1], [3/2, -1/2]] 38 | "Matrix([[1, 2, 3], [4, 5, 6]]).eigenvals()" → {9/2 - sqrt(33)/2: 1, 9/2 + sqrt(33)/2: 1} 39 | ``` 40 | 41 | ## Installation 42 | 43 | ### Installing via Smithery 44 | 45 | To install Calculate Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@611711Dark/mcp_calculate_server): 46 | 47 | ```bash 48 | npx -y @smithery/cli install @611711Dark/mcp_sympy_calculate_server --client claude 49 | ``` 50 | 51 | ### Local Installation 52 | 53 | 1. Clone repository: 54 | ```bash 55 | git clone https://github.com/611711Dark/mcp-calculate-server.git 56 | cd mcp-calculate-server 57 | ``` 58 | 59 | 2. Create virtual environment and install dependencies: 60 | ```bash 61 | uv venv 62 | source .venv/bin/activate 63 | uv pip install -e . 64 | ``` 65 | 66 | 3. Configuration: 67 | ```json 68 | "calculate_expression1": { 69 | "isActive": false, 70 | "command": "uv", 71 | "args": [ 72 | "run", 73 | "--directory", 74 | "/path/to/mcp_calculate_server", 75 | "server.py" 76 | ], 77 | } 78 | ``` 79 | 80 | ## API Usage 81 | 82 | Call `calculate_expression` tool via MCP protocol by passing mathematical expression string, returns computation result. 83 | 84 | ## Dependencies 85 | 86 | - mcp>=1.5.0 87 | - sympy>=1.13.3 88 | - fastapi>=0.95.0 89 | - uvicorn>=0.21.0 90 | 91 | ## License 92 | 93 | This project is licensed under MIT License. See [LICENSE](LICENSE) file. 94 | 95 | [中文版本](README_CN.md) 96 | ``` -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- ```toml 1 | [project] 2 | name = "mcp-calcualte-server" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.11" 7 | dependencies = [ 8 | "mcp>=1.5.0", 9 | "sympy>=1.13.3", 10 | ] 11 | ``` -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- ```yaml 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | {} 8 | commandFunction: 9 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 10 | |- 11 | (config) => ({ command: 'python', args: ['server.py'] }) 12 | exampleConfig: {} 13 | ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM python:3.11-alpine 3 | 4 | # Install build dependencies 5 | RUN apk add --no-cache build-base 6 | 7 | WORKDIR /app 8 | 9 | # Copy project files into the container 10 | COPY . . 11 | 12 | # Upgrade pip and install the project with its dependencies 13 | RUN pip install --upgrade pip \ 14 | && pip install . 15 | 16 | # Expose port if needed (not required for mcp via stdio) 17 | 18 | # Run the MCP server 19 | CMD ["python", "server.py"] 20 | ``` -------------------------------------------------------------------------------- /test/test_server.py: -------------------------------------------------------------------------------- ```python 1 | import unittest 2 | import sys 3 | import os 4 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 5 | from server import calculate_expression 6 | 7 | class TestCalculateExpression(unittest.TestCase): 8 | def test_basic_operations(self): 9 | """测试基础运算""" 10 | self.assertEqual(calculate_expression("2 + 3*5"), "17") 11 | self.assertEqual(calculate_expression("10 / 2"), "5.0") 12 | self.assertEqual(calculate_expression("2**8"), "256") 13 | 14 | def test_algebraic_operations(self): 15 | """测试代数运算""" 16 | self.assertEqual(calculate_expression("expand((x + 1)**2)"), "x**2 + 2.0*x + 1.0") 17 | self.assertEqual(calculate_expression("factor(x**2 - 2*x - 15)"), "(x - 5.0)*(x + 3.0)") 18 | self.assertEqual(calculate_expression("simplify((x**2 - 1)/(x + 1))"), "x - 1.0") 19 | 20 | def test_calculus(self): 21 | """测试微积分""" 22 | self.assertEqual(calculate_expression("diff(sin(x), x)"), "cos(x)") 23 | self.assertEqual(calculate_expression("integrate(exp(x), x)"), "exp(x)") 24 | self.assertEqual(calculate_expression("limit(tan(x)/x, x, 0)"), "1.00000000000000") 25 | 26 | def test_equation_solving(self): 27 | """测试方程求解""" 28 | self.assertEqual(calculate_expression("solve(x**2 - 4, x)"), "[-2.00000000000000, 2.00000000000000]") 29 | self.assertEqual(calculate_expression("solve([x + y - 1, x - y - 1], [x, y])"), "{x: 1, y: 0}") 30 | 31 | def test_matrix_operations(self): 32 | """测试矩阵运算""" 33 | self.assertEqual(calculate_expression("Matrix([[1, 2], [3, 4]]).inv()"), 34 | "Matrix([[-2.00000000000000, 1.00000000000000], [1.50000000000000, -0.500000000000000]])") 35 | self.assertEqual(calculate_expression("Matrix([[1, 2], [3, 4]]).det()"), "-2.00000000000000") 36 | 37 | def test_error_handling(self): 38 | """测试错误处理""" 39 | self.assertTrue(calculate_expression("1 / 0").startswith("Error:")) 40 | self.assertTrue(calculate_expression("invalid expression").startswith("Error:")) 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | ``` -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- ```python 1 | from mcp.server.fastmcp import FastMCP 2 | import sympy as sp 3 | from sympy import symbols, Matrix, sympify 4 | import re 5 | 6 | # Create MCP server 7 | mcp = FastMCP("MathTool") 8 | 9 | # Add tool for calculating expressions 10 | @mcp.tool() 11 | def calculate_expression(expression: str) -> str: 12 | """ 13 | calculate mathematical expressions using the `sympify` function from `sympy`, parse and compute the input mathematical expression string, supports direct calls to SymPy functions (automatically recognizes x, y, z as symbolic variables) 14 | Parameters: 15 | expression (str): Mathematical expression, e.g., "223 - 344 * 6" or "sin(pi/2) + log(10)".Replace special symbols with approximate values, e.g., pi → 3.1415" 16 | Example expressions: 17 | "2 + 3*5" # Basic arithmetic → 17 18 | "expand((x + 1)**2)" # Expand → x² + 2x + 1 19 | "diff(sin(x), x)" # Derivative → cos(x) 20 | "integrate(exp(x), (x, 0, 1))" # Definite integral → E - 1 21 | "solve(x**2 - 4, x)" # Solve equation → [-2, 2] 22 | "limit(tan(x)/x, x, 0)" # Limit → 1 23 | "Sum(k, (k, 1, 10)).doit()" # Summation → 55 24 | "Matrix([[1, 2], [3, 4]]).inv()" # Matrix inverse → [[-2, 1], [3/2, -1/2]] 25 | "simplify((x**2 - 1)/(x + 1))" # Simplify → x - 1 26 | "factor(x**2 - 2*x - 15)" # Factorize → (x - 5)(x + 3) 27 | "series(cos(x), x, 0, 4)" # Taylor series → 1 - x²/2 + x⁴/24 + O(x⁴) 28 | "integrate(exp(-x**2)*sin(x), (x, -oo, oo))" # Complex integral 29 | "solve([x**2 + y**2 - 1, x + y - 1], [x, y])" # Solve system of equations 30 | "Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).eigenvals()" # Matrix eigenvalues 31 | Returns: 32 | str: Calculation result. If the expression cannot be parsed or computed, returns an error message (str). 33 | """ 34 | try: 35 | # Define common symbolic variables 36 | x, y, z = sp.symbols('x y z') 37 | 38 | # Create local namespace containing all sympy functions and symbolic variables 39 | locals_dict = {**sp.__dict__, 'x': x, 'y': y, 'z': z} 40 | 41 | # Special handling for various types of expressions 42 | 43 | # 1. Handle complex integral expressions 44 | if "integrate" in expression and ("oo" in expression or "-oo" in expression): 45 | return handle_complex_integration(expression, locals_dict) 46 | 47 | # 2. Handle system of equations solving expressions 48 | elif "solve(" in expression and "[" in expression and "]" in expression: 49 | return handle_equation_solving(expression, locals_dict) 50 | 51 | # 3. Handle matrix eigenvalue calculation expressions 52 | elif "eigenvals" in expression or "eigenvects" in expression: 53 | return handle_matrix_eigenvalues(expression, locals_dict) 54 | 55 | # 4. General expression calculation 56 | else: 57 | # First try to evaluate the expression directly 58 | result = eval(expression, globals(), locals_dict) 59 | 60 | # Process based on result type 61 | return format_result(result) 62 | 63 | except Exception as e: 64 | return f"Error: {e}" 65 | 66 | def handle_complex_integration(expression, locals_dict): 67 | """Handle complex integral expressions""" 68 | try: 69 | # Check if it's an infinite integral 70 | if "-oo" in expression or "oo" in expression: 71 | # Try symbolic computation 72 | expr = eval(expression, globals(), locals_dict) 73 | 74 | # If it's an integral object but not computed 75 | if isinstance(expr, sp.Integral): 76 | try: 77 | # Try to perform the integral 78 | result = expr.doit() 79 | 80 | # Try to compute numerical result 81 | try: 82 | numerical = result.evalf() 83 | return str(numerical) 84 | except: 85 | return str(result) 86 | except Exception as e: 87 | # If symbolic integration fails, try alternative methods 88 | try: 89 | # Extract integral expression information 90 | match = re.search(r"integrate\((.*?), \((.*?), (.*?), (.*?)\)\)", expression) 91 | if match: 92 | integrand, var, lower, upper = match.groups() 93 | 94 | # For infinite integrals, use finite approximation 95 | if (lower == "-oo" or lower == "oo") or (upper == "oo" or upper == "-oo"): 96 | # Replace infinity with a large value 97 | if lower == "-oo": 98 | lower = "-100" 99 | elif lower == "oo": 100 | lower = "100" 101 | 102 | if upper == "-oo": 103 | upper = "-100" 104 | elif upper == "oo": 105 | upper = "100" 106 | 107 | # Build finite range integral expression 108 | finite_expr = f"integrate({integrand}, ({var}, {lower}, {upper}))" 109 | result = eval(finite_expr, globals(), locals_dict) 110 | 111 | try: 112 | numerical = result.evalf() 113 | return f"Approximate numerical result: {numerical} (using finite range integral)" 114 | except: 115 | return f"Approximate result: {result} (using finite range integral)" 116 | except Exception as e2: 117 | return f"Integration error: {e}, finite approximation failed: {e2}" 118 | 119 | # Try to compute result directly 120 | try: 121 | numerical = expr.evalf() 122 | return str(numerical) 123 | except: 124 | return str(expr) 125 | 126 | # Regular integral 127 | result = eval(expression, globals(), locals_dict) 128 | return format_result(result) 129 | 130 | except Exception as e: 131 | return f"Integration error: {e}" 132 | 133 | def handle_equation_solving(expression, locals_dict): 134 | """Handle system of equations solving expressions""" 135 | try: 136 | # Compute result 137 | result = eval(expression, globals(), locals_dict) 138 | 139 | # Format result 140 | return format_result(result) 141 | 142 | except Exception as e: 143 | return f"Equation solving error: {e}" 144 | 145 | def handle_matrix_eigenvalues(expression, locals_dict): 146 | """Handle matrix eigenvalue calculation expressions""" 147 | try: 148 | # Extract matrix expression 149 | matrix_expr = expression.split(".eigen")[0] 150 | operation = "eigenvals" if "eigenvals" in expression else "eigenvects" 151 | 152 | # Compute matrix 153 | matrix = eval(matrix_expr, globals(), locals_dict) 154 | 155 | # Compute eigenvalues or eigenvectors 156 | if operation == "eigenvals": 157 | result = matrix.eigenvals() 158 | else: 159 | result = matrix.eigenvects() 160 | 161 | # Format result 162 | return format_result(result) 163 | 164 | except Exception as e: 165 | return f"Matrix eigenvalue calculation error: {e}" 166 | 167 | def format_result(result): 168 | """Format output based on result type""" 169 | try: 170 | # Handle dictionary type results (e.g., eigenvalues) 171 | if isinstance(result, dict): 172 | formatted = "{" 173 | for key, value in result.items(): 174 | # Try numerical computation 175 | try: 176 | key_eval = key.evalf() 177 | except: 178 | key_eval = key 179 | 180 | formatted += f"{key_eval}: {value}, " 181 | 182 | if formatted.endswith(", "): 183 | formatted = formatted[:-2] 184 | 185 | formatted += "}" 186 | return formatted 187 | 188 | # Handle list type results (e.g., solutions to equations) 189 | elif isinstance(result, list): 190 | formatted = "[" 191 | for item in result: 192 | # Check if it's a tuple (e.g., coordinate points) 193 | if isinstance(item, tuple): 194 | coords = [] 195 | for val in item: 196 | # Try numerical computation 197 | try: 198 | val_eval = val.evalf() 199 | coords.append(str(val_eval)) 200 | except: 201 | coords.append(str(val)) 202 | 203 | formatted += "(" + ", ".join(coords) + "), " 204 | else: 205 | # Try numerical computation 206 | try: 207 | item_eval = item.evalf() 208 | formatted += f"{item_eval}, " 209 | except: 210 | formatted += f"{item}, " 211 | 212 | if formatted.endswith(", "): 213 | formatted = formatted[:-2] 214 | 215 | formatted += "]" 216 | return formatted 217 | 218 | # Other types of results 219 | else: 220 | # Try numerical computation 221 | try: 222 | return str(result.evalf()) 223 | except: 224 | return str(result) 225 | 226 | except Exception as e: 227 | return f"Result formatting error: {e}, original result: {result}" 228 | 229 | if __name__ == "__main__": 230 | mcp.run() 231 | ```