open-math-mcps 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.3
2
+ Name: open-math-mcps
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Author: K-Summer
6
+ Author-email: K-Summer <91466399+K-Summer@users.noreply.github.com>
7
+ Requires-Dist: mcp[cli]>=1.26.0
8
+ Requires-Python: >=3.12
9
+ Description-Content-Type: text/markdown
10
+
File without changes
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "open-math-mcps"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "K-Summer", email = "91466399+K-Summer@users.noreply.github.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "mcp[cli]>=1.26.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ open-math-mcps = "open_math_mcps:main"
16
+
17
+ [[tool.uv.index]]
18
+ url = "https://pypi.tuna.tsinghua.edu.cn/simple"
19
+ default = true
20
+
21
+ [build-system]
22
+ requires = ["uv_build>=0.10.3,<0.11.0"]
23
+ build-backend = "uv_build"
@@ -0,0 +1,444 @@
1
+ """
2
+ 数学计算 MCP 服务器
3
+
4
+ 提供各种数学计算功能,包括基础运算、高级数学函数、数学常量和复杂算式处理。
5
+ """
6
+
7
+ import math
8
+ import re
9
+ import ast
10
+ from mcp.server.fastmcp import FastMCP
11
+ from typing import List, Optional, Dict, Any
12
+
13
+ # 创建一个 MCP 服务器
14
+ mcp = FastMCP("数学计算器", json_response=True)
15
+
16
+
17
+ # ===== 基础数学运算工具 =====
18
+
19
+ @mcp.tool()
20
+ def add(a: float, b: float) -> float:
21
+ """两个数相加"""
22
+ return a + b
23
+
24
+
25
+ @mcp.tool()
26
+ def subtract(a: float, b: float) -> float:
27
+ """两个数相减"""
28
+ return a - b
29
+
30
+
31
+ @mcp.tool()
32
+ def multiply(a: float, b: float) -> float:
33
+ """两个数相乘"""
34
+ return a * b
35
+
36
+
37
+ @mcp.tool()
38
+ def divide(a: float, b: float) -> float:
39
+ """两个数相除"""
40
+ if b == 0:
41
+ raise ValueError("除数不能为零")
42
+ return a / b
43
+
44
+
45
+ # ===== 高级数学运算工具 =====
46
+
47
+ @mcp.tool()
48
+ def power(base: float, exponent: float) -> float:
49
+ """计算幂"""
50
+ return math.pow(base, exponent)
51
+
52
+
53
+ @mcp.tool()
54
+ def sqrt(number: float) -> float:
55
+ """计算平方根"""
56
+ if number < 0:
57
+ raise ValueError("不能计算负数的平方根")
58
+ return math.sqrt(number)
59
+
60
+
61
+ @mcp.tool()
62
+ def cbrt(number: float) -> float:
63
+ """计算立方根"""
64
+ return number ** (1/3)
65
+
66
+
67
+ @mcp.tool()
68
+ def log(number: float, base: float = math.e) -> float:
69
+ """计算对数"""
70
+ if number <= 0:
71
+ raise ValueError("对数的真数必须大于零")
72
+ if base <= 0 or base == 1:
73
+ raise ValueError("对数的底数必须大于零且不等于1")
74
+ return math.log(number, base)
75
+
76
+
77
+ @mcp.tool()
78
+ def ln(number: float) -> float:
79
+ """计算自然对数"""
80
+ return log(number, math.e)
81
+
82
+
83
+ @mcp.tool()
84
+ def exp(number: float) -> float:
85
+ """计算指数函数 e^x"""
86
+ return math.exp(number)
87
+
88
+
89
+ @mcp.tool()
90
+ def sin(angle: float) -> float:
91
+ """计算正弦值(角度制)"""
92
+ return math.sin(math.radians(angle))
93
+
94
+
95
+ @mcp.tool()
96
+ def cos(angle: float) -> float:
97
+ """计算余弦值(角度制)"""
98
+ return math.cos(math.radians(angle))
99
+
100
+
101
+ @mcp.tool()
102
+ def tan(angle: float) -> float:
103
+ """计算正切值(角度制)"""
104
+ result = math.tan(math.radians(angle))
105
+ if abs(result) > 1e15:
106
+ raise ValueError("正切值超出范围")
107
+ return result
108
+
109
+
110
+ @mcp.tool()
111
+ def asin(value: float) -> float:
112
+ """计算反正弦值(返回角度制)"""
113
+ if abs(value) > 1:
114
+ raise ValueError("反正弦的输入值必须在-1到1之间")
115
+ return math.degrees(math.asin(value))
116
+
117
+
118
+ @mcp.tool()
119
+ def acos(value: float) -> float:
120
+ """计算反余弦值(返回角度制)"""
121
+ if abs(value) > 1:
122
+ raise ValueError("反余弦的输入值必须在-1到1之间")
123
+ return math.degrees(math.acos(value))
124
+
125
+
126
+ @mcp.tool()
127
+ def atan(value: float) -> float:
128
+ """计算反正切值(返回角度制)"""
129
+ return math.degrees(math.atan(value))
130
+
131
+
132
+ @mcp.tool()
133
+ def factorial(n: int) -> int:
134
+ """计算阶乘"""
135
+ if n < 0:
136
+ raise ValueError("阶乘只能计算非负整数")
137
+ if n > 170:
138
+ raise ValueError("阶乘值太大,超出计算范围")
139
+ return math.factorial(n)
140
+
141
+
142
+ @mcp.tool()
143
+ def combination(n: int, r: int) -> int:
144
+ """计算组合数 C(n, r)"""
145
+ if n < 0 or r < 0 or r > n:
146
+ raise ValueError("组合数参数无效")
147
+ return math.comb(n, r)
148
+
149
+
150
+ @mcp.tool()
151
+ def permutation(n: int, r: int) -> int:
152
+ """计算排列数 P(n, r)"""
153
+ if n < 0 or r < 0 or r > n:
154
+ raise ValueError("排列数参数无效")
155
+ return math.perm(n, r)
156
+
157
+
158
+ # ===== 统计学工具 =====
159
+
160
+ @mcp.tool()
161
+ def mean(numbers: List[float]) -> float:
162
+ """计算平均值"""
163
+ if not numbers:
164
+ raise ValueError("列表不能为空")
165
+ return sum(numbers) / len(numbers)
166
+
167
+
168
+ @mcp.tool()
169
+ def median(numbers: List[float]) -> float:
170
+ """计算中位数"""
171
+ if not numbers:
172
+ raise ValueError("列表不能为空")
173
+ sorted_numbers = sorted(numbers)
174
+ n = len(sorted_numbers)
175
+ if n % 2 == 1:
176
+ return sorted_numbers[n//2]
177
+ else:
178
+ return (sorted_numbers[n//2 - 1] + sorted_numbers[n//2]) / 2
179
+
180
+
181
+ @mcp.tool()
182
+ def mode(numbers: List[float]) -> List[float]:
183
+ """计算众数"""
184
+ if not numbers:
185
+ raise ValueError("列表不能为空")
186
+ frequency = {}
187
+ for num in numbers:
188
+ frequency[num] = frequency.get(num, 0) + 1
189
+ max_freq = max(frequency.values())
190
+ return [num for num, freq in frequency.items() if freq == max_freq]
191
+
192
+
193
+ @mcp.tool()
194
+ def variance(numbers: List[float]) -> float:
195
+ """计算方差"""
196
+ if len(numbers) < 2:
197
+ raise ValueError("至少需要两个数据点")
198
+ avg = mean(numbers)
199
+ return sum((x - avg) ** 2 for x in numbers) / len(numbers)
200
+
201
+
202
+ @mcp.tool()
203
+ def standard_deviation(numbers: List[float]) -> float:
204
+ """计算标准差"""
205
+ return math.sqrt(variance(numbers))
206
+
207
+
208
+ # ===== 数学常量资源 =====
209
+
210
+ @mcp.resource("math:constant/{constant_name}")
211
+ def get_math_constant(constant_name: str) -> dict:
212
+ """获取数学常量"""
213
+ constants = {
214
+ "pi": {
215
+ "name": "圆周率",
216
+ "symbol": "π",
217
+ "value": math.pi,
218
+ "description": "圆的周长与直径之比",
219
+ "approximations": ["3.14159", "22/7"]
220
+ },
221
+ "e": {
222
+ "name": "自然常数",
223
+ "symbol": "e",
224
+ "value": math.e,
225
+ "description": "自然对数的底数",
226
+ "approximations": ["2.71828", "2.718"]
227
+ },
228
+ "golden_ratio": {
229
+ "name": "黄金比例",
230
+ "symbol": "φ",
231
+ "value": (1 + math.sqrt(5)) / 2,
232
+ "description": "数学中的黄金比例常数",
233
+ "approximations": ["1.61803", "1.618"]
234
+ },
235
+ "sqrt_2": {
236
+ "name": "根号2",
237
+ "symbol": "√2",
238
+ "value": math.sqrt(2),
239
+ "description": "2的平方根",
240
+ "approximations": ["1.41421", "1.414"]
241
+ }
242
+ }
243
+
244
+ if constant_name in constants:
245
+ return constants[constant_name]
246
+ else:
247
+ raise ValueError(f"未知的数学常量: {constant_name}")
248
+
249
+
250
+ # ===== 复杂算式处理工具 =====
251
+
252
+ @mcp.tool()
253
+ def evaluate_expression(expression: str, variables: Optional[Dict[str, float]] = None) -> float:
254
+ """
255
+ 计算复杂的数学表达式
256
+
257
+ 参数:
258
+ expression: 数学表达式字符串
259
+ variables: 变量名到值的映射字典(可选)
260
+
261
+ 支持的运算符:
262
+ +, -, *, /, ** (幂), % (取模)
263
+ 函数: sin, cos, tan, asin, acos, atan, sqrt, log, ln, exp, factorial
264
+ 常量: pi, e
265
+
266
+ 示例:
267
+ "2 * (3 + 4)" -> 14.0
268
+ "sin(30) + cos(60)" -> 1.0
269
+ "x + y" (其中 variables={"x": 2, "y": 3}) -> 5.0
270
+ """
271
+ # 替换常量
272
+ expression = expression.replace("pi", str(math.pi))
273
+ expression = expression.replace("e", str(math.e))
274
+
275
+ # 替换变量
276
+ if variables:
277
+ for var, value in variables.items():
278
+ # 使用正则表达式确保匹配完整的变量名
279
+ expression = re.sub(rf"\b{var}\b", str(value), expression)
280
+
281
+ # 预处理表达式,确保安全
282
+ try:
283
+ # 检查表达式是否只包含允许的字符
284
+ allowed_chars = r"^[0-9+\-*/.() **,%\s\w]+$"
285
+ if not re.match(allowed_chars, expression):
286
+ raise ValueError("表达式包含不允许的字符")
287
+
288
+ # 使用ast.literal_eval进行基本评估
289
+ # 对于更复杂的表达式,我们使用eval,但有安全检查
290
+ # 注意:在生产环境中,使用eval有安全风险,这里简化处理
291
+
292
+ # 定义安全的函数和常量
293
+ safe_dict = {
294
+ "__builtins__": {},
295
+ "sin": math.sin,
296
+ "cos": math.cos,
297
+ "tan": math.tan,
298
+ "asin": math.asin,
299
+ "acos": math.acos,
300
+ "atan": math.atan,
301
+ "sqrt": math.sqrt,
302
+ "log": math.log,
303
+ "ln": math.log,
304
+ "exp": math.exp,
305
+ "factorial": math.factorial,
306
+ "pi": math.pi,
307
+ "e": math.e,
308
+ "radians": math.radians,
309
+ "degrees": math.degrees
310
+ }
311
+
312
+ # 计算表达式
313
+ result = eval(expression, {"__builtins__": None}, safe_dict)
314
+
315
+ # 确保结果是数字
316
+ if not isinstance(result, (int, float)):
317
+ raise ValueError("表达式计算结果不是数字")
318
+
319
+ return float(result)
320
+
321
+ except (SyntaxError, NameError, TypeError, ZeroDivisionError) as e:
322
+ raise ValueError(f"表达式计算错误: {str(e)}")
323
+
324
+
325
+ @mcp.tool()
326
+ def simplify_expression(expression: str) -> str:
327
+ """
328
+ 简化数学表达式(基础实现)
329
+
330
+ 参数:
331
+ expression: 数学表达式字符串
332
+
333
+ 返回:
334
+ 简化后的表达式字符串
335
+ """
336
+ # 这里实现一个基础的简化逻辑
337
+ # 在实际应用中,可以使用更复杂的符号计算库
338
+
339
+ # 移除不必要的空格
340
+ simplified = expression.replace(" ", "")
341
+
342
+ # 处理简单的 +0 或 *1
343
+ simplified = re.sub(r"\+0(?=[+\-*/)])", "", simplified)
344
+ simplified = re.sub(r"\*1(?=[+\-*/)])", "", simplified)
345
+
346
+ # 处理 0+x 或 x+0
347
+ simplified = re.sub(r"0\+(?=[0-9.])", "", simplified)
348
+ simplified = re.sub(r"\+0$", "", simplified)
349
+
350
+ # 处理 1*x 或 x*1
351
+ simplified = re.sub(r"1\*(?=[0-9.])", "", simplified)
352
+ simplified = re.sub(r"\*1$", "", simplified)
353
+
354
+ # 处理 x-0
355
+ simplified = re.sub(r"(?<![0-9.])-0(?=[+\-*/)])", "", simplified)
356
+ simplified = re.sub(r"-0$", "", simplified)
357
+
358
+ # 处理 x/1
359
+ simplified = re.sub(r"(?<![0-9.])/1(?=[+\-*/)])", "", simplified)
360
+ simplified = re.sub(r"/1$", "", simplified)
361
+
362
+ return simplified
363
+
364
+
365
+ # ===== 数学公式提示生成器 =====
366
+
367
+ @mcp.prompt()
368
+ def solve_equation(equation_type: str, variables: dict, context: str = "") -> str:
369
+ """生成解方程的提示"""
370
+ prompts = {
371
+ "linear": f"请解以下一元一次方程:{context or '请提供具体方程'}",
372
+ "quadratic": f"请解以下二次方程:{context or '请提供具体方程'}。请使用求根公式,并说明每一步的推导过程。",
373
+ "system": f"请解以下方程组:{context or '请提供具体方程组'}。请使用代入法、消元法或其他适当的方法。",
374
+ "inequality": f"请解以下不等式:{context or '请提供具体不等式'}。请注意不等式的性质和解的表示方法。"
375
+ }
376
+
377
+ prompt = prompts.get(equation_type, f"请解以下数学问题:{context}")
378
+
379
+ if variables:
380
+ prompt += "\n\n已知变量:" + ", ".join(f"{k} = {v}" for k, v in variables.items())
381
+
382
+ return prompt + "\n\n请详细展示解题步骤和最终答案。"
383
+
384
+
385
+ @mcp.prompt()
386
+ def prove_theorem(theorem: str, method: str = "direct") -> str:
387
+ """生成证明数学定理的提示"""
388
+ method_descriptions = {
389
+ "direct": "直接证明法",
390
+ "contradiction": "反证法",
391
+ "induction": "数学归纳法",
392
+ "contrapositive": "逆否命题法"
393
+ }
394
+
395
+ return f"""请使用{method_descriptions.get(method, method)}证明以下定理:
396
+ {theorem}
397
+
398
+ 请按照以下格式进行证明:
399
+ 1. 明确已知条件和要证明的结论
400
+ 2. 证明的详细步骤
401
+ 3. 最终结论
402
+
403
+ 每个步骤都要有充分的理由和依据。"""
404
+
405
+
406
+ @mcp.prompt()
407
+ def create_graph(function_type: str, parameters: dict) -> str:
408
+ """生成创建图形的提示"""
409
+ return f"""请绘制以下{function_type}函数的图像:
410
+
411
+ 函数参数:{parameters}
412
+
413
+ 请在坐标系中绘制该函数的图像,并:
414
+ 1. 标出关键点(如极值点、零点等)
415
+ 2. 标出渐近线(如果有)
416
+ 3. 标出定义域和值域
417
+ 4. 添加适当的坐标轴标签和标题
418
+
419
+ 使用表格或列表形式说明图像的特征。"""
420
+
421
+
422
+ # ===== 单位转换工具 =====
423
+
424
+ @mcp.tool()
425
+ def angle_convert(angle: float, from_unit: str, to_unit: str) -> float:
426
+ """角度单位转换"""
427
+ # 转换为弧度
428
+ if from_unit == "degree":
429
+ radians = math.radians(angle)
430
+ elif from_unit == "radian":
431
+ radians = angle
432
+ else:
433
+ raise ValueError("不支持的输入单位,请使用 'degree' 或 'radian'")
434
+
435
+ # 从弧度转换到目标单位
436
+ if to_unit == "degree":
437
+ return math.degrees(radians)
438
+ elif to_unit == "radian":
439
+ return radians
440
+ else:
441
+ raise ValueError("不支持的输出单位,请使用 'degree' 或 'radian'")
442
+
443
+ def main() -> None:
444
+ mcp.run(transport="stdio")