poker-mcp 1.8.3 → 1.9.8
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.
- package/LICENSE +21 -0
- package/README.md +126 -15
- package/package.json +68 -62
- package/src/mcp/handlers/cadHandlers.js +202 -1
- package/src/mcp/handlers/index.js +6 -2
- package/src/mcp/handlers/schemaHandlers.js +78 -0
- package/src/mcp/server.js +15 -1
- package/src/mcp/tools/cadTools.js +91 -65
- package/src/mcp/tools/index.js +6 -3
- package/src/mcp/tools/schemaTools.js +30 -0
- package/src/mcp/tools/thinnedIndicesTools.js +86 -78
- package/src/services/TaskManager.js +114 -103
- package/src/utils/doseMapParser.js +168 -109
- package/tools/gen_paths.py +549 -470
- package/tools/ray_trace_tri.py +10 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Yoshihiro Hirao
|
|
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.
|
package/README.md
CHANGED
|
@@ -4,14 +4,53 @@ YAML-based input file management tool for radiation-shielding calculation code P
|
|
|
4
4
|
|
|
5
5
|
## 📋 クイック情報
|
|
6
6
|
|
|
7
|
-
- **バージョン**: 1.
|
|
7
|
+
- **バージョン**: 1.9.8
|
|
8
|
+
- **ツール数**: 36メソッド
|
|
8
9
|
- **プロトコル**: MCP (Model Context Protocol) 1.0.0 完全準拠
|
|
9
10
|
- **メインサーバー**: `src/mcp_server_stdio_v4.js`
|
|
10
11
|
- **データ保存**: `~/.poker-mcp/`(`POKER_MCP_HOME`環境変数で変更可)
|
|
11
12
|
- **核種データ**: `${POKER_INSTALL_PATH}/LIB/ICRP-07.NDX` を直接参照
|
|
12
13
|
- **実行方式**: STDIO通信(MCPプロトコル標準)
|
|
13
14
|
|
|
14
|
-
## 🆕 バージョン1.
|
|
15
|
+
## 🆕 バージョン1.9.8の新機能
|
|
16
|
+
|
|
17
|
+
### 📜 `poker_getSchema` — ファイル書式を JSON Schema で取得
|
|
18
|
+
|
|
19
|
+
POKER が扱う4種類のファイルの書式を、機械可読な形で取得できます。
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
poker_getSchema() // 入力 YAML
|
|
23
|
+
poker_getSchema({ kind: "paths" }) // CAD から抽出した経路ファイル
|
|
24
|
+
poker_getSchema({ kind: "summary" })
|
|
25
|
+
poker_getSchema({ kind: "dose" })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
スキーマは静的なファイルではなく、`poker_cui --schema` をその場で呼んで
|
|
29
|
+
生成します。そのため実行環境の POKER の版と材料ライブラリに必ず一致し、
|
|
30
|
+
`lib_material.dat` に材料を追加していれば、その材料も材料名の一覧
|
|
31
|
+
(`$defs` の `x-poker-names`)に現れます。
|
|
32
|
+
|
|
33
|
+
規定するのは書式・型・値域までです。参照の解決(`zone.body_name` が
|
|
34
|
+
`body` に在るか)や名前の存在確認は従来どおり `poker_cui --validate`
|
|
35
|
+
(`poker_executeCalculation` が内部で実施)の担当で、両者で役割が分かれます。
|
|
36
|
+
|
|
37
|
+
`.dose` のスキーマには、YAML の頭部に続く数値表の読み方
|
|
38
|
+
(ブロックの区切り・列の索引 `[dose, ray, energy]`・行の索引
|
|
39
|
+
`i + j*number_i + k*number_i*number_j`)が `x-poker-data-layout` として
|
|
40
|
+
書かれています。
|
|
41
|
+
|
|
42
|
+
### 🐛 `poker_getDoseMap` が線量種別の増加に追随
|
|
43
|
+
|
|
44
|
+
`.dose` の列構成を、線量種別3種・線種4種と決め打ちしていました。線量種別は
|
|
45
|
+
ライブラリ設定で増えるため(`E(PA)` や空気カーマを足した場合)、その環境では
|
|
46
|
+
誤った列を読むか、集計ブロックを見つけられず失敗していました。
|
|
47
|
+
|
|
48
|
+
冒頭の `information` から `dose_type` / `ray_type` / 線源ごとの `energy_bin` を
|
|
49
|
+
数えて列構成を決めるようにしました。1D/2D/3D 検出器の全評価点について、
|
|
50
|
+
`.summary` の `statistics_total`(min/max)と一致することを
|
|
51
|
+
`npm run test:schema` で検証しています。
|
|
52
|
+
|
|
53
|
+
## 🆕 バージョン1.8.0〜1.8.3の新機能
|
|
15
54
|
|
|
16
55
|
### 📐 CAD連携が MCP だけで完結
|
|
17
56
|
従来、CAD からの経路抽出は MCP を経由せず、FreeCAD を手で起動する必要が
|
|
@@ -19,6 +58,7 @@ YAML-based input file management tool for radiation-shielding calculation code P
|
|
|
19
58
|
スクリプトを渡す。3 手順すべてが手作業でした。
|
|
20
59
|
|
|
21
60
|
```javascript
|
|
61
|
+
poker_generateInput({ fcstd: "C:/path/to/model.FCStd" }) // YAML を生成
|
|
22
62
|
poker_generatePaths({ fcstd: "C:/path/to/model.FCStd" })
|
|
23
63
|
poker_executeCalculation({ yaml_file: "poker.yaml", path_input: "poker.paths" })
|
|
24
64
|
```
|
|
@@ -43,6 +83,15 @@ FreeCAD の場所を、環境変数 → PATH → 既定のインストール先
|
|
|
43
83
|
解決されていました。MCP サーバの起動場所によっては別の場所を指します。
|
|
44
84
|
|
|
45
85
|
|
|
86
|
+
### 📦 CAD連携ツールを npm パッケージに同梱(v1.8.2)
|
|
87
|
+
`poker_generatePaths` が使う Python スクリプトが `files` に含まれておらず、
|
|
88
|
+
GitHub から clone した環境でしか動きませんでした。`npm install` / `npx` でも
|
|
89
|
+
使えるようにしています。
|
|
90
|
+
|
|
91
|
+
### 🐛 サマリーの衝突を修正(v1.8.3)
|
|
92
|
+
`generatePaths` が `-p` 付きで作ったサマリーを `executeCalculation` が上書きし、
|
|
93
|
+
次に `generatePaths` を呼ぶと失敗していました。専用ファイルに分離しています。
|
|
94
|
+
|
|
46
95
|
## 🆕 バージョン1.7.0〜1.7.2の新機能
|
|
47
96
|
|
|
48
97
|
### 📉 サマリー出力量の制御(ThinnedIndices 操作系 3メソッド)
|
|
@@ -50,6 +99,9 @@ FreeCAD の場所を、環境変数 → PATH → 既定のインストール先
|
|
|
50
99
|
線源分割点と検出器評価点が必要ですが、既定では間引かれるため手で書き足す
|
|
51
100
|
必要がありました。
|
|
52
101
|
|
|
102
|
+
**`poker_generatePaths` が自動で合わせる**ので、通常は意識する必要はありません。
|
|
103
|
+
手で設定したい場合はこうします。
|
|
104
|
+
|
|
53
105
|
```javascript
|
|
54
106
|
poker_updateThinnedIndices({ fit_for_paths: true })
|
|
55
107
|
// → 入力の分割定義と検出器グリッドから必要数を計算して設定
|
|
@@ -104,16 +156,18 @@ poker_getThinnedIndices()
|
|
|
104
156
|
|
|
105
157
|
### 📡 CAD連携レイトレース
|
|
106
158
|
FreeCAD のソリッドモデルを、STEP のような中間フォーマットを介さず、CSG で
|
|
107
|
-
表現し直す作業も無しに遮蔽体系として扱えます。**MCP の
|
|
159
|
+
表現し直す作業も無しに遮蔽体系として扱えます。**MCP の 3 呼び出しで完結**します。
|
|
108
160
|
|
|
109
161
|
```javascript
|
|
162
|
+
poker_generateInput({ fcstd: "C:/path/to/model.FCStd" })
|
|
110
163
|
poker_generatePaths({ fcstd: "C:/path/to/model.FCStd" })
|
|
111
164
|
poker_executeCalculation({ yaml_file: "poker.yaml", path_input: "poker.paths" })
|
|
112
165
|
```
|
|
113
166
|
|
|
167
|
+
CAD が正本です。**YAML を手で書く必要はありません。** ソリッドに材質・線源・
|
|
168
|
+
検出器をカスタムプロパティで設定しておけば、そこから入力が生成されます。
|
|
114
169
|
線源点→検出器の直線を FreeCAD 側で追跡し、通過した材質と厚さを `.paths` に
|
|
115
|
-
記録して POKER
|
|
116
|
-
等価材料の解決はすべて自動です。
|
|
170
|
+
記録して POKER に渡します。
|
|
117
171
|
|
|
118
172
|
CSG 経由と一致することを平板・円筒・球の 5 体系で確認済みです。フィレットや
|
|
119
173
|
自由曲面のように CSG で表現できない形状も扱えます。**複数線源・グリッド検出器・
|
|
@@ -156,6 +210,7 @@ numpy はレイトレーサが使いますが、FreeCAD に同梱されている
|
|
|
156
210
|
他の CAD で作ったモデルを STEP で読み込んだ場合、材質情報は失われるので
|
|
157
211
|
FreeCAD 上で設定してください。一度設定すれば `.FCStd` に保存されます。
|
|
158
212
|
|
|
213
|
+
まずは [CAD_QUICKSTART.md](./docs/manuals/CAD_QUICKSTART.md)(最短手順と実例)を、
|
|
159
214
|
詳細は [CAD_RAYTRACE.md](./docs/manuals/CAD_RAYTRACE.md)、フォーマットは
|
|
160
215
|
[PATHS_FORMAT.md](./docs/manuals/PATHS_FORMAT.md)。
|
|
161
216
|
|
|
@@ -401,14 +456,14 @@ Claude Desktopで以下のようにテストできます:
|
|
|
401
456
|
## 🏆 主要機能
|
|
402
457
|
|
|
403
458
|
### ✅ **MCP完全対応**
|
|
404
|
-
- **
|
|
459
|
+
- **36メソッド完全実装**: 全ての放射線遮蔽計算入力管理機能
|
|
405
460
|
- **JSON-RPC 2.0準拠**: 標準プロトコル完全対応
|
|
406
461
|
- **STDIO通信**: MCPクライアントとの標準通信方式
|
|
407
462
|
- **自動バックアップ・ロールバック**: 企業品質のデータ保護
|
|
408
463
|
|
|
409
464
|
### ✅ **放射線遮蔽計算専用設計**
|
|
410
465
|
- **10種類の立体形状**: SPH, RCC, RPP, BOX, CMB, TOR, ELL, REC, TRC, WED
|
|
411
|
-
- **
|
|
466
|
+
- **23種類の材料**: 鉄・鉛・コンクリート(普通/重量3種)・SUS・ポリエチレン等(`lib_material.dat` から読み込み)
|
|
412
467
|
- **複数線源対応**: 点・体積線源の完全管理
|
|
413
468
|
- **検出器配置**: 0D/1D/2D/3D検出器の柔軟な配置
|
|
414
469
|
|
|
@@ -420,7 +475,7 @@ Claude Desktopで以下のようにテストできます:
|
|
|
420
475
|
|
|
421
476
|
## 🎯 API構成
|
|
422
477
|
|
|
423
|
-
### 🔧 **
|
|
478
|
+
### 🔧 **36メソッド完全実装**
|
|
424
479
|
|
|
425
480
|
| **カテゴリ** | **メソッド数** | **機能** | **主要操作** |
|
|
426
481
|
|-------------|---------------|----------|-------------|
|
|
@@ -431,9 +486,12 @@ Claude Desktopで以下のようにテストできます:
|
|
|
431
486
|
| **📡 Source** | 3個 | 線源管理 | propose・update・delete |
|
|
432
487
|
| **🎯 Detector** | 3個 | 検出器管理 | propose・update・delete |
|
|
433
488
|
| **📏 Unit** | 5個 | 単位設定管理 | propose・get・update・validateIntegrity・analyzeConversion |
|
|
434
|
-
|
|
|
489
|
+
| **📉 ThinnedIndices** | 3個 | サマリー出力量の制御 | propose・get・update |
|
|
490
|
+
| **📐 CAD** | 2個 | CAD から入力と経路を生成 | generateInput・generatePaths |
|
|
491
|
+
| **⚙️ System** | 6個 | システム制御 | applyChanges・executeCalculation・resetYaml・confirmDaughterNuclides・openGui・getDoseMap |
|
|
492
|
+
| **📜 Schema** | 1個 | ファイル書式の取得 | getSchema |
|
|
435
493
|
|
|
436
|
-
### 📋 **全
|
|
494
|
+
### 📋 **全36メソッド一覧**
|
|
437
495
|
```
|
|
438
496
|
Body系 (3): poker_proposeBody, poker_updateBody, poker_deleteBody
|
|
439
497
|
Zone系 (3): poker_proposeZone, poker_updateZone, poker_deleteZone
|
|
@@ -444,8 +502,12 @@ Source系 (3): poker_proposeSource, poker_updateSource, poker_deleteSourc
|
|
|
444
502
|
Detector系 (3): poker_proposeDetector, poker_updateDetector, poker_deleteDetector
|
|
445
503
|
Unit系 (5): poker_proposeUnit, poker_getUnit, poker_updateUnit,
|
|
446
504
|
poker_validateUnitIntegrity, poker_analyzeUnitConversion
|
|
505
|
+
ThinnedIndices系 (3): poker_proposeThinnedIndices, poker_getThinnedIndices,
|
|
506
|
+
poker_updateThinnedIndices
|
|
507
|
+
CAD系 (2): poker_generateInput, poker_generatePaths
|
|
447
508
|
System系 (6): poker_applyChanges, poker_executeCalculation, poker_resetYaml,
|
|
448
|
-
poker_confirmDaughterNuclides, poker_openGui,
|
|
509
|
+
poker_confirmDaughterNuclides, poker_openGui, poker_getDoseMap
|
|
510
|
+
Schema系 (1): poker_getSchema
|
|
449
511
|
```
|
|
450
512
|
|
|
451
513
|
## 📁 プロジェクト構造
|
|
@@ -462,9 +524,10 @@ poker_mcp/
|
|
|
462
524
|
│ │ ├── logger.js # ログ出力(絶対パス)
|
|
463
525
|
│ │ └── ...
|
|
464
526
|
│ └── 📁 config/ # 設定管理
|
|
527
|
+
├── 📁 tools/ # 🔧 CAD連携(レイトレーサ・経路生成)
|
|
465
528
|
├── 📁 docs/ # 📚 完全ドキュメント
|
|
466
529
|
├── .mcp.json # MCPクライアント接続設定
|
|
467
|
-
├── package.json #
|
|
530
|
+
├── package.json # パッケージ定義
|
|
468
531
|
└── README.md # このファイル
|
|
469
532
|
|
|
470
533
|
# 実行時に自動作成されるディレクトリ(POKER_MCP_HOME配下)
|
|
@@ -542,7 +605,7 @@ POKER_MCP_HOME/
|
|
|
542
605
|
|
|
543
606
|
### **✅ 放射線遮蔽計算特化**
|
|
544
607
|
- **物理的妥当性**: 全パラメータの物理検証
|
|
545
|
-
- **材料データベース**: 標準遮蔽材料
|
|
608
|
+
- **材料データベース**: 標準遮蔽材料23種(lib_material.dat 準拠)
|
|
546
609
|
- **単位系管理**: 4キー完全性保証(長さ・角度・密度・放射能)
|
|
547
610
|
- **計算品質保証**: 自動整合性チェック
|
|
548
611
|
|
|
@@ -562,6 +625,7 @@ POKER_MCP_HOME/
|
|
|
562
625
|
|
|
563
626
|
- **POKER**: 放射線遮蔽計算メインコード
|
|
564
627
|
- **poker_cui**: コマンドライン実行インターフェース
|
|
628
|
+
- **FreeCAD**: CAD連携レイトレース(`poker_generatePaths` を使う場合のみ)
|
|
565
629
|
|
|
566
630
|
## 🔗 システム要件
|
|
567
631
|
|
|
@@ -569,6 +633,8 @@ POKER_MCP_HOME/
|
|
|
569
633
|
- **OS**: Windows, macOS, Linux
|
|
570
634
|
- **MCP Client**: Claude Desktop (推奨)、その他MCPクライアント
|
|
571
635
|
- **メモリ**: 512MB以上推奨(大規模検出器使用時は1GB以上)
|
|
636
|
+
- **POKER 本体**: v2.1.5 以降(`POKER_INSTALL_PATH` で場所を指定)
|
|
637
|
+
- **FreeCAD**: 1.0 以降(CAD連携を使う場合。`FREECAD_PATH`、未設定なら自動探索)
|
|
572
638
|
|
|
573
639
|
## 🎯 実際の使用ワークフロー
|
|
574
640
|
|
|
@@ -593,6 +659,41 @@ POKER_MCP_HOME/
|
|
|
593
659
|
|
|
594
660
|
## 📝 更新履歴
|
|
595
661
|
|
|
662
|
+
### v1.9.8 (2026-10)
|
|
663
|
+
- ✨ `poker_getSchema`(入力YAML・`.paths`・`.summary`・`.dose` の書式を JSON Schema で取得)
|
|
664
|
+
- 🐛 `poker_getDoseMap` が `.dose` の列構成を `information` から読むようになり、線量種別の増加に追随
|
|
665
|
+
- ✅ `npm run test:schema` を追加
|
|
666
|
+
|
|
667
|
+
### v1.9.0〜v1.9.7 (2026-09)
|
|
668
|
+
- ✨ `poker_generateInput`(CAD から入力 YAML を生成)
|
|
669
|
+
- ✨ `generatePaths` が `thinnedindices` を `.paths` 生成に必要な値へ自動調整
|
|
670
|
+
- ✨ `.paths` の座標・個数照合を強化
|
|
671
|
+
|
|
672
|
+
### v1.8.0〜v1.8.3 (2026-09)
|
|
673
|
+
- ✨ CAD連携が MCP だけで完結(`poker_generatePaths` と `path_input`)
|
|
674
|
+
- ✨ `FREECAD_PATH` 環境変数(未設定なら PATH と既定の場所を自動探索)
|
|
675
|
+
- 🐛 相対パスの解決をプロセスのカレントから `TASKS_DIR` 基準に修正
|
|
676
|
+
- 🐛 `generatePaths` と `executeCalculation` のサマリー衝突を修正
|
|
677
|
+
|
|
678
|
+
### v1.7.0〜v1.7.2 (2026-09)
|
|
679
|
+
- ✨ ThinnedIndices 操作系 3メソッド(サマリー出力量の制御)
|
|
680
|
+
- ✨ `get` 系が保留中の変更を `pending` として併記
|
|
681
|
+
- ✨ `.paths` の座標照合(検出器を動かして再生成し忘れた場合を検出)
|
|
682
|
+
|
|
683
|
+
### v1.6.0 (2026-09)
|
|
684
|
+
- ✨ CAD連携レイトレースのツール群と `.paths` フォーマット
|
|
685
|
+
- ✨ 材料システムを `lib_material.dat` 準拠に
|
|
686
|
+
|
|
687
|
+
### v1.5.0 (2026-08)
|
|
688
|
+
- ✨ `poker_openGui` で POKER を閉じずに表示を切り替え(POKER 2.1.1 以降)
|
|
689
|
+
- 🐛 起動失敗時の偽の成功報告を修正
|
|
690
|
+
|
|
691
|
+
### v1.4.0 (2026-07)
|
|
692
|
+
- ✨ 子孫核種の自動管理(ICRP-07 準拠)
|
|
693
|
+
|
|
694
|
+
### v1.3.0 (2026-06)
|
|
695
|
+
- ✨ `poker_getDoseMap`(グリッド検出器の線量マップ取得)
|
|
696
|
+
|
|
596
697
|
### v1.2.8 (2026-05-16)
|
|
597
698
|
- ✨ `poker_openGui` メソッドを新設(POKER.exe でGUI確認)
|
|
598
699
|
- ✨ 起動前に `applyChanges` を自動実行
|
|
@@ -628,6 +729,16 @@ POKER_MCP_HOME/
|
|
|
628
729
|
- 初期リリース
|
|
629
730
|
- YAML管理基本機能
|
|
630
731
|
|
|
732
|
+
## 📜 ライセンス
|
|
733
|
+
|
|
734
|
+
**MIT License** — 著作権 (c) 2025-2026 Yoshihiro Hirao。全文は
|
|
735
|
+
[LICENSE](LICENSE) を参照してください。
|
|
736
|
+
|
|
737
|
+
このライセンスが及ぶのは本リポジトリの内容(poker-mcp)だけです。POKER itself is
|
|
738
|
+
obtained separately and licensed separately. poker-mcp は `poker_cui` を外部
|
|
739
|
+
プロセスとして起動するだけなので、POKER 本体の許諾には影響しません。FreeCAD も
|
|
740
|
+
別途入手が必要で、それぞれのライセンスに従います。
|
|
741
|
+
|
|
631
742
|
## 📞 サポート・詳細情報
|
|
632
743
|
|
|
633
744
|
- **📖 詳細README**: [docs/README.md](docs/README.md)
|
|
@@ -638,6 +749,6 @@ POKER_MCP_HOME/
|
|
|
638
749
|
|
|
639
750
|
---
|
|
640
751
|
|
|
641
|
-
**🎯 Poker MCP Server v1.
|
|
752
|
+
**🎯 Poker MCP Server v1.9.8**
|
|
642
753
|
**プロトコル**: MCP 1.0.0 完全準拠
|
|
643
|
-
**作者**: Yoshihiro Hirao | **ライセンス**:
|
|
754
|
+
**作者**: Yoshihiro Hirao | **ライセンス**: MIT
|
package/package.json
CHANGED
|
@@ -1,62 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "poker-mcp",
|
|
3
|
-
"version": "1.8
|
|
4
|
-
"description": "YAML-based input file management tool for radiation shielding calculation code POKER with MCP support - Enhanced with collision detection, daughter nuclide auto-completion, and environment variable support",
|
|
5
|
-
"main": "src/mcp_server_stdio_v4.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"poker-mcp": "src/mcp_server_stdio_v4.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"start": "node src/mcp_server_stdio_v4.js",
|
|
12
|
-
"dev": "nodemon src/mcp_server_stdio_v4.js",
|
|
13
|
-
"prepare": "echo 'NPX ready: npx poker-mcp'",
|
|
14
|
-
"check:manifest": "node scripts/check-manifest-sync.mjs",
|
|
15
|
-
"test:pending": "node tests/pending-view.test.mjs",
|
|
16
|
-
"test:cad": "node tests/cad-e2e.test.mjs"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
".
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "poker-mcp",
|
|
3
|
+
"version": "1.9.8",
|
|
4
|
+
"description": "YAML-based input file management tool for radiation shielding calculation code POKER with MCP support - Enhanced with collision detection, daughter nuclide auto-completion, and environment variable support",
|
|
5
|
+
"main": "src/mcp_server_stdio_v4.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"poker-mcp": "src/mcp_server_stdio_v4.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/mcp_server_stdio_v4.js",
|
|
12
|
+
"dev": "nodemon src/mcp_server_stdio_v4.js",
|
|
13
|
+
"prepare": "echo 'NPX ready: npx poker-mcp'",
|
|
14
|
+
"check:manifest": "node scripts/check-manifest-sync.mjs",
|
|
15
|
+
"test:pending": "node tests/pending-view.test.mjs",
|
|
16
|
+
"test:cad": "node tests/cad-e2e.test.mjs",
|
|
17
|
+
"test:cad-full": "node tests/cad-full.test.mjs",
|
|
18
|
+
"test:cad-validate": "node tests/cad-validate.test.mjs",
|
|
19
|
+
"test:cad-pipe": "node tests/cad-pipe.test.mjs",
|
|
20
|
+
"test:cad-thinned": "node tests/cad-thinned-fit.test.mjs",
|
|
21
|
+
"test:schema": "node tests/schema-dose.test.mjs"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"yaml",
|
|
27
|
+
"radiation",
|
|
28
|
+
"shielding",
|
|
29
|
+
"nuclear",
|
|
30
|
+
"radioisotope",
|
|
31
|
+
"medical",
|
|
32
|
+
"accelerator",
|
|
33
|
+
"POKER",
|
|
34
|
+
"physics",
|
|
35
|
+
"validation",
|
|
36
|
+
"npx",
|
|
37
|
+
"point-kernel",
|
|
38
|
+
"collision-detection",
|
|
39
|
+
"daughter-nuclide"
|
|
40
|
+
],
|
|
41
|
+
"author": "Yoshihiro Hirao <tora@pointkernel.com>",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/Hirao-Y/poker_mcp.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/Hirao-Y/poker_mcp#readme",
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
53
|
+
"js-yaml": "^4.1.0",
|
|
54
|
+
"winston": "^3.17.0",
|
|
55
|
+
"zod": "^3.24.2"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"src/",
|
|
59
|
+
"tools/gen_paths.py",
|
|
60
|
+
"tools/ray_trace_tri.py",
|
|
61
|
+
"tools/poker_lib.py",
|
|
62
|
+
"tools/audit_mfp.py",
|
|
63
|
+
"tools/compare_poker_trace.py",
|
|
64
|
+
"README.md",
|
|
65
|
+
"LICENSE",
|
|
66
|
+
".mcp.json"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -13,6 +13,7 @@ import path from 'path';
|
|
|
13
13
|
import { FREECAD_CMD, CAD_TOOLS_DIR, POKER_INSTALL_DIR, TASKS_DIR, YAML_FILE }
|
|
14
14
|
from '../../utils/paths.js';
|
|
15
15
|
import { logger } from '../../utils/logger.js';
|
|
16
|
+
import { reconcileInventory } from '../../utils/DaughterReconciler.js';
|
|
16
17
|
import { MaterialCatalog } from '../../utils/MaterialCatalog.js';
|
|
17
18
|
|
|
18
19
|
function run(cmd, args, opts = {}) {
|
|
@@ -55,6 +56,30 @@ export function createCadHandlers(taskManager) {
|
|
|
55
56
|
return fail('入力 YAML がありません: ' + yamlFile,
|
|
56
57
|
'applyChanges で入力を確定してから実行してください。');
|
|
57
58
|
|
|
59
|
+
// --- thinnedindices を全点が出る値に合わせる ---
|
|
60
|
+
// .paths の生成には、線源の全分割点と検出器の全評価点が要る。
|
|
61
|
+
// thinnedindices が小さいままだと、サマリーに一部しか出ず、経路が
|
|
62
|
+
// 足りない .paths ができてしまう。利用者が updateThinnedIndices を
|
|
63
|
+
// 呼び忘れても起きないよう、ここで自動的に合わせる。
|
|
64
|
+
//
|
|
65
|
+
// .paths を作らない通常の計算では全点が出る必要はないので、この調整は
|
|
66
|
+
// generatePaths のときだけ行う。
|
|
67
|
+
let thinnedNote = null;
|
|
68
|
+
try {
|
|
69
|
+
const before = JSON.stringify(taskManager.data?.thinnedindices ?? {});
|
|
70
|
+
await taskManager.updateThinnedIndices({ fit_for_paths: true });
|
|
71
|
+
await taskManager.applyChanges();
|
|
72
|
+
const after = JSON.stringify(taskManager.data?.thinnedindices ?? {});
|
|
73
|
+
if (before !== after) {
|
|
74
|
+
thinnedNote = 'thinnedindices を入力の分割数・評価点数に合わせました';
|
|
75
|
+
logger.info(thinnedNote, { thinnedindices: taskManager.data?.thinnedindices });
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// 合わせられなくても処理は続ける。点が足りなければ gen_paths.py が
|
|
79
|
+
// エラーにするので、黙って誤った .paths ができることはない。
|
|
80
|
+
logger.warn('thinnedindices の自動調整に失敗しました', { error: e.message });
|
|
81
|
+
}
|
|
82
|
+
|
|
58
83
|
// --- 1. poker_cui -p で分割点と評価点を出す ---
|
|
59
84
|
// .paths には全点が必要。間引かれていると gen_paths.py がエラーにする。
|
|
60
85
|
const exe = path.join(POKER_INSTALL_DIR, 'poker_cui.EXE');
|
|
@@ -185,10 +210,186 @@ export function createCadHandlers(taskManager) {
|
|
|
185
210
|
freecad: FREECAD_CMD,
|
|
186
211
|
poker_dir: spec.poker_dir
|
|
187
212
|
},
|
|
213
|
+
// thinnedindices を調整した場合だけ知らせる(毎回出すと煩わしい)
|
|
214
|
+
...(thinnedNote ? { note: thinnedNote } : {}),
|
|
188
215
|
next: 'poker_cui "' + yamlFile + '" --path-input "' + out + '" -t で計算できます'
|
|
189
216
|
}, null, 2)
|
|
190
217
|
}]
|
|
191
218
|
};
|
|
192
|
-
}
|
|
219
|
+
},
|
|
220
|
+
async generateInput(args) {
|
|
221
|
+
const fail = (msg, hint) => ({
|
|
222
|
+
content: [{
|
|
223
|
+
type: 'text',
|
|
224
|
+
text: JSON.stringify({ success: false, error: msg, hint }, null, 2)
|
|
225
|
+
}],
|
|
226
|
+
isError: true
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!FREECAD_CMD) {
|
|
230
|
+
return fail(
|
|
231
|
+
'FreeCAD が見つかりません',
|
|
232
|
+
'環境変数 FREECAD_PATH に freecadcmd の場所を設定してください。');
|
|
233
|
+
}
|
|
234
|
+
if (!args || !args.fcstd) return fail('fcstd を指定してください');
|
|
235
|
+
if (!fssync.existsSync(args.fcstd))
|
|
236
|
+
return fail('CAD ファイルが見つかりません: ' + args.fcstd);
|
|
237
|
+
|
|
238
|
+
const out = args.output || YAML_FILE;
|
|
239
|
+
// 既存の YAML はバックアップしてから上書きする。
|
|
240
|
+
// CAD が正本なので YAML は生成物であり、毎回 overwrite を
|
|
241
|
+
// 求めるのは煩わしいだけ。退避先は applyChanges と同じ
|
|
242
|
+
// backups/ に混ぜる。CAD 連携と手動編集が同じセッションで
|
|
243
|
+
// 混ざるので、区別しても探しにくくなるだけ。
|
|
244
|
+
let backedUp = false;
|
|
245
|
+
if (fssync.existsSync(out) && out === YAML_FILE) {
|
|
246
|
+
try {
|
|
247
|
+
await taskManager.dataManager.createBackup();
|
|
248
|
+
backedUp = true;
|
|
249
|
+
} catch (e) {
|
|
250
|
+
logger.warn('バックアップに失敗(生成は続行)', { error: e.message });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// --- 1. CAD から線源の核種を読み、子孫核種を補完する ---
|
|
255
|
+
// まず核種だけを抜き出すために FreeCAD を一度呼ぶ。
|
|
256
|
+
const probe = path.join(TASKS_DIR, '.probe_nuclides.py');
|
|
257
|
+
const probeOut = path.join(TASKS_DIR, '.probe_nuclides.json');
|
|
258
|
+
await fs.writeFile(probe,
|
|
259
|
+
'import FreeCAD as App, json, os\n' +
|
|
260
|
+
'd = App.openDocument(r"' + args.fcstd + '")\n' +
|
|
261
|
+
'out = {}\n' +
|
|
262
|
+
'for o in d.Objects:\n' +
|
|
263
|
+
' r = (getattr(o, "PokerRole", None) or "shield").strip().lower()\n' +
|
|
264
|
+
' if r != "source":\n' +
|
|
265
|
+
' continue\n' +
|
|
266
|
+
' out[o.Name] = getattr(o, "PokerNuclides", None) or ""\n' +
|
|
267
|
+
'json.dump(out, open(r"' + probeOut + '", "w"), ensure_ascii=False)\n',
|
|
268
|
+
'utf8');
|
|
269
|
+
|
|
270
|
+
const r0 = await run(FREECAD_CMD, [probe], { cwd: TASKS_DIR });
|
|
271
|
+
let nuclides = null;
|
|
272
|
+
let daughterNote = null;
|
|
273
|
+
if (fssync.existsSync(probeOut)) {
|
|
274
|
+
const raw = JSON.parse(await fs.readFile(probeOut, 'utf8'));
|
|
275
|
+
nuclides = {};
|
|
276
|
+
const added = [];
|
|
277
|
+
for (const [name, text] of Object.entries(raw)) {
|
|
278
|
+
const inv = [];
|
|
279
|
+
for (const part of String(text).split(/[,;]/)) {
|
|
280
|
+
const m = part.trim().match(/^([A-Za-z]+[-_]?\d+m?)\s*[:=]\s*([\d.eE+-]+)$/);
|
|
281
|
+
if (m) inv.push({
|
|
282
|
+
nuclide: m[1].replace(/[-_]/g, ''),
|
|
283
|
+
radioactivity: parseFloat(m[2])
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (!inv.length) continue;
|
|
287
|
+
try {
|
|
288
|
+
const rec = await reconcileInventory(inv,
|
|
289
|
+
{ nuclideManager: taskManager.dataManager.nuclideManager });
|
|
290
|
+
nuclides[name] = rec.inventory;
|
|
291
|
+
for (const a of (rec.added || [])) added.push(name + ': ' + a.nuclide);
|
|
292
|
+
} catch (e) {
|
|
293
|
+
nuclides[name] = inv;
|
|
294
|
+
logger.warn('子孫核種の補完に失敗', { source: name, error: e.message });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (added.length) daughterNote = '子孫核種を補完しました: ' + added.join(', ');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// --- 2. gen_input.py で YAML を組み立てる ---
|
|
301
|
+
const spec = {
|
|
302
|
+
fcstd: args.fcstd.replace(/\\/g, '/'),
|
|
303
|
+
out: out.replace(/\\/g, '/'),
|
|
304
|
+
poker_dir: POKER_INSTALL_DIR.replace(/\\/g, '/'),
|
|
305
|
+
};
|
|
306
|
+
if (nuclides && Object.keys(nuclides).length) spec.nuclides = nuclides;
|
|
307
|
+
|
|
308
|
+
const specFile = path.join(TASKS_DIR, '.generate_input_spec.json');
|
|
309
|
+
await fs.writeFile(specFile, JSON.stringify(spec, null, 2), 'utf8');
|
|
310
|
+
|
|
311
|
+
const script = path.join(TASKS_DIR, '.generate_input.py');
|
|
312
|
+
await fs.writeFile(script,
|
|
313
|
+
'import sys, traceback\n' +
|
|
314
|
+
'sys.path.insert(0, r"' + CAD_TOOLS_DIR + '")\n' +
|
|
315
|
+
'try:\n' +
|
|
316
|
+
' import gen_input\n' +
|
|
317
|
+
' gen_input.main(r"' + specFile + '")\n' +
|
|
318
|
+
' print("GEN_INPUT_OK")\n' +
|
|
319
|
+
'except BaseException:\n' +
|
|
320
|
+
' print("GEN_INPUT_ERROR")\n' +
|
|
321
|
+
' traceback.print_exc()\n', 'utf8');
|
|
322
|
+
|
|
323
|
+
const r1 = await run(FREECAD_CMD, [script], { cwd: TASKS_DIR });
|
|
324
|
+
const log = (r1.out || '') + (r1.err || '');
|
|
325
|
+
if (!log.includes('GEN_INPUT_OK') || !fssync.existsSync(out)) {
|
|
326
|
+
const m = log.match(/(?:SystemExit|Error|Exception)[^\n]*(?:\n[^\n]*){0,3}/);
|
|
327
|
+
return fail('入力の生成に失敗しました', m ? m[0].trim() : log.slice(-500));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 生成した YAML をサーバのデータに反映する。
|
|
331
|
+
// これが無いと、メモリ上には起動時に読んだ古い YAML が残る。続けて
|
|
332
|
+
// generatePaths を呼んだとき、ゾーンから材質を読めず等価材料を解決
|
|
333
|
+
// できない(SUS_A -> Iron の読み替えが効かず "no buildup data" で失敗)。
|
|
334
|
+
if (out === YAML_FILE) {
|
|
335
|
+
try {
|
|
336
|
+
await taskManager.dataManager.loadData();
|
|
337
|
+
} catch (e) {
|
|
338
|
+
logger.warn('生成後のデータ再読み込みに失敗', { error: e.message });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// --- 3. 生成した入力を POKER に検証させる ---
|
|
343
|
+
// poker_cui -c は線量計算をせず、入力の妥当性確認と経路追跡だけ行う。
|
|
344
|
+
// これを通しておけば、線源が遮蔽体の外にある、材質名がライブラリに
|
|
345
|
+
// 無い、立体が重なっている、といった誤りを生成の時点で捕まえられる。
|
|
346
|
+
// 利用者が計算まで進んでから気づくより早い。
|
|
347
|
+
let validation = null;
|
|
348
|
+
{
|
|
349
|
+
const exe2 = path.join(POKER_INSTALL_DIR, 'poker_cui.EXE');
|
|
350
|
+
if (fssync.existsSync(exe2)) {
|
|
351
|
+
const rv = await run(exe2, [out, '-c'], { cwd: TASKS_DIR });
|
|
352
|
+
if (rv.code === 0) {
|
|
353
|
+
validation = { ok: true, message: 'poker_cui -c で検証を通過しました' };
|
|
354
|
+
} else {
|
|
355
|
+
const text = (rv.out || "") + (rv.err || "");
|
|
356
|
+
const m = text.match(/(?:ERROR|INPUT ERROR)[\s\S]{0,300}/);
|
|
357
|
+
validation = {
|
|
358
|
+
ok: false,
|
|
359
|
+
message: 'poker_cui -c で問題が見つかりました(終了コード ' + rv.code + ')',
|
|
360
|
+
detail: m ? m[0].trim() : text.slice(-300),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// --- 3. 生成結果を要約する ---
|
|
367
|
+
const text = await fs.readFile(out, 'utf8');
|
|
368
|
+
const count = (re) => (text.match(re) || []).length;
|
|
369
|
+
// YAML の節を切り出して、その中の " - name:" を数える。
|
|
370
|
+
// 節をまたいで数えると線源と検出器が混ざる。
|
|
371
|
+
const sectionCount = (t, sec) => {
|
|
372
|
+
const m = t.split(new RegExp('^' + sec + ':', 'm'))[1];
|
|
373
|
+
if (!m) return 0;
|
|
374
|
+
const body = m.split(/^[a-z_]+:/m)[0];
|
|
375
|
+
return (body.match(/^ - name: /gm) || []).length;
|
|
376
|
+
};
|
|
377
|
+
const result = {
|
|
378
|
+
success: true,
|
|
379
|
+
message: '入力を生成しました',
|
|
380
|
+
output: out,
|
|
381
|
+
summary: {
|
|
382
|
+
材質数: count(/^ - body_name: REF_/gm),
|
|
383
|
+
線源数: sectionCount(text, 'source'),
|
|
384
|
+
検出器数: sectionCount(text, 'detector'),
|
|
385
|
+
},
|
|
386
|
+
next: 'poker_generatePaths で経路を抽出し、executeCalculation の path_input に渡してください',
|
|
387
|
+
};
|
|
388
|
+
if (daughterNote) result.note = daughterNote;
|
|
389
|
+
if (validation) result.validation = validation;
|
|
390
|
+
|
|
391
|
+
logger.info('CAD から入力を生成しました', { out, added: daughterNote });
|
|
392
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
393
|
+
},
|
|
193
394
|
};
|
|
194
395
|
}
|