Skip to main content
Glama
by apetta
calculus.py11 kB
"""Calculus tools using SymPy for symbolic computation.""" from typing import Annotated, Literal, Union from pydantic import Field from mcp.types import ToolAnnotations from sympy import sympify, diff, integrate, limit, series, Symbol, oo, N, lambdify import scipy.integrate as integrate_numeric from ..server import mcp from ..core import format_result @mcp.tool( name="derivative", description="""Compute symbolic and numerical derivatives with support for higher orders and partial derivatives. Examples: FIRST DERIVATIVE: expression="x^3 + 2*x^2", variable="x", order=1 Result: derivative="3*x^2 + 4*x" SECOND DERIVATIVE (acceleration/concavity): expression="x^3", variable="x", order=2 Result: derivative="6*x" EVALUATE AT POINT: expression="sin(x)", variable="x", order=1, point=0 Result: derivative="cos(x)", value_at_point=1.0 PRODUCT RULE: expression="sin(x)*cos(x)", variable="x", order=1 Result: derivative="cos(x)^2 - sin(x)^2" PARTIAL DERIVATIVE: expression="x^2*y", variable="y", order=1 Result: derivative="x^2" (treating x as constant)""", annotations=ToolAnnotations( title="Derivative Calculator", readOnlyHint=True, idempotentHint=True, ), ) async def derivative( expression: Annotated[ str, Field( description="Mathematical expression to differentiate (e.g., 'x^3 + 2*x^2', 'sin(x)')", min_length=1, ), ], variable: Annotated[ str, Field( description="Variable to differentiate with respect to (e.g., 'x', 't')", min_length=1 ), ], order: Annotated[ int, Field(description="Derivative order (1=first derivative, 2=second, etc.)", ge=1) ] = 1, point: Annotated[ float | None, Field(description="Optional point for numerical evaluation of the derivative") ] = None, ) -> str: """Compute symbolic derivatives using SymPy. Supports higher orders and partial derivatives. Optional numerical evaluation at a point.""" try: expr = sympify(expression) var = Symbol(variable) # Compute derivative derivative_expr = diff(expr, var, order) derivative_str = str(derivative_expr) # Build metadata metadata = { "expression": expression, "variable": variable, "order": order, } # Evaluate at point if provided if point is not None: value = float(N(derivative_expr.subs(var, point))) metadata["value_at_point"] = value metadata["point"] = point return format_result(derivative_str, metadata) except Exception as e: raise ValueError( f"Derivative calculation failed: {str(e)}. Example: expression='x^2', variable='x'" ) @mcp.tool( name="integral", description="""Compute symbolic and numerical integrals (definite and indefinite). Examples: INDEFINITE INTEGRAL (antiderivative): expression="x^2", variable="x" Result: "x^3/3" DEFINITE INTEGRAL (area): expression="x^2", variable="x", lower_bound=0, upper_bound=1 Result: 0.333 TRIGONOMETRIC: expression="sin(x)", variable="x", lower_bound=0, upper_bound=3.14159 Result: 2.0 (area under one period) NUMERICAL METHOD (non-elementary): expression="exp(-x^2)", variable="x", lower_bound=0, upper_bound=1, method="numerical" Result: 0.746824 (Gaussian integral approximation) SYMBOLIC ANTIDERIVATIVE: expression="1/x", variable="x" Result: "log(x)" """, annotations=ToolAnnotations( title="Integral Calculator", readOnlyHint=True, idempotentHint=True, ), ) async def integral( expression: Annotated[ str, Field( description="Mathematical expression to integrate (e.g., 'x^2', 'sin(x)')", min_length=1 ), ], variable: Annotated[ str, Field(description="Integration variable (e.g., 'x', 't')", min_length=1) ], lower_bound: Annotated[ float | None, Field(description="Lower bound for definite integral (omit for indefinite)") ] = None, upper_bound: Annotated[ float | None, Field(description="Upper bound for definite integral (omit for indefinite)") ] = None, method: Annotated[ Literal["symbolic", "numerical"], Field( description="Integration method: symbolic=exact/analytical, numerical=approximate (requires bounds)" ), ] = "symbolic", ) -> str: """Compute integrals using SymPy (symbolic/exact) or SciPy (numerical/approximate). Supports indefinite (antiderivatives) and definite (area) integrals.""" try: expr = sympify(expression) var = Symbol(variable) is_definite = lower_bound is not None and upper_bound is not None if method == "symbolic": if is_definite: # Definite symbolic integral result = integrate(expr, (var, lower_bound, upper_bound)) numeric_value = float(N(result)) metadata = { "expression": expression, "variable": variable, "lower_bound": lower_bound, "upper_bound": upper_bound, "symbolic_result": str(result), "type": "definite", } return format_result(numeric_value, metadata) else: # Indefinite symbolic integral result = integrate(expr, var) antiderivative_str = str(result) metadata = { "expression": expression, "variable": variable, "type": "indefinite", } return format_result(antiderivative_str, metadata) elif method == "numerical": if not is_definite: raise ValueError("Numerical integration requires lower_bound and upper_bound") # Convert SymPy expression to numeric function func = lambdify(var, expr, "numpy") # Use SciPy's quad for numerical integration result, error = integrate_numeric.quad(func, lower_bound, upper_bound) metadata = { "expression": expression, "variable": variable, "lower_bound": lower_bound, "upper_bound": upper_bound, "error_estimate": float(error), "method": "numerical", "type": "definite", } return format_result(float(result), metadata) else: raise ValueError(f"Unknown method: {method}") except Exception as e: raise ValueError( f"Integration failed: {str(e)}. " f"Example: expression='x^2', variable='x', lower_bound=0, upper_bound=1" ) @mcp.tool( name="limits_series", description="""Compute limits and series expansions using SymPy. Examples: CLASSIC LIMIT: expression="sin(x)/x", variable="x", point=0, operation="limit" Result: limit=1 LIMIT AT INFINITY: expression="1/x", variable="x", point="oo", operation="limit" Result: limit=0 ONE-SIDED LIMIT: expression="1/x", variable="x", point=0, operation="limit", direction="+" Result: limit=+∞ (approaching from right) REMOVABLE DISCONTINUITY: expression="(x^2-1)/(x-1)", variable="x", point=1, operation="limit" Result: limit=2 MACLAURIN SERIES (at 0): expression="exp(x)", variable="x", point=0, operation="series", order=4 Result: "1 + x + x^2/2 + x^3/6 + O(x^4)" TAYLOR SERIES (at point): expression="sin(x)", variable="x", point=3.14159, operation="series", order=4 Result: expansion around π""", annotations=ToolAnnotations( title="Limits and Series", readOnlyHint=True, idempotentHint=True, ), ) async def limits_series( expression: Annotated[ str, Field( description="Mathematical expression to analyse (e.g., 'sin(x)/x', 'exp(x)')", min_length=1, ), ], variable: Annotated[ str, Field(description="Variable for limit/expansion (e.g., 'x', 't')", min_length=1) ], point: Annotated[ Union[float, str], Field( description="Point for limit/expansion (number, 'oo' for infinity, '-oo' for -infinity)" ), ], operation: Annotated[ Literal["limit", "series"], Field(description="Operation: limit=compute limit, series=Taylor/Maclaurin expansion"), ] = "limit", order: Annotated[int, Field(description="Series expansion order (number of terms)", ge=1)] = 6, direction: Annotated[ Literal["+", "-", "+-"], Field(description="Limit direction: +=from right, -=from left, +-=both sides"), ] = "+-", ) -> str: """Compute limits (lim[x→a]f(x)) and Taylor/Maclaurin series expansions using SymPy. Handles infinity, one-sided limits, removable discontinuities.""" try: expr = sympify(expression) var = Symbol(variable) # Handle infinity if point == "oo" or point == "inf": point_sym = oo elif point == "-oo" or point == "-inf": point_sym = -oo else: point_sym = float(point) if operation == "limit": if direction == "+-": # Two-sided limit result = limit(expr, var, point_sym) else: # One-sided limit result = limit(expr, var, point_sym, direction) # Limit value is the primary result limit_str = str(result) metadata = { "expression": expression, "variable": variable, "point": str(point), "direction": direction, "numeric_value": float(N(result)) if result.is_number else None, } return format_result(limit_str, metadata) elif operation == "series": # Series expansion series_expr = series(expr, var, point_sym, order) # type: ignore[arg-type] series_str = str(series_expr) # Remove O() term for cleaner output series_no_o = series_expr.removeO() # Series string is the primary result metadata = { "expression": expression, "variable": variable, "point": str(point), "order": order, "series_without_O": str(series_no_o), } return format_result(series_str, metadata) else: raise ValueError(f"Unknown operation: {operation}") except Exception as e: raise ValueError( f"Limit/series calculation failed: {str(e)}. " f"Example: expression='sin(x)/x', variable='x', point=0, operation='limit'" )

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/apetta/vibe-math-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server