exact-calc について
exact-calc は、MCP またはコマンドラインから AI エージェント向けに厳密な算術計算を提供します。Python の計算結果を独立実装の C++ エンジンで照合し、任意コードを実行せず AST の許可リストで式を評価します。
主な機能
- 01分数と十進数の式を厳密に計算
- 02Python と C++ の計算結果を照合
- 03式同士の数学的な等価性を判定
- 04十進文字列を分数に変換
- 05AST の許可リストで安全に評価
活用シーン
- 01財務や科学計算の結果を検証
- 02十進演算の浮動小数点誤差を回避
- 03二つの式が等価か確認
- 04MCP またはコマンドラインから計算を実行
MCP 対応
計算、検証、式の等価性判定、十進数から分数への変換、エンジン状態確認のツールを MCP クライアントに公開します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
5掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| precision | integer | いいえ | |
| expression | string | はい |
詳細
exact-calc-mcp
AI にコンピューターの Python と C++ で計算させる
AI エージェント向けの精密計算ツールです。MCP またはコマンドラインから呼び出すと、独立して実装された2つのエンジンで相互検証した後にのみ結果を返します。
英語:AI エージェント向けの精密演算です。LLM は計算せず、もっともらしく見えるトークンを生成します。この MCP サーバーは、独立して実装された2つのエンジン(正確な Python 有理数/十進演算と、別個の C++ 評価器)に算術演算を委ね、両者の結果が一致した場合にのみ結果を返します。
問題
これは「モデルの賢さが足りない」という話ではなく、アーキテクチャによって決まる問題です。言語モデルは自己回帰型の生成器であり、出力するのは統計的にもっとも答えらしく見えるトークン列です。算術回路も、桁上がりの連鎖も、中間レジスターもありません。
そのため、0.1 + 0.2 と尋ねても、計算しているのではなく、「この会話の続きとして通常何がくるか」を思い出しています。ほとんどの場合、0.3 と答えます。——学習コーパスにそのように書かれているからです。答えはたまたま正しいのですが、正しく計算したからではありません。
しかし、次のようなケースでは安定して間違えます。
| 式 | 二進浮動小数点(Python float) | 本ツール |
|---|---|---|
0.1 + 0.2 | 0.30000000000000004 | 3/10 |
0.3 - 0.1 | 0.19999999999999998 | 1/5 |
1/3 | 0.3333333333333333 | 1/3 |
sqrt(2) ** 2 | 2.0000000000000004 | 1.999…9(50 桁の精度内で、誤差は 1e-49) |
1e16 + 1 | 1e+16(+1 が失われる) | 10000000000000001 |
2 ** 100 | 1.2676506002282294e+30 | 1267650600228229401496703205376 |
最後の2行の違いに注意してください。2 ** 100 は float でも実は正確です(2の累乗なので)。そのため、浮動小数点誤差の例として扱うべきではありません。真の例は 1e16 + 1 で、そこでは +1 が完全に失われます。例を選ぶときも、厳密さが必要です。
結論:モデルに計算させないでください。 意図を理解して手順を組み立てる役割はモデルに任せ、確定的な計算はプログラムに外部委託しましょう。このプロジェクトはそれを実現します。
他の計算機 MCP との違い
多くの calculator MCP は、単に eval() をラップしたものです。これは違います。
一般的な eval() 版 | 本ツール | |
|---|---|---|
| 数値型 | float(二進数で誤差あり) | Fraction / Decimal(正確) |
0.1 + 0.2 | 0.30000000000000004 | 3/10 |
| 安全性 | eval() で、インジェクションされる可能性あり | ast のホワイトリストで、ノードごとに評価 |
| 結果の信頼性 | 単一実装のため、間違っていてもわからない | 2つのエンジンで相互検証 |
| リテラルの処理 | ast 内の float をそのまま使用 | ソーステキストから再構築 |
最後の点は見落とされやすい一方で、もっとも重要です。以下で個別に説明します。
インストール
pip install -e .
依存するのは公式の mcp SDK(1.x に固定。詳細は下記)だけです。C++ エンジンはオプションで、コンパイラーが見つからない場合は自動的にスキップされ、使用に影響しません。
mcp<2に固定する理由:mcp 2.0 ではFastMCPがMCPServer(mcp.server.mcpserver)に改名され、mcp.server.fastmcpは明示的にModuleNotFoundErrorを発生させるプレースホルダーモジュールになりました。本プロジェクトは 1.x の API に基づいているため、pyproject.tomlにはmcp>=1.0.0,<2.0.0と記載しています。2.x への移行については、 公式移行ガイドを参照してください。
使い方 1:MCP クライアントに接続する
MCP に対応するクライアントなら、どれでもそのまま使用できます。以下では、主なクライアントをいくつか取り上げます。
まず、次の2つのコマンドを覚えておいてください。以下の設定はすべて、書き方が異なるだけです。
# 不用安装(uv 会自动拉到临时环境)
uvx --from git+https://github.com/flacales/exact-calc-mcp exact-calc --serve
# 或本地装好后
pip install -e . && python -m exact_calc_mcp --serve
Claude Desktop
claude_desktop_config.json を編集します(macOS:~/Library/Application Support/Claude/;
Windows:%APPDATA%\Claude\):
{
"mcpServers": {
"exact-calc": {
"command": "uvx",
"args": ["--from", "git+https://github.com/flacales/exact-calc-mcp", "exact-calc", "--serve"]
}
}
}
Claude Code
claude mcp add exact-calc -- uvx --from git+https://github.com/flacales/exact-calc-mcp exact-calc --serve
Cursor
~/.cursor/mcp.json(グローバル)またはプロジェクト内の .cursor/mcp.json を編集します:
{
"mcpServers": {
"exact-calc": {
"command": "uvx",
"args": ["--from", "git+https://github.com/flacales/exact-calc-mcp", "exact-calc", "--serve"]
}
}
}
VS Code
.vscode/mcp.json(ワークスペース)またはユーザー設定の mcp セクションを編集します。
VS Code ではキーに mcpServers ではなく servers を使い、type を明示的に記述する必要があります:
{
"servers": {
"exact-calc": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "git+https://github.com/flacales/exact-calc-mcp", "exact-calc", "--serve"]
}
}
}
Codex
~/.codex/config.toml を編集します:
[mcp_servers.exact-calc]
command = "uvx"
args = ["--from", "git+https://github.com/flacales/exact-calc-mcp", "exact-calc", "--serve"]
Cline / Continue / その他
基本的に設定の構造は同じで、mcpServers の下に項目を1つ追加します:
{
"mcpServers": {
"exact-calc": {
"command": "uvx",
"args": ["--from", "git+https://github.com/flacales/exact-calc-mcp", "exact-calc", "--serve"]
}
}
}
uvxを使いたくない場合は、commandをpythonに、argsを["-m", "exact_calc_mcp", "--serve"]に変更してもかまいません。その Python 環境に本プロジェクトがインストールされている必要があります。
提供されるツール
| ツール | 用途 |
|---|---|
calculate(expression, precision=50) | 精密に計算し、結果と2系統の検証内容を返します |
verify(expression, precision=50) | 同上ですが、最初に「信頼できる/信頼できない」と判定します |
are_equal(left, right, precision=50) | 2つの式が数学的に等しいかどうかを判定します |
to_fraction(decimal_string) | 小数を正確な分数に戻します |
engine_status() | 2つのエンジンの状態を報告します |
calculate が返す構造:
{
"expression": "0.1 + 0.2",
"value": "3/10",
"domain": "fraction",
"exact": true,
"cross_check": "通过(Fraction 与 Decimal 结果一致)",
"cpp_check": "通过(Python 与 C++ 独立实现结果完全一致:0.3)",
"note": ""
}
用法二:命令行
不需要 MCP 客户端也能用 —— 任何能执行 shell 的 agent 都可以调。
# 计算
exact-calc "0.1 + 0.2"
# 0.1 + 0.2 = 3/10
# 数值域 : fraction(无损)
# Python 自检: 通过(Fraction 与 Decimal 结果一致)
# C++ 独立复核: 通过(Python 与 C++ 独立实现结果完全一致:0.3)
# 给程序解析用的 JSON
exact-calc --json "1/3 + 1/6"
# 判断两个表达式是否相等(退出码 0 相等 / 1 不等)
exact-calc --compare "0.1 + 0.2" "0.3"
# 小数转精确分数
exact-calc --fraction 0.1 # 1/10
# 检查引擎状态
exact-calc --doctor
支持的语法:+ - * / // % ** 和括号,函数 sqrt abs round min max factorial gcd lcm ln log log10 exp pow,常量 pi、e。运算符优先级与 Python 完全一致(-2 ** 2 是 -4,2 ** 3 ** 2 是 512)。两点和 Python 直觉不同:log(x) 是自然对数(与 math.log 一致),log(x, base) 指定底数,常用对数用 log10;round 是远离零的四舍五入(round(2.5) = 3),不是 Python 内建的银行家舍入。
架构
┌──────────────────────────────────────┐
表达式 ──▶ │ ast 解析 + 白名单校验(不用 eval) │
└───────────────┬──────────────────────┘
│
┌───────────────▼──────────────────────┐
│ 从**源码文本**重建数字字面量 │
│ "0.1" ──▶ Decimal("0.1"),不经过 float│
└───────────────┬──────────────────────┘
│
┌─────────────────────┴─────────────────────┐
│ │
┌────▼─────────────┐ ┌────────▼──────────┐
│ Python 引擎 │ │ C++ 引擎 │
│ Fraction / Decimal│ │ 手写递归下降解析器 │
│ 任意精度,精确 │ │ long double │
└────┬─────────────┘ └────────┬──────────┘
│ │
│ Fraction ◀─对比──▶ Decimal │
│ │
└───────────────┬───────────────────────────┘
│
┌─────────▼──────────┐
│ 两路一致才返回 │
│ 不一致就明确报警 │
└────────────────────┘
四个设计决定
1. 不用 eval()
eval() 会把 __import__('os').system('rm -rf /') 当成正常表达式执行。这里用 ast 解析成语法树后逐节点求值,只放行白名单里的节点类型和函数名。Attribute、Subscript、Lambda、IfExp、Comprehension 全部拒绝。
函数名先于实参校验 —— 否则 open('x','w') 会先因为字符串参数报一个"无法识别的数字",把真正的拒绝理由盖掉。
2. 数字从源码文本重建(最关键的一条)
ast.parse("0.1") # Constant(value=0.1) ← 已经是二进制近似了
0.1 在 ast 里已经变成 float(0.1),而它的真实值是
0.1000000000000000055511151231257827021181583404541015625
精度在拿到它的那一刻就丢了。所以这里用 ast.get_source_segment() 取回源码里的字符串 "0.1",再交给 Decimal("0.1") —— 这才是精确的十进制 0.1。
去掉这一步,整个项目的精确性主张都是空话。有专门的测试守着这条(test_数字字面量从源码文本还原)。
3. 双数值域,自动降级
Fraction—— 有理数精确。分数运算、完全平方数的开方走这条。Decimal—— 十进制精确。无理数(sqrt(2)、pi、ln)走这条。
先试 Fraction,装不下就自动降级到 Decimal,并在结果里注明为什么。sqrt(144) 仍然是精确的 12,因为它能开尽。
4. 双引擎交叉验证
一个实现错了,你不会知道。 所以这里有两个:
- Python 引擎:
Fraction/Decimal,任意精度,精确 - C++ 引擎:手写的递归下降解析器,
long double,故意不精确
C++ 那边只回答"量级和有效数字对得上吗",不参与精度判定。两边对不上就明确报警,而不是返回一个可疑的数字。
C++ 引擎明确不支持的函数(factorial、gcd 等)会返回退出码 2,调用方据此跳过验证,而不是误报不一致。
关于 C++ 源码:它全部是 ASCII 的。中文标识符在 MSVC、clang、gcc 之间行为不一致,一个要发到 GitHub 上的 C++ 文件不该埋这个雷。
局限(诚实说明)
- 不支持复数:
sqrt(-1)会报错。 - 三角/双曲函数未实现:
Decimal没有这些函数,需要它们的话得引入mpmath。 %对负数取模的行为:Python 侧用operator.mod(结果为负时向负无穷取整),C++ 侧用fmodl(向零取整)。两者对负操作数的结果不同,这类表达式会被交叉验证判为不一致 —— 这是已知的行为差异,不是 bug。- C++ 引擎精度有限:
long double约 18~19 位有效十进制。超过这个量级的验证会以"相对误差 1e-18,差异来自精度上限"的形式通过。 0x10这类十六进制字面量:Python 引擎支持,C++ 引擎不支持(strtold要求十六进制浮点必须带p指数),会走"已跳过"。round是远离零舍入:round(2.5) = 3、round(-2.5) = -3。和 Python 内建round()的银行家舍入(round(2.5) = 2)不同 —— 这是故意的,三个引擎(Fraction / Decimal / C++roundl)在这件事上保持一致,比跟随 Python 的内建行为更重要。- 资源护栏:整数次幂的指数上限 ±10,000,000(结果约 300 万位);
factorial参数上限 1,000,000;整数转字符串上限 1,000 万位(Python 3.11+ 默认只有 4300 位,本工具已放宽);指数超过 1e±100000 的结果以科学计数法表示(如1e+999999999);工作精度限定在 1~100,000 位。超出护栏的表达式会被明确拒绝,而不是把机器算死。 pi、e预存 1020 位有效数字:超过这个位数的常数相关计算以预存值为准(sqrt(2)、ln(2)这类现场计算的值不受此限,随精度参数走)。- 它不是符号计算系统:不做化简、解方程、求导。需要这些请上
sympy。
测试
python -m unittest discover -s tests -v
44 个用例,全绿,覆盖四块:
| 文件 | 用例 | 测什么 |
|---|---|---|
tests/test_engine.py | 38 | 精确性、安全性、交叉验证 |
tests/test_mcp_protocol.py | 6 | MCP 协议层:stdio 上的 JSON-RPC 握手、tools/list、tools/call、注入拒绝 |
具体地:
- 精确性:浮点误差被消除、大整数不丢精度、优先级与 Python 一致
- 安全性:14 个注入/越权表达式全部被拒
- 交叉验证:两路结果一致;C++ 超范围时正确跳过而非误报
- 协议:
serverInfo.version是项目版本而不是 mcp SDK 的版本; 5 个工具都在且都有描述(描述是给模型读的 prompt,缺了模型就不会调)
为什么值得做这件事
给 AI 加一个计算器,听起来是个小工具。但它背后的原则很大:
模型负责理解与编排,程序负责确定性计算。
把这条原则推到底,就是 agent 工程的核心 —— 凡是"有唯一正确答案"的事,都不该交给一个概率模型去猜。日期计算、单位换算、财务对账、代码执行,全都一样。
这个项目是那条原则最小的一个实例。
贡献
欢迎 issue 和 PR。开始之前请读 CONTRIBUTING.md。
跑测试只要一条命令,不需要额外依赖:
python -m unittest discover -s tests -v
安全
这个工具不执行任意代码 —— 表达式经 ast 白名单逐节点求值,不走 eval(),
属性访问、下标、lambda、推导式一律拒绝。相关测试在
tests/test_engine.py 的「安全性」一节。
如果你发现了绕过白名单、拒绝服务或资源护栏失效的问题, 请按 SECURITY.md 的方式私下报告,不要开公开 issue。
变更记录
见 CHANGELOG.md。
License
MIT —— 见 LICENSE。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。