sym-mcp 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.
- sym_mcp-0.1.0/LICENSE +21 -0
- sym_mcp-0.1.0/PKG-INFO +274 -0
- sym_mcp-0.1.0/README.md +247 -0
- sym_mcp-0.1.0/pyproject.toml +51 -0
- sym_mcp-0.1.0/setup.cfg +4 -0
- sym_mcp-0.1.0/src/sym_mcp/__init__.py +2 -0
- sym_mcp-0.1.0/src/sym_mcp/__main__.py +6 -0
- sym_mcp-0.1.0/src/sym_mcp/config.py +27 -0
- sym_mcp-0.1.0/src/sym_mcp/errors/__init__.py +2 -0
- sym_mcp-0.1.0/src/sym_mcp/errors/parser.py +113 -0
- sym_mcp-0.1.0/src/sym_mcp/executor/__init__.py +2 -0
- sym_mcp-0.1.0/src/sym_mcp/executor/pool.py +182 -0
- sym_mcp-0.1.0/src/sym_mcp/executor/sandbox.py +82 -0
- sym_mcp-0.1.0/src/sym_mcp/executor/worker_main.py +74 -0
- sym_mcp-0.1.0/src/sym_mcp/schemas.py +8 -0
- sym_mcp-0.1.0/src/sym_mcp/security/__init__.py +2 -0
- sym_mcp-0.1.0/src/sym_mcp/security/ast_guard.py +190 -0
- sym_mcp-0.1.0/src/sym_mcp/server.py +193 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/PKG-INFO +274 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/SOURCES.txt +29 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/dependency_links.txt +1 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/entry_points.txt +2 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/requires.txt +7 -0
- sym_mcp-0.1.0/src/sym_mcp.egg-info/top_level.txt +1 -0
- sym_mcp-0.1.0/tests/test_ast_guard.py +47 -0
- sym_mcp-0.1.0/tests/test_error_parser.py +55 -0
- sym_mcp-0.1.0/tests/test_math_correctness.py +40 -0
- sym_mcp-0.1.0/tests/test_perf_guardrails.py +64 -0
- sym_mcp-0.1.0/tests/test_pool.py +66 -0
- sym_mcp-0.1.0/tests/test_security_adversarial.py +41 -0
- sym_mcp-0.1.0/tests/test_server_integration.py +115 -0
sym_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tyy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sym_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sym-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SymPy sandbox MCP server based on FastMCP
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/Eis4TY/Sym-MCP
|
|
7
|
+
Project-URL: Repository, https://github.com/Eis4TY/Sym-MCP
|
|
8
|
+
Project-URL: Issues, https://github.com/Eis4TY/Sym-MCP/issues
|
|
9
|
+
Keywords: mcp,sympy,sandbox,agent,llm,math
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: fastmcp>=3.0.0
|
|
21
|
+
Requires-Dist: sympy>=1.13.0
|
|
22
|
+
Requires-Dist: pydantic>=2.8.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# SymPy Sandbox MCP
|
|
29
|
+
|
|
30
|
+
一个面向 Agent/LLM 的 SymPy 计算沙箱 MCP 服务。
|
|
31
|
+
目标是:在保证安全隔离的前提下,提供低延迟、低 token、可稳定重试的数学计算工具。
|
|
32
|
+
|
|
33
|
+
## 特性
|
|
34
|
+
|
|
35
|
+
- 单工具 `sympy`,输入参数仅 `code`
|
|
36
|
+
- 预热进程池,避免每次请求重复 `import sympy`
|
|
37
|
+
- 双层安全防护:AST 拦截 + 运行时资源限制
|
|
38
|
+
- 统一紧凑 JSON 输出,便于程序解析且节省 token
|
|
39
|
+
- 错误码标准化,方便 Agent 自动纠错重试
|
|
40
|
+
|
|
41
|
+
## 适用场景
|
|
42
|
+
|
|
43
|
+
- 让 LLM 执行代数化简、求导、积分、方程求解、符号推导
|
|
44
|
+
- 作为 MCP Tool 接入 Codex / Cursor / Claude Desktop / 自建 MCP Client
|
|
45
|
+
- 需要“可控失败 + 低噪声错误信息”的自动化 Agent 工作流
|
|
46
|
+
|
|
47
|
+
## 快速开始
|
|
48
|
+
|
|
49
|
+
### 1) 环境要求
|
|
50
|
+
|
|
51
|
+
- Python 3.11+
|
|
52
|
+
- Linux / macOS(推荐 Linux 作为生产环境)
|
|
53
|
+
|
|
54
|
+
### 2) 安装(清华源优先)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e .
|
|
58
|
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e ".[dev]"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3) 启动服务(stdio)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python -m sym_mcp.server
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 4) 验证工具可用
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
fastmcp list --command 'python -m sym_mcp.server'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 通过 PyPI 安装(推荐给最终用户)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install -U sym-mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
安装后可直接运行:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
sym-mcp
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
或临时运行(不落地安装,推荐给体验用户):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uvx sym-mcp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 工具接口(对外契约)
|
|
92
|
+
|
|
93
|
+
### Tool 名称
|
|
94
|
+
|
|
95
|
+
- `sympy`
|
|
96
|
+
|
|
97
|
+
### 输入
|
|
98
|
+
|
|
99
|
+
- `code: str`
|
|
100
|
+
|
|
101
|
+
约定:
|
|
102
|
+
- 代码必须使用 `print()` 输出最终结果
|
|
103
|
+
- 如果不 `print`,`out` 可能为空字符串
|
|
104
|
+
|
|
105
|
+
### 输出(固定为紧凑 JSON 字符串)
|
|
106
|
+
|
|
107
|
+
成功:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{"ok":1,"out":"x**2/2","meta":{"trunc":0,"ms":23}}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
失败:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{"ok":0,"code":"E_RUNTIME","line":3,"err":"ZeroDivisionError: division by zero","hint":"运行时错误。请根据行号检查变量类型、零除、未定义变量等问题后重试。","meta":{"trunc":0,"ms":31}}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
字段定义:
|
|
120
|
+
|
|
121
|
+
- `ok`: `1` 成功,`0` 失败
|
|
122
|
+
- `out`: 成功输出文本(来自 stdout)
|
|
123
|
+
- `code`: 错误码
|
|
124
|
+
- `line`: 用户代码报错行号;无则 `null`
|
|
125
|
+
- `err`: 精简错误信息(去除 traceback 噪声)
|
|
126
|
+
- `hint`: 修复建议(按配置等级生成)
|
|
127
|
+
- `meta.trunc`: 是否发生截断(`1/0`)
|
|
128
|
+
- `meta.ms`: 执行耗时(毫秒)
|
|
129
|
+
|
|
130
|
+
## 错误码说明
|
|
131
|
+
|
|
132
|
+
- `E_AST_BLOCK`: AST 安全拦截(危险导入/调用/双下划线穿透)
|
|
133
|
+
- `E_SYNTAX`: 语法错误
|
|
134
|
+
- `E_TIMEOUT`: 超时(死循环或计算过慢)
|
|
135
|
+
- `E_MEMORY`: 内存限制触发
|
|
136
|
+
- `E_RUNTIME`: 一般运行时错误
|
|
137
|
+
- `E_WORKER`: Worker 通讯/状态异常
|
|
138
|
+
- `E_INTERNAL`: 服务内部异常
|
|
139
|
+
|
|
140
|
+
## 给 LLM/Agent 的调用规范(建议直接放系统提示)
|
|
141
|
+
|
|
142
|
+
1. 仅写数学代码,禁止文件、网络、系统调用。
|
|
143
|
+
2. 仅导入 `sympy` / `math`。
|
|
144
|
+
3. 最终答案必须 `print()`。
|
|
145
|
+
4. 多个结果使用多行 `print()`。
|
|
146
|
+
5. 收到失败结果时,只修改 `line` 附近最小范围代码并重试。
|
|
147
|
+
6. `E_TIMEOUT` 先缩小规模再算;`E_MEMORY` 先减小对象维度;`E_AST_BLOCK` 删除不安全语句。
|
|
148
|
+
|
|
149
|
+
推荐模板:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import sympy as sp
|
|
153
|
+
x = sp.Symbol("x")
|
|
154
|
+
expr = (x + 1)**5
|
|
155
|
+
print(sp.expand(expr))
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 安全模型
|
|
159
|
+
|
|
160
|
+
### 执行前(AST 白名单)
|
|
161
|
+
|
|
162
|
+
- 仅允许 `sympy` / `math` 导入
|
|
163
|
+
- 禁止 `eval/exec/open/__import__` 等危险能力
|
|
164
|
+
- 禁止双下划线属性穿透(如 `__class__`)
|
|
165
|
+
|
|
166
|
+
### 执行中(OS 资源限制)
|
|
167
|
+
|
|
168
|
+
- 子进程 CPU 时间限制 + 超时强杀
|
|
169
|
+
- 子进程内存限制(`setrlimit`)
|
|
170
|
+
- Worker 异常自动重建,不影响主服务
|
|
171
|
+
|
|
172
|
+
## 架构总览
|
|
173
|
+
|
|
174
|
+
- `src/sym_mcp/server.py`: MCP 入口与工具注册
|
|
175
|
+
- `src/sym_mcp/security/ast_guard.py`: AST 安全校验
|
|
176
|
+
- `src/sym_mcp/executor/worker_main.py`: Worker 进程执行循环
|
|
177
|
+
- `src/sym_mcp/executor/pool.py`: 异步预热进程池
|
|
178
|
+
- `src/sym_mcp/executor/sandbox.py`: 受限执行环境与输出捕获
|
|
179
|
+
- `src/sym_mcp/errors/parser.py`: 错误降噪与错误码映射
|
|
180
|
+
- `src/sym_mcp/config.py`: 运行配置
|
|
181
|
+
|
|
182
|
+
## 配置项(环境变量)
|
|
183
|
+
|
|
184
|
+
- `SYMMCP_POOL_SIZE`:进程池大小,默认 `10`
|
|
185
|
+
- `SYMMCP_EXEC_TIMEOUT_SEC`:单次执行超时秒数,默认 `3`
|
|
186
|
+
- `SYMMCP_MEMORY_LIMIT_MB`:单 worker 内存上限 MB,默认 `150`
|
|
187
|
+
- `SYMMCP_QUEUE_WAIT_SEC`:队列等待超时秒数,默认 `2`
|
|
188
|
+
- `SYMMCP_LOG_LEVEL`:日志级别,默认 `INFO`
|
|
189
|
+
- `SYMMCP_MAX_OUTPUT_CHARS`:输出截断阈值,默认 `1200`
|
|
190
|
+
- `SYMMCP_HINT_LEVEL`:提示等级(`none/short/medium`),默认 `medium`
|
|
191
|
+
|
|
192
|
+
## 本地开发
|
|
193
|
+
|
|
194
|
+
### 运行测试
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
PYTHONPATH=src pytest -q
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 基准压测
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
PYTHONPATH=src python scripts/benchmark.py --concurrency 100 --total 500
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## MCP 客户端接入示例(stdio)
|
|
207
|
+
|
|
208
|
+
示例命令:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
fastmcp call \
|
|
212
|
+
--command 'python -m sym_mcp.server' \
|
|
213
|
+
--target sympy \
|
|
214
|
+
--input-json '{"code":"import sympy as sp\nx=sp.Symbol(\"x\")\nprint(sp.factor(x**2-1))"}'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
示例配置(安装为命令 `sym-mcp` 后):
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"mcpServers": {
|
|
222
|
+
"sympy-sandbox": {
|
|
223
|
+
"command": "sym-mcp",
|
|
224
|
+
"args": []
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
示例配置(使用 `uvx`):
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"mcpServers": {
|
|
235
|
+
"sympy-sandbox": {
|
|
236
|
+
"command": "uvx",
|
|
237
|
+
"args": ["sym-mcp"]
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## 常见问题
|
|
244
|
+
|
|
245
|
+
### 1) 为什么结果为空?
|
|
246
|
+
|
|
247
|
+
通常是代码未 `print` 最终结果。请显式 `print(...)`。
|
|
248
|
+
|
|
249
|
+
### 2) 为什么返回 JSON 字符串而不是纯文本?
|
|
250
|
+
|
|
251
|
+
为便于 Agent 稳定解析并减少 token,返回固定结构化紧凑 JSON。
|
|
252
|
+
|
|
253
|
+
### 3) macOS 下内存限制有时不稳定?
|
|
254
|
+
|
|
255
|
+
`setrlimit` 在不同系统语义不同。生产建议优先 Linux。
|
|
256
|
+
|
|
257
|
+
### 4) 可以支持 HTTP/SSE 吗?
|
|
258
|
+
|
|
259
|
+
当前主交付是 `stdio`。后续可按 FastMCP 方式扩展 HTTP/SSE 传输层。
|
|
260
|
+
|
|
261
|
+
## 已知边界
|
|
262
|
+
|
|
263
|
+
- 当前为“受限 Python 执行”,不是内核虚拟化级别隔离
|
|
264
|
+
- 内存限制依赖 OS 层实现,跨平台表现会有差异
|
|
265
|
+
- 输出会按阈值截断,请调用方检查 `meta.trunc`
|
|
266
|
+
|
|
267
|
+
## 贡献建议
|
|
268
|
+
|
|
269
|
+
- 提交 PR 前先运行 `PYTHONPATH=src pytest -q`
|
|
270
|
+
- 新增能力时请同步更新:
|
|
271
|
+
- 错误码文档
|
|
272
|
+
- README 示例
|
|
273
|
+
- 对应单元测试 / 集成测试
|
|
274
|
+
- 发布流程见 [PUBLISHING.md](./PUBLISHING.md)
|
sym_mcp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# SymPy Sandbox MCP
|
|
2
|
+
|
|
3
|
+
一个面向 Agent/LLM 的 SymPy 计算沙箱 MCP 服务。
|
|
4
|
+
目标是:在保证安全隔离的前提下,提供低延迟、低 token、可稳定重试的数学计算工具。
|
|
5
|
+
|
|
6
|
+
## 特性
|
|
7
|
+
|
|
8
|
+
- 单工具 `sympy`,输入参数仅 `code`
|
|
9
|
+
- 预热进程池,避免每次请求重复 `import sympy`
|
|
10
|
+
- 双层安全防护:AST 拦截 + 运行时资源限制
|
|
11
|
+
- 统一紧凑 JSON 输出,便于程序解析且节省 token
|
|
12
|
+
- 错误码标准化,方便 Agent 自动纠错重试
|
|
13
|
+
|
|
14
|
+
## 适用场景
|
|
15
|
+
|
|
16
|
+
- 让 LLM 执行代数化简、求导、积分、方程求解、符号推导
|
|
17
|
+
- 作为 MCP Tool 接入 Codex / Cursor / Claude Desktop / 自建 MCP Client
|
|
18
|
+
- 需要“可控失败 + 低噪声错误信息”的自动化 Agent 工作流
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
### 1) 环境要求
|
|
23
|
+
|
|
24
|
+
- Python 3.11+
|
|
25
|
+
- Linux / macOS(推荐 Linux 作为生产环境)
|
|
26
|
+
|
|
27
|
+
### 2) 安装(清华源优先)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e .
|
|
31
|
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e ".[dev]"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3) 启动服务(stdio)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python -m sym_mcp.server
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 4) 验证工具可用
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
fastmcp list --command 'python -m sym_mcp.server'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 通过 PyPI 安装(推荐给最终用户)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install -U sym-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
安装后可直接运行:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sym-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
或临时运行(不落地安装,推荐给体验用户):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uvx sym-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 工具接口(对外契约)
|
|
65
|
+
|
|
66
|
+
### Tool 名称
|
|
67
|
+
|
|
68
|
+
- `sympy`
|
|
69
|
+
|
|
70
|
+
### 输入
|
|
71
|
+
|
|
72
|
+
- `code: str`
|
|
73
|
+
|
|
74
|
+
约定:
|
|
75
|
+
- 代码必须使用 `print()` 输出最终结果
|
|
76
|
+
- 如果不 `print`,`out` 可能为空字符串
|
|
77
|
+
|
|
78
|
+
### 输出(固定为紧凑 JSON 字符串)
|
|
79
|
+
|
|
80
|
+
成功:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{"ok":1,"out":"x**2/2","meta":{"trunc":0,"ms":23}}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
失败:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{"ok":0,"code":"E_RUNTIME","line":3,"err":"ZeroDivisionError: division by zero","hint":"运行时错误。请根据行号检查变量类型、零除、未定义变量等问题后重试。","meta":{"trunc":0,"ms":31}}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
字段定义:
|
|
93
|
+
|
|
94
|
+
- `ok`: `1` 成功,`0` 失败
|
|
95
|
+
- `out`: 成功输出文本(来自 stdout)
|
|
96
|
+
- `code`: 错误码
|
|
97
|
+
- `line`: 用户代码报错行号;无则 `null`
|
|
98
|
+
- `err`: 精简错误信息(去除 traceback 噪声)
|
|
99
|
+
- `hint`: 修复建议(按配置等级生成)
|
|
100
|
+
- `meta.trunc`: 是否发生截断(`1/0`)
|
|
101
|
+
- `meta.ms`: 执行耗时(毫秒)
|
|
102
|
+
|
|
103
|
+
## 错误码说明
|
|
104
|
+
|
|
105
|
+
- `E_AST_BLOCK`: AST 安全拦截(危险导入/调用/双下划线穿透)
|
|
106
|
+
- `E_SYNTAX`: 语法错误
|
|
107
|
+
- `E_TIMEOUT`: 超时(死循环或计算过慢)
|
|
108
|
+
- `E_MEMORY`: 内存限制触发
|
|
109
|
+
- `E_RUNTIME`: 一般运行时错误
|
|
110
|
+
- `E_WORKER`: Worker 通讯/状态异常
|
|
111
|
+
- `E_INTERNAL`: 服务内部异常
|
|
112
|
+
|
|
113
|
+
## 给 LLM/Agent 的调用规范(建议直接放系统提示)
|
|
114
|
+
|
|
115
|
+
1. 仅写数学代码,禁止文件、网络、系统调用。
|
|
116
|
+
2. 仅导入 `sympy` / `math`。
|
|
117
|
+
3. 最终答案必须 `print()`。
|
|
118
|
+
4. 多个结果使用多行 `print()`。
|
|
119
|
+
5. 收到失败结果时,只修改 `line` 附近最小范围代码并重试。
|
|
120
|
+
6. `E_TIMEOUT` 先缩小规模再算;`E_MEMORY` 先减小对象维度;`E_AST_BLOCK` 删除不安全语句。
|
|
121
|
+
|
|
122
|
+
推荐模板:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import sympy as sp
|
|
126
|
+
x = sp.Symbol("x")
|
|
127
|
+
expr = (x + 1)**5
|
|
128
|
+
print(sp.expand(expr))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 安全模型
|
|
132
|
+
|
|
133
|
+
### 执行前(AST 白名单)
|
|
134
|
+
|
|
135
|
+
- 仅允许 `sympy` / `math` 导入
|
|
136
|
+
- 禁止 `eval/exec/open/__import__` 等危险能力
|
|
137
|
+
- 禁止双下划线属性穿透(如 `__class__`)
|
|
138
|
+
|
|
139
|
+
### 执行中(OS 资源限制)
|
|
140
|
+
|
|
141
|
+
- 子进程 CPU 时间限制 + 超时强杀
|
|
142
|
+
- 子进程内存限制(`setrlimit`)
|
|
143
|
+
- Worker 异常自动重建,不影响主服务
|
|
144
|
+
|
|
145
|
+
## 架构总览
|
|
146
|
+
|
|
147
|
+
- `src/sym_mcp/server.py`: MCP 入口与工具注册
|
|
148
|
+
- `src/sym_mcp/security/ast_guard.py`: AST 安全校验
|
|
149
|
+
- `src/sym_mcp/executor/worker_main.py`: Worker 进程执行循环
|
|
150
|
+
- `src/sym_mcp/executor/pool.py`: 异步预热进程池
|
|
151
|
+
- `src/sym_mcp/executor/sandbox.py`: 受限执行环境与输出捕获
|
|
152
|
+
- `src/sym_mcp/errors/parser.py`: 错误降噪与错误码映射
|
|
153
|
+
- `src/sym_mcp/config.py`: 运行配置
|
|
154
|
+
|
|
155
|
+
## 配置项(环境变量)
|
|
156
|
+
|
|
157
|
+
- `SYMMCP_POOL_SIZE`:进程池大小,默认 `10`
|
|
158
|
+
- `SYMMCP_EXEC_TIMEOUT_SEC`:单次执行超时秒数,默认 `3`
|
|
159
|
+
- `SYMMCP_MEMORY_LIMIT_MB`:单 worker 内存上限 MB,默认 `150`
|
|
160
|
+
- `SYMMCP_QUEUE_WAIT_SEC`:队列等待超时秒数,默认 `2`
|
|
161
|
+
- `SYMMCP_LOG_LEVEL`:日志级别,默认 `INFO`
|
|
162
|
+
- `SYMMCP_MAX_OUTPUT_CHARS`:输出截断阈值,默认 `1200`
|
|
163
|
+
- `SYMMCP_HINT_LEVEL`:提示等级(`none/short/medium`),默认 `medium`
|
|
164
|
+
|
|
165
|
+
## 本地开发
|
|
166
|
+
|
|
167
|
+
### 运行测试
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
PYTHONPATH=src pytest -q
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 基准压测
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
PYTHONPATH=src python scripts/benchmark.py --concurrency 100 --total 500
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## MCP 客户端接入示例(stdio)
|
|
180
|
+
|
|
181
|
+
示例命令:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
fastmcp call \
|
|
185
|
+
--command 'python -m sym_mcp.server' \
|
|
186
|
+
--target sympy \
|
|
187
|
+
--input-json '{"code":"import sympy as sp\nx=sp.Symbol(\"x\")\nprint(sp.factor(x**2-1))"}'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
示例配置(安装为命令 `sym-mcp` 后):
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"mcpServers": {
|
|
195
|
+
"sympy-sandbox": {
|
|
196
|
+
"command": "sym-mcp",
|
|
197
|
+
"args": []
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
示例配置(使用 `uvx`):
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"mcpServers": {
|
|
208
|
+
"sympy-sandbox": {
|
|
209
|
+
"command": "uvx",
|
|
210
|
+
"args": ["sym-mcp"]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 常见问题
|
|
217
|
+
|
|
218
|
+
### 1) 为什么结果为空?
|
|
219
|
+
|
|
220
|
+
通常是代码未 `print` 最终结果。请显式 `print(...)`。
|
|
221
|
+
|
|
222
|
+
### 2) 为什么返回 JSON 字符串而不是纯文本?
|
|
223
|
+
|
|
224
|
+
为便于 Agent 稳定解析并减少 token,返回固定结构化紧凑 JSON。
|
|
225
|
+
|
|
226
|
+
### 3) macOS 下内存限制有时不稳定?
|
|
227
|
+
|
|
228
|
+
`setrlimit` 在不同系统语义不同。生产建议优先 Linux。
|
|
229
|
+
|
|
230
|
+
### 4) 可以支持 HTTP/SSE 吗?
|
|
231
|
+
|
|
232
|
+
当前主交付是 `stdio`。后续可按 FastMCP 方式扩展 HTTP/SSE 传输层。
|
|
233
|
+
|
|
234
|
+
## 已知边界
|
|
235
|
+
|
|
236
|
+
- 当前为“受限 Python 执行”,不是内核虚拟化级别隔离
|
|
237
|
+
- 内存限制依赖 OS 层实现,跨平台表现会有差异
|
|
238
|
+
- 输出会按阈值截断,请调用方检查 `meta.trunc`
|
|
239
|
+
|
|
240
|
+
## 贡献建议
|
|
241
|
+
|
|
242
|
+
- 提交 PR 前先运行 `PYTHONPATH=src pytest -q`
|
|
243
|
+
- 新增能力时请同步更新:
|
|
244
|
+
- 错误码文档
|
|
245
|
+
- README 示例
|
|
246
|
+
- 对应单元测试 / 集成测试
|
|
247
|
+
- 发布流程见 [PUBLISHING.md](./PUBLISHING.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sym-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SymPy sandbox MCP server based on FastMCP"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = ["mcp", "sympy", "sandbox", "agent", "llm", "math"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
20
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"fastmcp>=3.0.0",
|
|
24
|
+
"sympy>=1.13.0",
|
|
25
|
+
"pydantic>=2.8.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/Eis4TY/Sym-MCP"
|
|
30
|
+
Repository = "https://github.com/Eis4TY/Sym-MCP"
|
|
31
|
+
Issues = "https://github.com/Eis4TY/Sym-MCP/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
sym-mcp = "sym_mcp.server:main"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=8.0.0",
|
|
39
|
+
"pytest-asyncio>=0.23.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
asyncio_mode = "auto"
|
|
44
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools]
|
|
48
|
+
package-dir = { "" = "src" }
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
sym_mcp-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class Settings:
|
|
9
|
+
pool_size: int = 10
|
|
10
|
+
exec_timeout_sec: float = 3.0
|
|
11
|
+
memory_limit_mb: int = 150
|
|
12
|
+
queue_wait_sec: float = 2.0
|
|
13
|
+
log_level: str = "INFO"
|
|
14
|
+
max_output_chars: int = 1200
|
|
15
|
+
hint_level: str = "medium"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_settings() -> Settings:
|
|
19
|
+
return Settings(
|
|
20
|
+
pool_size=int(os.getenv("SYMMCP_POOL_SIZE", "10")),
|
|
21
|
+
exec_timeout_sec=float(os.getenv("SYMMCP_EXEC_TIMEOUT_SEC", "3")),
|
|
22
|
+
memory_limit_mb=int(os.getenv("SYMMCP_MEMORY_LIMIT_MB", "150")),
|
|
23
|
+
queue_wait_sec=float(os.getenv("SYMMCP_QUEUE_WAIT_SEC", "2")),
|
|
24
|
+
log_level=os.getenv("SYMMCP_LOG_LEVEL", "INFO"),
|
|
25
|
+
max_output_chars=max(100, int(os.getenv("SYMMCP_MAX_OUTPUT_CHARS", "1200"))),
|
|
26
|
+
hint_level=os.getenv("SYMMCP_HINT_LEVEL", "medium"),
|
|
27
|
+
)
|