Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
350cd336d2 | ||
|
|
e4b5ea649f | ||
|
|
a17b0c8017 |
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM docker.io/library/python:3.12-slim
|
||||
LABEL org.opencontainers.image.title="calcmcp" \
|
||||
org.opencontainers.image.description="Scientific calculator MCP server (SymPy/NumPy/SciPy/pandas), stdio transport" \
|
||||
org.opencontainers.image.source="https://mysources.co.uk/michal/CalcMCP"
|
||||
WORKDIR /app
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY calculator.py mcp_server.py ./
|
||||
USER nobody
|
||||
ENTRYPOINT ["python", "-u", "mcp_server.py"]
|
||||
19
README.md
19
README.md
@@ -1,9 +1,26 @@
|
||||
# Scientific Calculator MCP Server
|
||||
|
||||
> A production-ready Model Context Protocol (MCP) server providing advanced mathematical calculation capabilities for AI models. Supports symbolic math (SymPy), numerical computing (NumPy/SciPy), data analysis (pandas), and image processing.
|
||||
> A Model Context Protocol (MCP) server providing advanced mathematical calculation capabilities for AI models. Supports symbolic math (SymPy), numerical computing (NumPy/SciPy), data analysis (pandas), and image processing.
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
> **Fork notice** — this is a patched fork of
|
||||
> [thinkitpossible/CalcMCP](https://github.com/thinkitpossible/CalcMCP).
|
||||
> Changes relative to upstream:
|
||||
>
|
||||
> - **MCP compliance**: tools are published with a proper `inputSchema` (upstream used a
|
||||
> non-standard `params` key, so clients saw no parameters at all); JSON-RPC notifications
|
||||
> (`notifications/initialized`) are no longer answered with an error; tool failures set
|
||||
> `isError: true` on the result.
|
||||
> - **Tool descriptions rewritten in English**, with per-parameter docs and inline examples.
|
||||
> - **Security**: removed all raw `eval()` of tool arguments (matrix inputs now parse via
|
||||
> `json.loads`/`ast.literal_eval`, the numeric-ODE right-hand side via `sympy.lambdify`).
|
||||
> - **Bug fixes**: eigenvalue/eigenvector results with complex values no longer crash JSON
|
||||
> serialization (rendered as `a+bi` strings); `solve` accepts equations written with `=`;
|
||||
> one-sample t-test accepts `params` as `{"popmean": ...}` or a bare number.
|
||||
> - **New**: `symbolic_tool` operation `dsolve` — symbolic ODE solving
|
||||
> (e.g. `Derivative(y(x), x, 2) + y(x) = 0` → `y(x) = C1*sin(x) + C2*cos(x)`).
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
415
README_ZH.md
Normal file
415
README_ZH.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# Scientific Calculator MCP 服务器
|
||||
|
||||
> 一个生产级别的 Model Context Protocol(MCP)服务器,为 AI 模型提供高级数学计算功能。支持符号数学(SymPy)、数值计算(NumPy/SciPy)、数据分析(pandas)和图像处理。
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.python.org/downloads/)
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [快速开始](#快速开始)
|
||||
- [安装指南](#安装指南)
|
||||
- [配置说明](#配置说明)
|
||||
- [使用示例](#使用示例)
|
||||
- [工具参考](#工具参考)
|
||||
- [常见问题](#常见问题)
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 第一步:安装依赖
|
||||
|
||||
```bash
|
||||
pip install sympy numpy scipy pandas
|
||||
```
|
||||
|
||||
### 第二步:配置 Claude Desktop
|
||||
|
||||
编辑 Claude Desktop 的配置文件 `claude_desktop_config.json`:
|
||||
|
||||
**Windows 用户:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"scientific-calculator": {
|
||||
"command": "python",
|
||||
"args": ["-u", "F:\\AAchengguoofAI\\cuz_caculat\\mcp_server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**macOS/Linux 用户:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"scientific-calculator": {
|
||||
"command": "python3",
|
||||
"args": ["-u", "/path/to/mcp_server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第三步:重启 Claude Desktop
|
||||
|
||||
在 Claude 中就能使用计算工具了。
|
||||
|
||||
---
|
||||
|
||||
## 📦 安装指南
|
||||
|
||||
### 方式 1:通过 pip 安装(推荐)
|
||||
|
||||
```bash
|
||||
pip install mcp-scientific-calculator
|
||||
```
|
||||
|
||||
### 方式 2:从源码安装
|
||||
|
||||
```bash
|
||||
git clone https://github.com/thinkitpossible/CalcMCP
|
||||
cd CalcMCP
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 方式 3:本地开发安装
|
||||
|
||||
```bash
|
||||
git clone https://github.com/thinkitpossible/CalcMCP
|
||||
cd CalcMCP
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 基础配置
|
||||
|
||||
确保在 `claude_desktop_config.json` 中配置了服务器路径:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"scientific-calculator": {
|
||||
"command": "python",
|
||||
"args": ["-u", "path/to/mcp_server.py"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 环境变量(可选)
|
||||
|
||||
```bash
|
||||
# 设置日志级别(可选)
|
||||
export LOG_LEVEL=DEBUG
|
||||
|
||||
# Windows PowerShell
|
||||
$env:LOG_LEVEL = "DEBUG"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 使用示例
|
||||
|
||||
### 1. 符号数学运算
|
||||
|
||||
**简化表达式:**
|
||||
```
|
||||
用户:简化 (x**2 + 2*x + 1) / (x + 1)
|
||||
工具调用:symbolic_tool
|
||||
参数:
|
||||
operation: "simplify"
|
||||
expression: "(x**2 + 2*x + 1) / (x + 1)"
|
||||
|
||||
返回:x + 1
|
||||
```
|
||||
|
||||
**求导数:**
|
||||
```
|
||||
用户:求 sin(x) + x^3 对 x 的导数
|
||||
工具调用:symbolic_tool
|
||||
参数:
|
||||
operation: "derivative"
|
||||
expression: "sin(x) + x**3"
|
||||
variable: "x"
|
||||
|
||||
返回:cos(x) + 3*x**2
|
||||
```
|
||||
|
||||
**求积分:**
|
||||
```
|
||||
用户:求 2*x 的不定积分
|
||||
工具调用:symbolic_tool
|
||||
参数:
|
||||
operation: "integral"
|
||||
expression: "2*x"
|
||||
variable: "x"
|
||||
|
||||
返回:x**2
|
||||
```
|
||||
|
||||
**解方程:**
|
||||
```
|
||||
用户:解方程 x^2 - 5*x + 6 = 0
|
||||
工具调用:symbolic_tool
|
||||
参数:
|
||||
operation: "solve_equation"
|
||||
equation: "x**2 - 5*x + 6"
|
||||
variable: "x"
|
||||
|
||||
返回:[2, 3]
|
||||
```
|
||||
|
||||
### 2. 数值计算
|
||||
|
||||
**线性方程组求解:**
|
||||
```
|
||||
用户:求解线性方程组:2x + y = 5, x + 3y = 7
|
||||
工具调用:numpy_tool
|
||||
参数:
|
||||
operation: "linear_algebra"
|
||||
array_data: [[2, 1], [1, 3]]
|
||||
array_data_b: [5, 7]
|
||||
|
||||
返回:[2.0, 1.0]
|
||||
```
|
||||
|
||||
**矩阵运算:**
|
||||
```
|
||||
用户:计算矩阵 [[1, 2], [3, 4]] 的行列式
|
||||
工具调用:numpy_tool
|
||||
参数:
|
||||
operation: "determinant"
|
||||
array_data: [[1, 2], [3, 4]]
|
||||
|
||||
返回:-2.0
|
||||
```
|
||||
|
||||
### 3. 科学计算
|
||||
|
||||
**常微分方程求解:**
|
||||
```
|
||||
用户:求解 dy/dx = 2*x,初始条件 y(0)=0
|
||||
工具调用:scipy_tool
|
||||
参数:
|
||||
operation: "solve_ode"
|
||||
expression: "2*x"
|
||||
initial_condition: 0
|
||||
|
||||
返回:数值解向量
|
||||
```
|
||||
|
||||
**函数积分:**
|
||||
```
|
||||
用户:计算 ∫sin(x)dx 从 0 到 π
|
||||
工具调用:scipy_tool
|
||||
参数:
|
||||
operation: "integrate"
|
||||
expression: "sin(x)"
|
||||
limits: "0:pi"
|
||||
|
||||
返回:2.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 工具参考
|
||||
|
||||
### symbolic_tool(符号数学)
|
||||
|
||||
提供以下操作:
|
||||
|
||||
| 操作 | 参数 | 返回 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `simplify` | expression | str | 简化数学表达式 |
|
||||
| `expand` | expression | str | 展开表达式 |
|
||||
| `factor` | expression | str | 因式分解 |
|
||||
| `derivative` | expression, variable, order | str | 求导数(可指定阶数) |
|
||||
| `integral` | expression, variable, lower_limit, upper_limit | str | 求积分(定积分或不定积分) |
|
||||
| `limit` | expression, variable, point | str | 求极限 |
|
||||
| `solve_equation` | equation, variable | str | 解方程 |
|
||||
| `matrix_multiply` | matrix1, matrix2 | str | 矩阵乘法 |
|
||||
| `matrix_inverse` | matrix | str | 矩阵求逆 |
|
||||
|
||||
**使用示例:**
|
||||
```json
|
||||
{
|
||||
"tool": "symbolic_tool",
|
||||
"input": {
|
||||
"operation": "simplify",
|
||||
"expression": "x**2 + 2*x + 1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### numpy_tool(数值计算)
|
||||
|
||||
提供以下操作:
|
||||
|
||||
| 操作 | 参数 | 返回 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `linear_algebra` | array_data, array_data_b | list | 求解线性方程组 Ax=b |
|
||||
| `determinant` | array_data | float | 计算矩阵行列式 |
|
||||
| `eigenvalues` | array_data | str | 计算特征值 |
|
||||
| `eigenvectors` | array_data | str | 计算特征向量 |
|
||||
| `statistics` | array_data | dict | 统计信息(均值、标准差等) |
|
||||
| `fft` | array_data | list | 快速傅里叶变换 |
|
||||
| `correlation` | array1, array2 | float | 计算两个数组的相关系数 |
|
||||
| `pandas_read` | file_path, file_type | str | 读取数据文件 |
|
||||
| `pandas_describe` | file_path | str | 数据摘要统计 |
|
||||
|
||||
**使用示例:**
|
||||
```json
|
||||
{
|
||||
"tool": "numpy_tool",
|
||||
"input": {
|
||||
"operation": "linear_algebra",
|
||||
"array_data": [[2, 1], [1, 3]],
|
||||
"array_data_b": [5, 7]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### scipy_tool(科学计算)
|
||||
|
||||
提供以下操作:
|
||||
|
||||
| 操作 | 参数 | 返回 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `solve_ode` | expression, initial_condition, t_span | str | 求解常微分方程 |
|
||||
| `integrate` | expression, limits | float | 数值积分 |
|
||||
| `optimize` | expression, initial_guess | str | 函数优化 |
|
||||
| `interpolate` | x_data, y_data, x_new | str | 插值 |
|
||||
| `fft` | array_data | list | 快速傅里叶变换 |
|
||||
| `root_find` | expression, initial_guess | float | 求根 |
|
||||
| `curve_fit` | x_data, y_data | str | 曲线拟合 |
|
||||
| `special_function` | func_type, value | float | 特殊函数计算 |
|
||||
|
||||
**使用示例:**
|
||||
```json
|
||||
{
|
||||
"tool": "scipy_tool",
|
||||
"input": {
|
||||
"operation": "integrate",
|
||||
"expression": "sin(x)",
|
||||
"limits": "0:pi"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q1:如何添加到 Claude Desktop?
|
||||
|
||||
**A:**
|
||||
1. 打开 Claude Desktop 配置文件夹
|
||||
- **Windows**: `%APPDATA%\Claude\`
|
||||
- **macOS**: `~/Library/Application Support/Claude/`
|
||||
- **Linux**: `~/.config/Claude/`
|
||||
|
||||
2. 编辑 `claude_desktop_config.json`
|
||||
|
||||
3. 在 `mcpServers` 字段添加配置
|
||||
|
||||
4. 保存并重启 Claude Desktop
|
||||
|
||||
### Q2:提示 "Python not found" 怎么办?
|
||||
|
||||
**A:** 确保 Python 已正确安装并在系统 PATH 中:
|
||||
```bash
|
||||
python --version
|
||||
# 或
|
||||
python3 --version
|
||||
```
|
||||
|
||||
如果没有找到,使用完整路径:
|
||||
```json
|
||||
{
|
||||
"command": "C:\\Python312\\python.exe",
|
||||
"args": ["-u", "path/to/mcp_server.py"]
|
||||
}
|
||||
```
|
||||
|
||||
### Q3:模块导入错误怎样修复?
|
||||
|
||||
**A:** 重新安装依赖:
|
||||
```bash
|
||||
pip install --upgrade sympy numpy scipy pandas
|
||||
```
|
||||
|
||||
### Q4:如何运行测试?
|
||||
|
||||
**A:**
|
||||
```bash
|
||||
python verify_production_readiness.py
|
||||
```
|
||||
|
||||
### Q5:如何调试问题?
|
||||
|
||||
**A:** 使用完整路径并添加日志:
|
||||
```json
|
||||
{
|
||||
"command": "python",
|
||||
"args": ["-u", "-u", "path/to/mcp_server.py"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── calculator.py # 核心计算模块(3个工具函数)
|
||||
├── mcp_server.py # MCP 服务器实现
|
||||
├── mcp_protocols.py # MCP 协议定义(可选)
|
||||
├── requirements.txt # 依赖列表
|
||||
├── setup.py # 打包配置
|
||||
├── pyproject.toml # 项目元数据
|
||||
├── advanced_math_problems.py # 测试用例
|
||||
├── advanced_math_problems.json# 测试数据
|
||||
├── verify_production_readiness.py # 验证脚本
|
||||
├── README.md # 英文文档
|
||||
├── README_ZH.md # 中文文档(本文件)
|
||||
├── QUICKSTART.md # 快速入门
|
||||
├── DEPLOYMENT.md # 部署指南
|
||||
└── .vscode/ # VS Code 配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 版本信息
|
||||
|
||||
- **版本**: 1.0.0(初始版本)
|
||||
- **发布日期**: 2025 年 12 月 7 日
|
||||
- **Python 版本**: 3.8+
|
||||
- **MCP 版本**: 兼容最新版本
|
||||
- **许可证**: MIT
|
||||
|
||||
---
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
- **GitHub**: https://github.com/thinkitpossible/CalcMCP
|
||||
- **PyPI**: https://pypi.org/project/mcp-scientific-calculator/
|
||||
- **MCP 协议**: https://modelcontextprotocol.io/
|
||||
- **SymPy 文档**: https://docs.sympy.org/
|
||||
- **NumPy 文档**: https://numpy.org/doc/
|
||||
- **SciPy 文档**: https://docs.scipy.org/
|
||||
|
||||
---
|
||||
|
||||
## 📧 反馈与支持
|
||||
|
||||
如有问题或建议,请提出 Issue 或联系维护者。
|
||||
|
||||
**GitHub Issues**: https://github.com/thinkitpossible/CalcMCP/issues
|
||||
|
||||
---
|
||||
|
||||
**祝您使用愉快!** 🎉
|
||||
@@ -13,8 +13,18 @@ except Exception:
|
||||
from scipy.integrate import odeint
|
||||
from scipy.fftpack import fft, rfft
|
||||
from typing import Optional
|
||||
import ast
|
||||
import json
|
||||
|
||||
|
||||
def _json_default(obj):
|
||||
"""JSON fallback for numpy/complex values (eigenvalues are often complex)."""
|
||||
if isinstance(obj, complex):
|
||||
return obj.real if obj.imag == 0 else f"{obj.real}{obj.imag:+}i"
|
||||
if hasattr(obj, "item"):
|
||||
return _json_default(obj.item())
|
||||
return str(obj)
|
||||
|
||||
# ============================================================================
|
||||
# SYMBOLIC MATHEMATICS (SymPy)
|
||||
# ============================================================================
|
||||
@@ -91,9 +101,14 @@ def symbolic_limit(expression: str, variable: str, point: str) -> str:
|
||||
|
||||
|
||||
def solve_equation(equation: str, variable: str) -> str:
|
||||
"""解方程"""
|
||||
"""Solve an equation. Accepts 'expr' (treated as expr = 0), 'lhs = rhs' or 'lhs == rhs'."""
|
||||
try:
|
||||
expr = sp.sympify(equation)
|
||||
eq_str = equation.replace("==", "=")
|
||||
if "=" in eq_str:
|
||||
lhs, rhs = eq_str.split("=", 1)
|
||||
expr = sp.Eq(sp.sympify(lhs), sp.sympify(rhs))
|
||||
else:
|
||||
expr = sp.sympify(eq_str)
|
||||
var = sp.Symbol(variable)
|
||||
result = sp.solve(expr, var)
|
||||
return str(result)
|
||||
@@ -101,6 +116,22 @@ def solve_equation(equation: str, variable: str) -> str:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
|
||||
def symbolic_dsolve(equation: str, variable: str = "x") -> str:
|
||||
"""Solve an ODE symbolically. Unknown function must be written as y(x),
|
||||
e.g. 'Derivative(y(x), x, 2) + y(x) = 0' -> y(x) = C1*sin(x) + C2*cos(x)."""
|
||||
try:
|
||||
eq_str = equation.replace("==", "=")
|
||||
if "=" in eq_str:
|
||||
lhs, rhs = eq_str.split("=", 1)
|
||||
eq = sp.Eq(sp.sympify(lhs), sp.sympify(rhs))
|
||||
else:
|
||||
eq = sp.sympify(eq_str)
|
||||
result = sp.dsolve(eq)
|
||||
return str(result)
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
|
||||
def taylor_series(expression: str, variable: str, point: str, n: int) -> str:
|
||||
"""泰勒级数展开"""
|
||||
try:
|
||||
@@ -185,8 +216,8 @@ def numpy_linear_algebra(matrix_a, operation: str, matrix_b=None) -> str:
|
||||
if isinstance(matrix_a, str):
|
||||
try:
|
||||
mat1 = np.array(json.loads(matrix_a), dtype=float)
|
||||
except:
|
||||
mat1 = np.array(eval(matrix_a), dtype=float)
|
||||
except Exception:
|
||||
mat1 = np.array(ast.literal_eval(matrix_a), dtype=float)
|
||||
else:
|
||||
mat1 = np.array(matrix_a, dtype=float)
|
||||
|
||||
@@ -195,27 +226,19 @@ def numpy_linear_algebra(matrix_a, operation: str, matrix_b=None) -> str:
|
||||
if isinstance(matrix_b, str):
|
||||
try:
|
||||
mat2 = np.array(json.loads(matrix_b), dtype=float)
|
||||
except:
|
||||
mat2 = np.array(eval(matrix_b), dtype=float)
|
||||
except Exception:
|
||||
mat2 = np.array(ast.literal_eval(matrix_b), dtype=float)
|
||||
else:
|
||||
mat2 = np.array(matrix_b, dtype=float)
|
||||
|
||||
# 执行操作
|
||||
if operation == "eigenvalues":
|
||||
if operation in ("eigenvalues", "eigenvectors"):
|
||||
eigenvalues, eigenvectors = np.linalg.eig(mat1)
|
||||
result = {
|
||||
"eigenvalues": eigenvalues.tolist(),
|
||||
"eigenvectors": eigenvectors.tolist()
|
||||
}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
|
||||
elif operation == "eigenvectors":
|
||||
eigenvalues, eigenvectors = np.linalg.eig(mat1)
|
||||
result = {
|
||||
"eigenvalues": eigenvalues.tolist(),
|
||||
"eigenvectors": eigenvectors.tolist()
|
||||
}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
return json.dumps(result, ensure_ascii=False, default=_json_default)
|
||||
|
||||
elif operation == "determinant":
|
||||
result = float(np.linalg.det(mat1))
|
||||
@@ -434,21 +457,17 @@ def scipy_solve_ode(expression: str, initial_conditions: str, t_values: str, var
|
||||
else:
|
||||
reverse_order = False
|
||||
|
||||
# 评估 ODE 函数表达式
|
||||
try:
|
||||
# 尝试作为 lambda 函数字符串
|
||||
ode_func = eval(expression)
|
||||
except:
|
||||
# 如果失败,尝试作为符号表达式
|
||||
var_list = [sp.Symbol(v.strip()) for v in variables.split(",")]
|
||||
t_sym = sp.Symbol('t')
|
||||
expr = sp.sympify(expression)
|
||||
|
||||
if len(var_list) == 1:
|
||||
ode_func = sp.lambdify([t_sym, var_list[0]], expr, modules=['numpy'])
|
||||
ode_func_wrapped = lambda y, t: ode_func(t, y)
|
||||
else:
|
||||
return "Error: System ODEs not yet fully supported"
|
||||
# Parse dy/dt = f(t, y) as a SymPy expression (no eval of raw Python)
|
||||
var_list = [sp.Symbol(v.strip()) for v in (variables or "y").split(",")]
|
||||
t_sym = sp.Symbol('t')
|
||||
expr = sp.sympify(expression)
|
||||
|
||||
if len(var_list) == 1:
|
||||
f = sp.lambdify([t_sym, var_list[0]], expr, modules=['numpy'])
|
||||
# odeint calls func(y, t)
|
||||
ode_func = lambda y, t: f(t, y)
|
||||
else:
|
||||
return "Error: System ODEs not yet fully supported"
|
||||
|
||||
# 调用 odeint
|
||||
if isinstance(y0, (int, float)):
|
||||
@@ -489,6 +508,8 @@ def scipy_statistics(operation: str, data: str, params: Optional[str] = None) ->
|
||||
return str(float(np.std(data_arr)))
|
||||
elif operation == "ttest":
|
||||
popmean = json.loads(params) if params else 0
|
||||
if isinstance(popmean, dict):
|
||||
popmean = popmean.get("popmean", 0)
|
||||
result = stats.ttest_1samp(data_arr, popmean)
|
||||
return f"statistic={result.statistic}, pvalue={result.pvalue}"
|
||||
elif operation == "pearsonr" and params:
|
||||
@@ -560,6 +581,8 @@ def symbolic_tool(
|
||||
return symbolic_limit(expression, variable, point or point_value)
|
||||
if op in ("solve", "solve_equation"):
|
||||
return solve_equation(equation or expression, variable)
|
||||
if op in ("dsolve", "ode", "solve_ode", "differential_equation"):
|
||||
return symbolic_dsolve(equation or expression, variable or "x")
|
||||
if op in ("taylor", "taylor_series"):
|
||||
return taylor_series(expression, variable, point or "0", order)
|
||||
if op in ("matrix", "symbolic_matrix_operations"):
|
||||
|
||||
135
mcp_server.py
135
mcp_server.py
@@ -58,55 +58,85 @@ class MCPCalculatorServer:
|
||||
# Consolidated tool definitions (3 tools)
|
||||
tool_definitions = {
|
||||
"symbolic_tool": {
|
||||
"description": "统一符号计算入口:simplify/expand/factor/derivative/integral/limit/solve/taylor/matrix",
|
||||
"description": (
|
||||
"Exact symbolic math (SymPy). Set operation to one of: simplify, expand, factor, "
|
||||
"derivative, integral, limit, solve, dsolve, taylor, matrix. "
|
||||
"Write expressions in Python/SymPy syntax, e.g. 'x**2 * exp(-x)'; use 'oo' for infinity. "
|
||||
"integral: optional lower_limit/upper_limit for definite integrals. "
|
||||
"solve: pass the equation, '=' is allowed (e.g. 'x**2 = 4' or 'x**3 - 6*x**2 + 11*x - 6'). "
|
||||
"dsolve: solves an ODE symbolically — write the unknown function as y(x), "
|
||||
"e.g. equation='Derivative(y(x), x, 2) + y(x) = 0'. "
|
||||
"taylor: expression + variable + point + order. "
|
||||
"matrix: matrix_data (JSON) with variable=determinant|inverse|rank|trace. "
|
||||
"Results are exact (e.g. '2', 'E', 'C1*sin(x) + C2*cos(x)')."
|
||||
),
|
||||
"params": [
|
||||
{"name": "operation", "type": "string", "description": "操作类型,如 derivative, integral, solve 等"},
|
||||
{"name": "expression", "type": "string", "description": "表达式", "optional": True},
|
||||
{"name": "variable", "type": "string", "description": "变量", "optional": True},
|
||||
{"name": "order", "type": "integer", "description": "阶数(导数/泰勒)", "optional": True},
|
||||
{"name": "lower_limit", "type": "string", "description": "积分下限", "optional": True},
|
||||
{"name": "upper_limit", "type": "string", "description": "积分上限", "optional": True},
|
||||
{"name": "equation", "type": "string", "description": "方程", "optional": True},
|
||||
{"name": "point", "type": "string", "description": "极限或泰勒展开点", "optional": True},
|
||||
{"name": "matrix_data", "type": "string", "description": "符号矩阵(JSON)", "optional": True}
|
||||
{"name": "operation", "type": "string", "description": "One of: simplify, expand, factor, derivative, integral, limit, solve, dsolve, taylor, matrix"},
|
||||
{"name": "expression", "type": "string", "description": "Expression in SymPy syntax, e.g. 'sin(x)/x'", "optional": True},
|
||||
{"name": "variable", "type": "string", "description": "Variable name, e.g. 'x' (for matrix: the operation name)", "optional": True},
|
||||
{"name": "order", "type": "integer", "description": "Order for derivative/taylor (default 1 / 5)", "optional": True},
|
||||
{"name": "lower_limit", "type": "string", "description": "Lower bound for definite integral, e.g. '0'", "optional": True},
|
||||
{"name": "upper_limit", "type": "string", "description": "Upper bound for definite integral, e.g. 'oo'", "optional": True},
|
||||
{"name": "equation", "type": "string", "description": "Equation for solve/dsolve; '=' allowed", "optional": True},
|
||||
{"name": "point", "type": "string", "description": "Point for limit/taylor, e.g. '0' or 'oo'", "optional": True},
|
||||
{"name": "matrix_data", "type": "string", "description": "Matrix as JSON, e.g. '[[1,2],[3,4]]'", "optional": True}
|
||||
]
|
||||
},
|
||||
"numpy_tool": {
|
||||
"description": "统一 NumPy 入口:数组归约、线性代数、三角、多项式",
|
||||
"description": (
|
||||
"Numeric arrays and linear algebra (NumPy). Set operation to one of: "
|
||||
"sum, mean, std, max, min (array_data, optional axis); "
|
||||
"eigenvalues, eigenvectors, determinant, inverse, solve, norm, rank, trace, "
|
||||
"svd, qr, cholesky, matmul, dot, hadamard (matrix_a, and matrix_b where two matrices are needed); "
|
||||
"sin, cos, tan, arcsin, arccos, arctan, sinh, cosh, tanh (values, optional use_degrees); "
|
||||
"poly_eval, poly_derivative, poly_integral (coefficients highest-degree first, optional x_values). "
|
||||
"All array/matrix inputs are JSON strings, e.g. matrix_a='[[2,1],[1,2]]'. Results are numeric."
|
||||
),
|
||||
"params": [
|
||||
{"name": "operation", "type": "string", "description": "操作类型,如 eigenvalues, sum, poly_eval 等"},
|
||||
{"name": "array_data", "type": "string", "description": "数组数据(JSON)", "optional": True},
|
||||
{"name": "axis", "type": "string", "description": "轴参数", "optional": True},
|
||||
{"name": "matrix_a", "type": "string", "description": "矩阵A(JSON)", "optional": True},
|
||||
{"name": "matrix_b", "type": "string", "description": "矩阵B(JSON)", "optional": True},
|
||||
{"name": "values", "type": "string", "description": "三角函数输入(JSON)", "optional": True},
|
||||
{"name": "use_degrees", "type": "boolean", "description": "角度制", "optional": True},
|
||||
{"name": "coefficients", "type": "string", "description": "多项式系数(JSON)", "optional": True},
|
||||
{"name": "x_values", "type": "string", "description": "多项式自变量数组(JSON)", "optional": True}
|
||||
{"name": "operation", "type": "string", "description": "Operation name, e.g. 'eigenvalues', 'matmul', 'mean', 'poly_eval'"},
|
||||
{"name": "array_data", "type": "string", "description": "Array as JSON, e.g. '[1,2,3]' or '[[1,2],[3,4]]'", "optional": True},
|
||||
{"name": "axis", "type": "string", "description": "Axis for reductions: '0' or '1'", "optional": True},
|
||||
{"name": "matrix_a", "type": "string", "description": "First matrix as JSON", "optional": True},
|
||||
{"name": "matrix_b", "type": "string", "description": "Second matrix as JSON (solve/matmul/dot/hadamard)", "optional": True},
|
||||
{"name": "values", "type": "string", "description": "Input values for trig functions as JSON, e.g. '[0, 90, 180]'", "optional": True},
|
||||
{"name": "use_degrees", "type": "boolean", "description": "Treat trig inputs as degrees (default radians)", "optional": True},
|
||||
{"name": "coefficients", "type": "string", "description": "Polynomial coefficients as JSON, highest degree first", "optional": True},
|
||||
{"name": "x_values", "type": "string", "description": "x values for poly_eval as JSON", "optional": True}
|
||||
]
|
||||
},
|
||||
"scipy_tool": {
|
||||
"description": "统一 SciPy 入口:积分、优化、插值、特殊函数、ODE、统计、FFT、特征分解",
|
||||
"description": (
|
||||
"Numerical methods (SciPy). Set operation to one of: "
|
||||
"integrate (numeric definite integral: expression, variable, lower, upper); "
|
||||
"optimize_minimize, optimize_root (expression, initial_value, optional method); "
|
||||
"interpolate_linear, interpolate_cubic, interpolate_spline (x_data, y_data, x_eval); "
|
||||
"special (function name + parameters JSON, e.g. gamma, besselj); "
|
||||
"solve_ode (NUMERIC simulation of dy/dt = f(t,y): expression e.g. '-2*y + t', "
|
||||
"initial_conditions, t_values; for exact general solutions use symbolic_tool with operation='dsolve'); "
|
||||
"statistics ops mean, std, describe, ttest, pearsonr (function + data, optional params); "
|
||||
"fft, rfft (data_series or data); matrix_eigensystem (matrix_a). "
|
||||
"All list inputs are JSON strings. Results are numeric."
|
||||
),
|
||||
"params": [
|
||||
{"name": "operation", "type": "string", "description": "操作类型,如 integrate, optimize_minimize, fft 等"},
|
||||
{"name": "expression", "type": "string", "description": "表达式/函数", "optional": True},
|
||||
{"name": "variable", "type": "string", "description": "变量", "optional": True},
|
||||
{"name": "lower", "type": "string", "description": "积分下限", "optional": True},
|
||||
{"name": "upper", "type": "string", "description": "积分上限", "optional": True},
|
||||
{"name": "initial_value", "type": "string", "description": "初值/起点", "optional": True},
|
||||
{"name": "method", "type": "string", "description": "算法方法", "optional": True},
|
||||
{"name": "x_data", "type": "string", "description": "插值 x 数据(JSON)", "optional": True},
|
||||
{"name": "y_data", "type": "string", "description": "插值 y 数据(JSON)", "optional": True},
|
||||
{"name": "x_eval", "type": "string", "description": "插值求值点(JSON)", "optional": True},
|
||||
{"name": "function", "type": "string", "description": "特殊函数名/统计操作名", "optional": True},
|
||||
{"name": "parameters", "type": "string", "description": "特殊函数参数(JSON)", "optional": True},
|
||||
{"name": "initial_conditions", "type": "string", "description": "ODE 初值(JSON)", "optional": True},
|
||||
{"name": "t_values", "type": "string", "description": "ODE 时间点(JSON)", "optional": True},
|
||||
{"name": "variables", "type": "string", "description": "ODE 变量名", "optional": True},
|
||||
{"name": "data", "type": "string", "description": "统计或 FFT 数据(JSON)", "optional": True},
|
||||
{"name": "params", "type": "string", "description": "统计附加参数(JSON)", "optional": True},
|
||||
{"name": "data_series", "type": "string", "description": "FFT 数据(JSON)", "optional": True},
|
||||
{"name": "matrix_a", "type": "string", "description": "矩阵(JSON)用于特征分解", "optional": True}
|
||||
{"name": "operation", "type": "string", "description": "Operation name, e.g. 'integrate', 'optimize_minimize', 'solve_ode', 'fft'"},
|
||||
{"name": "expression", "type": "string", "description": "Expression/function of the variable, e.g. 'x**2 * exp(-x)'", "optional": True},
|
||||
{"name": "variable", "type": "string", "description": "Variable name, e.g. 'x'", "optional": True},
|
||||
{"name": "lower", "type": "string", "description": "Lower integration bound (number)", "optional": True},
|
||||
{"name": "upper", "type": "string", "description": "Upper integration bound (number or 'oo')", "optional": True},
|
||||
{"name": "initial_value", "type": "string", "description": "Starting point for optimization/root finding", "optional": True},
|
||||
{"name": "method", "type": "string", "description": "Algorithm, e.g. 'BFGS', 'brentq'", "optional": True},
|
||||
{"name": "x_data", "type": "string", "description": "Interpolation x data as JSON", "optional": True},
|
||||
{"name": "y_data", "type": "string", "description": "Interpolation y data as JSON", "optional": True},
|
||||
{"name": "x_eval", "type": "string", "description": "Points to evaluate the interpolant at, JSON", "optional": True},
|
||||
{"name": "function", "type": "string", "description": "Special-function or statistics operation name", "optional": True},
|
||||
{"name": "parameters", "type": "string", "description": "Special-function parameters as JSON", "optional": True},
|
||||
{"name": "initial_conditions", "type": "string", "description": "ODE initial value(s) as JSON, e.g. '[1.0]'", "optional": True},
|
||||
{"name": "t_values", "type": "string", "description": "ODE time points as JSON, e.g. '[0, 0.1, 0.2]'", "optional": True},
|
||||
{"name": "variables", "type": "string", "description": "ODE dependent variable name, default 'y'", "optional": True},
|
||||
{"name": "data", "type": "string", "description": "Statistics/FFT data as JSON", "optional": True},
|
||||
{"name": "params", "type": "string", "description": "Extra statistics parameter as JSON: for ttest the population mean ('2.3' or '{\"popmean\": 2.3}'), for pearsonr the second data series", "optional": True},
|
||||
{"name": "data_series", "type": "string", "description": "FFT data as JSON", "optional": True},
|
||||
{"name": "matrix_a", "type": "string", "description": "Matrix as JSON for matrix_eigensystem", "optional": True}
|
||||
]
|
||||
},
|
||||
}
|
||||
@@ -116,7 +146,7 @@ class MCPCalculatorServer:
|
||||
tools.append({
|
||||
"name": tool_name,
|
||||
"description": definition["description"],
|
||||
"params": {
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
param["name"]: {
|
||||
@@ -170,7 +200,11 @@ class MCPCalculatorServer:
|
||||
method = request_dict.get("method")
|
||||
params = request_dict.get("params", {})
|
||||
req_id = request_dict.get("id")
|
||||
|
||||
|
||||
# JSON-RPC notifications (no id) must not be answered
|
||||
if req_id is None and isinstance(method, str) and method.startswith("notifications/"):
|
||||
return None
|
||||
|
||||
# 处理特殊方法
|
||||
if method == "initialize":
|
||||
return {
|
||||
@@ -200,7 +234,10 @@ class MCPCalculatorServer:
|
||||
tool_params = params.get("arguments", {})
|
||||
|
||||
result = self.call_tool(tool_name, tool_params)
|
||||
|
||||
|
||||
is_error = isinstance(result, str) and result.startswith(
|
||||
("Error", "Parameter Error", "Execution Error")
|
||||
)
|
||||
return {
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
@@ -209,7 +246,8 @@ class MCPCalculatorServer:
|
||||
"type": "text",
|
||||
"text": result
|
||||
}
|
||||
]
|
||||
],
|
||||
"isError": is_error
|
||||
},
|
||||
"id": req_id
|
||||
}
|
||||
@@ -252,10 +290,11 @@ class MCPCalculatorServer:
|
||||
try:
|
||||
request = json.loads(line)
|
||||
response = self.handle_rpc_request(request)
|
||||
|
||||
# 写入 JSON-RPC 响应到标准输出
|
||||
sys.stdout.write(json.dumps(response, ensure_ascii=False) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
# Notifications return None — nothing must be written back
|
||||
if response is not None:
|
||||
sys.stdout.write(json.dumps(response, ensure_ascii=False) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
# 发送 JSON 解析错误
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
Flask>=3.0.0
|
||||
Flask-CORS>=4.0.0
|
||||
requests>=2.31.0
|
||||
fastmcp>=0.1.0
|
||||
sympy>=1.12
|
||||
scipy>=1.11.0
|
||||
numpy>=1.24.0
|
||||
pytest>=7.0.0
|
||||
sympy>=1.12
|
||||
numpy>=1.24.0
|
||||
scipy>=1.11.0
|
||||
pandas>=2.0.0
|
||||
|
||||
Reference in New Issue
Block a user