File size: 9,907 Bytes
1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b 1f2d50a 64ced8b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
#!/usr/bin/env python3
"""
MCP Math Calculator Tool - Gradio Implementation
This MCP server provides mathematical calculation capabilities including:
- Basic and advanced mathematical operations
- Statistical calculations
- Expression evaluation
- Data analysis functions
Supports MCP protocol via Gradio interface.
"""
import logging
import math
import os
import re
import statistics
from typing import Any
import gradio as gr
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
import numpy as np
NUMPY_AVAILABLE = True
except ImportError:
NUMPY_AVAILABLE = False
logger.warning("NumPy not available, using basic math operations")
class MathCalculator:
"""Mathematical calculator with advanced functions."""
def __init__(self):
"""Initialize the math calculator."""
self.allowed_names = {
# Basic math functions
"abs": abs, "round": round, "min": min, "max": max,
"sum": sum, "len": len,
# Math module functions
"sin": math.sin, "cos": math.cos, "tan": math.tan,
"asin": math.asin, "acos": math.acos, "atan": math.atan,
"sinh": math.sinh, "cosh": math.cosh, "tanh": math.tanh,
"exp": math.exp, "log": math.log, "log10": math.log10,
"sqrt": math.sqrt, "pow": pow, "factorial": math.factorial,
"floor": math.floor, "ceil": math.ceil,
# Constants
"pi": math.pi, "e": math.e,
# Statistics
"mean": statistics.mean, "median": statistics.median,
"mode": statistics.mode, "stdev": statistics.stdev,
"variance": statistics.variance,
}
if NUMPY_AVAILABLE:
self.allowed_names.update({
"array": np.array, "zeros": np.zeros, "ones": np.ones,
"linspace": np.linspace, "arange": np.arange,
"dot": np.dot, "cross": np.cross,
})
def calculate(self, expression: str) -> str:
"""
Calculate mathematical expression.
Args:
expression: Mathematical expression to evaluate
Returns:
String with calculation results
"""
try:
if not expression.strip():
return "Error: No expression provided"
# Check for data analysis operations
if self._is_data_operation(expression):
return self._handle_data_operation(expression)
# Regular mathematical expression
result = self._evaluate_expression(expression)
# Format result
formatted_result = self._format_result(result)
response = "🧮 CALCULATION RESULT\n"
response += "=" * 30 + "\n"
response += f"Expression: {expression}\n"
response += f"Result: {formatted_result}\n"
# Add additional info for interesting results
if isinstance(result, (int, float)):
response += self._add_number_info(result)
logger.info(f"Successfully calculated: {expression} = {result}")
return response
except Exception as e:
error_msg = f"Calculation error: {e!s}"
logger.error(error_msg)
return error_msg
def _is_data_operation(self, expression: str) -> bool:
"""Check if expression is a data analysis operation."""
data_keywords = ["mean", "median", "stdev", "variance", "sum", "min", "max"]
return any(keyword in expression.lower() for keyword in data_keywords)
def _handle_data_operation(self, expression: str) -> str:
"""Handle data analysis operations."""
try:
# Extract numbers from expression
numbers = re.findall(r"-?\d+\.?\d*", expression)
if not numbers:
return "Error: No numbers found for data analysis"
data = [float(x) for x in numbers]
results = []
results.append("📊 DATA ANALYSIS RESULTS")
results.append("=" * 35)
results.append(f"Dataset: {data}")
results.append(f"Count: {len(data)}")
results.append("")
# Calculate statistics
if len(data) > 0:
results.append("📈 STATISTICS:")
results.append(f" • Sum: {sum(data):.4f}")
results.append(f" • Mean: {statistics.mean(data):.4f}")
results.append(f" • Median: {statistics.median(data):.4f}")
results.append(f" • Min: {min(data):.4f}")
results.append(f" • Max: {max(data):.4f}")
results.append(f" • Range: {max(data) - min(data):.4f}")
if len(data) > 1:
results.append(f" • Std Dev: {statistics.stdev(data):.4f}")
results.append(f" • Variance: {statistics.variance(data):.4f}")
return "\n".join(results)
except Exception as e:
return f"Data analysis error: {e!s}"
def _evaluate_expression(self, expression: str) -> float | int | str:
"""Safely evaluate mathematical expression."""
# Clean the expression
expression = expression.replace("^", "**") # Replace ^ with **
# Remove potentially dangerous characters/functions
dangerous_patterns = ["import", "exec", "eval", "__", "open", "file"]
for pattern in dangerous_patterns:
if pattern in expression.lower():
raise ValueError(f"Potentially dangerous operation: {pattern}")
# Evaluate expression safely
try:
result = eval(expression, {"__builtins__": {}}, self.allowed_names)
return result
except NameError as e:
raise ValueError(f"Unknown function or variable: {e!s}")
except Exception as e:
raise ValueError(f"Invalid expression: {e!s}")
def _format_result(self, result: Any) -> str:
"""Format calculation result for display."""
if isinstance(result, (int, float)):
if abs(result) < 0.0001 or abs(result) > 1000000:
return f"{result:.4e}"
return f"{result:.6f}".rstrip("0").rstrip(".")
if isinstance(result, complex):
return f"{result.real:.4f} + {result.imag:.4f}i"
return str(result)
def _add_number_info(self, number: float) -> str:
"""Add interesting information about the number."""
info = []
if isinstance(number, int):
if number > 1 and self._is_prime(number):
info.append(" ℹ️ This is a prime number!")
if number > 0 and math.sqrt(number) == int(math.sqrt(number)):
info.append(" ℹ️ This is a perfect square!")
if isinstance(number, float):
if abs(number - math.pi) < 0.001:
info.append(" ℹ️ Very close to π!")
elif abs(number - math.e) < 0.001:
info.append(" ℹ️ Very close to e!")
return "\n" + "\n".join(info) if info else ""
def _is_prime(self, n: int) -> bool:
"""Check if a number is prime."""
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
# Initialize the math calculator
math_calculator = MathCalculator()
def calculate_math_mcp(expression: str) -> str:
"""
MCP-compatible mathematical calculation function.
Args:
expression: Mathematical expression to calculate
Returns:
String with formatted calculation results
"""
try:
if not expression.strip():
return "Error: No expression provided"
result = math_calculator.calculate(expression)
return result
except Exception as e:
error_msg = f"Error calculating expression: {e!s}"
logger.error(error_msg)
return error_msg
def create_gradio_interface():
"""Create and configure the Gradio interface."""
interface = gr.Interface(
fn=calculate_math_mcp,
inputs=[
gr.Textbox(
label="Mathematical Expression",
placeholder="Enter expression (e.g., 2^3 + sqrt(16) or mean([1,2,3,4,5]))",
lines=2
)
],
outputs=[
gr.Textbox(
label="Calculation Result",
lines=15,
show_copy_button=True
)
],
title="🧮 MCP Math Calculator",
description="""
**Mathematical Calculation MCP Server**
Perform various calculations:
- Basic arithmetic (+ - * / ^ %)
- Advanced functions (sin, cos, tan, log, sqrt, etc.)
- Statistical analysis (mean, median, stdev, etc.)
- Data operations on lists of numbers
Examples: `2^3 + sqrt(16)`, `sin(pi/2)`, `mean([1,2,3,4,5])`
""",
examples=[
["2^3 + sqrt(16)"],
["sin(pi/2) + cos(0)"],
["mean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])"],
["factorial(5) / (2^3)"],
["log(e^2) + sqrt(25)"]
],
allow_flagging="never",
analytics_enabled=False
)
return interface
def main():
"""Main function to run the Gradio app."""
port = int(os.getenv("GRADIO_SERVER_PORT", 7860))
host = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
logger.info(f"Starting MCP Math Calculator on {host}:{port}")
interface = create_gradio_interface()
interface.launch(
server_name=host,
server_port=port,
share=False,
debug=False,
quiet=False,
show_error=True
)
if __name__ == "__main__":
main()
|