poker-mcp 1.0.0

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.
Files changed (50) hide show
  1. package/README.md +241 -0
  2. package/config/mcp-manifest.json +1713 -0
  3. package/config/package-lock.json +1335 -0
  4. package/config/poker_mcp.code-workspace +8 -0
  5. package/package.json +51 -0
  6. package/src/config/ConfigManager.js +120 -0
  7. package/src/config/default.json +62 -0
  8. package/src/mcp/handlers/bodyHandlers.js +63 -0
  9. package/src/mcp/handlers/buildupFactorHandlers.js +43 -0
  10. package/src/mcp/handlers/calculationHandlers.js +265 -0
  11. package/src/mcp/handlers/detectorHandlers.js +201 -0
  12. package/src/mcp/handlers/index.js +43 -0
  13. package/src/mcp/handlers/sourceHandlers.js +25 -0
  14. package/src/mcp/handlers/transformHandlers.js +26 -0
  15. package/src/mcp/handlers/unitHandlers.js +305 -0
  16. package/src/mcp/handlers/zoneHandlers.js +83 -0
  17. package/src/mcp/middleware/errorHandler.js +49 -0
  18. package/src/mcp/middleware/requestValidator.js +129 -0
  19. package/src/mcp/server.js +76 -0
  20. package/src/mcp/tools/bodyTools.js +185 -0
  21. package/src/mcp/tools/buildupFactorTools.js +82 -0
  22. package/src/mcp/tools/calculationTools.js +58 -0
  23. package/src/mcp/tools/commonTools.js +22 -0
  24. package/src/mcp/tools/detectorTools.js +131 -0
  25. package/src/mcp/tools/index.js +34 -0
  26. package/src/mcp/tools/sourceTools.js +726 -0
  27. package/src/mcp/tools/sourceTools_fixed_part2.js +41 -0
  28. package/src/mcp/tools/transformTools.js +73 -0
  29. package/src/mcp/tools/unitTools.js +137 -0
  30. package/src/mcp/tools/zoneTools.js +65 -0
  31. package/src/mcp_server_stdio_v4.js +45 -0
  32. package/src/routes/restApi.js +184 -0
  33. package/src/services/CalculationService.js +525 -0
  34. package/src/services/DataManager.js +738 -0
  35. package/src/services/TaskManager.js +1622 -0
  36. package/src/utils/MaterialAlternatives.js +189 -0
  37. package/src/utils/errorHandler.js +68 -0
  38. package/src/utils/errors.js +37 -0
  39. package/src/utils/logger.js +24 -0
  40. package/src/utils/logger_no_color.js +28 -0
  41. package/src/utils/mcpErrors.js +225 -0
  42. package/src/validators/CMBValidator.js +191 -0
  43. package/src/validators/DetectorValidator.js +512 -0
  44. package/src/validators/ManifestValidator.js +249 -0
  45. package/src/validators/NuclideValidator.js +171 -0
  46. package/src/validators/PhysicsValidator.js +401 -0
  47. package/src/validators/PhysicsValidatorSimple.js +87 -0
  48. package/src/validators/SourceValidator.js +515 -0
  49. package/src/validators/TransformValidator.js +472 -0
  50. package/src/validators/UnitValidator.js +562 -0
@@ -0,0 +1,401 @@
1
+ // validators/PhysicsValidator.js
2
+ import { z } from 'zod';
3
+ import { ValidationError, PhysicsError } from '../utils/errors.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { CMBValidator } from './CMBValidator.js';
6
+
7
+ export class PhysicsValidator {
8
+ static getMaterialProperties() {
9
+ // fallbackデータを返す
10
+ return {
11
+ 'Concrete': { densityRange: { min: 1.8, max: 2.5 } },
12
+ 'Iron': { densityRange: { min: 7.6, max: 7.9 } },
13
+ 'Lead': { densityRange: { min: 11.0, max: 11.4 } },
14
+ 'Water': { densityRange: { min: 0.95, max: 1.05 } },
15
+ 'Air': { densityRange: { min: 0.001, max: 0.002 } },
16
+ 'VOID': { densityRange: { min: 0, max: 0 } }
17
+ };
18
+ }
19
+
20
+ static GEOMETRY_TYPES = {
21
+ 'BOX': {
22
+ requiredParams: ['vertex', 'edge_1', 'edge_2', 'edge_3'],
23
+ description: '直方体(頂点+3つの辺)'
24
+ },
25
+ 'RPP': {
26
+ requiredParams: ['min', 'max'],
27
+ description: '軸平行直方体(最小・最大座標)'
28
+ },
29
+ 'SPH': {
30
+ requiredParams: ['center', 'radius'],
31
+ description: '球体'
32
+ },
33
+ 'RCC': {
34
+ requiredParams: ['bottom_center', 'height_vector', 'radius'],
35
+ description: '円柱'
36
+ },
37
+ 'CMB': {
38
+ requiredParams: ['expression'],
39
+ description: '組み合わせ形状'
40
+ },
41
+ 'TOR': {
42
+ requiredParams: ['center', 'normal', 'major_radius', 'minor_radius_horizontal', 'minor_radius_vertical'],
43
+ description: 'トーラス(ドーナツ形状)'
44
+ },
45
+ 'ELL': {
46
+ requiredParams: ['center', 'radius_vector_1', 'radius_vector_2', 'radius_vector_3'],
47
+ description: '楕円体(3つの半径ベクトル)'
48
+ },
49
+ 'REC': {
50
+ requiredParams: ['bottom_center', 'height_vector', 'radius_vector_1', 'radius_vector_2'],
51
+ description: '楕円円柱(2つの半径ベクトル)'
52
+ },
53
+ 'TRC': {
54
+ requiredParams: ['bottom_center', 'height_vector', 'bottom_radius', 'top_radius'],
55
+ description: '円錐台(異なる上下半径)'
56
+ },
57
+ 'WED': {
58
+ requiredParams: ['vertex', 'width_vector', 'height_vector', 'depth_vector'],
59
+ description: 'くさび形(頂点+3方向ベクトル)'
60
+ }
61
+ };
62
+
63
+ static VectorSchema = z.string().refine(val => {
64
+ const parts = val.trim().split(/\s+/);
65
+ return parts.length === 3 && parts.every(x => !isNaN(Number(x)) && isFinite(Number(x)));
66
+ }, "3つの有限数値(空白区切り)で指定してください");
67
+
68
+ static PositiveNumberSchema = z.union([
69
+ z.number().positive(),
70
+ z.string().refine(val => {
71
+ const num = Number(val);
72
+ return !isNaN(num) && num > 0 && isFinite(num);
73
+ }, "正の数値を指定してください")
74
+ ]).transform(val => Number(val));
75
+
76
+ static validateMaterialDensity(material, density) {
77
+ const materialProperties = this.getMaterialProperties();
78
+ const properties = materialProperties[material];
79
+
80
+ if (!properties) {
81
+ throw new ValidationError(
82
+ `未知の材料です: ${material}`,
83
+ 'material',
84
+ material
85
+ );
86
+ }
87
+
88
+ if (material === 'VOID') {
89
+ if (density !== undefined && density !== 0) {
90
+ throw new PhysicsError(
91
+ 'VOID材料の密度は0でなければなりません',
92
+ 'VOID_DENSITY_ERROR'
93
+ );
94
+ }
95
+ return true;
96
+ }
97
+
98
+ if (density === undefined || density === null) {
99
+ throw new ValidationError(
100
+ `材料 ${material} には密度の指定が必要です`,
101
+ 'density',
102
+ density
103
+ );
104
+ }
105
+
106
+ const numDensity = Number(density);
107
+ const { min, max } = properties.densityRange;
108
+
109
+ if (numDensity < min || numDensity > max) {
110
+ throw new PhysicsError(
111
+ `材料 ${material} の密度 ${numDensity} は物理的範囲 [${min}, ${max}] g/cm³ を外れています`,
112
+ 'DENSITY_OUT_OF_RANGE'
113
+ );
114
+ }
115
+
116
+ return true;
117
+ }
118
+
119
+ static validateGeometry(type, params, context = null) {
120
+ const geometryDef = this.GEOMETRY_TYPES[type];
121
+
122
+ if (!geometryDef) {
123
+ throw new ValidationError(
124
+ `未サポートの形状タイプ: ${type}`,
125
+ 'type',
126
+ type
127
+ );
128
+ }
129
+
130
+ // 必須パラメータのチェック
131
+ for (const param of geometryDef.requiredParams) {
132
+ if (!(param in params) || params[param] === undefined) {
133
+ throw new ValidationError(
134
+ `${type}型には${param}パラメータが必須です`,
135
+ param,
136
+ undefined
137
+ );
138
+ }
139
+ }
140
+
141
+ // 型別の詳細バリデーション
142
+ switch (type) {
143
+ case 'SPH':
144
+ this.validateSphere(params);
145
+ break;
146
+ case 'RCC':
147
+ this.validateCylinder(params);
148
+ break;
149
+ case 'RPP':
150
+ this.validateRectangularParallelepiped(params);
151
+ break;
152
+ case 'BOX':
153
+ this.validateBox(params);
154
+ break;
155
+ case 'CMB':
156
+ this.validateCombination(params, context);
157
+ break;
158
+ case 'TOR':
159
+ this.validateTorus(params);
160
+ break;
161
+ case 'ELL':
162
+ this.validateEllipsoid(params);
163
+ break;
164
+ case 'REC':
165
+ this.validateEllipticalCylinder(params);
166
+ break;
167
+ case 'TRC':
168
+ this.validateTruncatedCone(params);
169
+ break;
170
+ case 'WED':
171
+ this.validateWedge(params);
172
+ break;
173
+ }
174
+
175
+ return true;
176
+ }
177
+
178
+ static validateSphere(params) {
179
+ this.VectorSchema.parse(params.center);
180
+ const radius = this.PositiveNumberSchema.parse(params.radius);
181
+
182
+ if (radius > 1000) {
183
+ logger.warn('非常に大きな球体が定義されています', { radius });
184
+ }
185
+ }
186
+
187
+ static validateCylinder(params) {
188
+ this.VectorSchema.parse(params.bottom_center);
189
+ this.VectorSchema.parse(params.height_vector);
190
+ const radius = this.PositiveNumberSchema.parse(params.radius);
191
+
192
+ const [hx, hy, hz] = params.height_vector.split(/\s+/).map(Number);
193
+ const height = Math.sqrt(hx*hx + hy*hy + hz*hz);
194
+
195
+ if (height === 0) {
196
+ throw new PhysicsError(
197
+ '円柱の高さベクトルは零ベクトルにできません',
198
+ 'ZERO_HEIGHT_VECTOR'
199
+ );
200
+ }
201
+ }
202
+
203
+ static validateRectangularParallelepiped(params) {
204
+ this.VectorSchema.parse(params.min);
205
+ this.VectorSchema.parse(params.max);
206
+
207
+ const [minX, minY, minZ] = params.min.split(/\s+/).map(Number);
208
+ const [maxX, maxY, maxZ] = params.max.split(/\s+/).map(Number);
209
+
210
+ if (minX >= maxX || minY >= maxY || minZ >= maxZ) {
211
+ throw new PhysicsError(
212
+ 'RPPの最小座標は最大座標より小さくなければなりません',
213
+ 'INVALID_RPP_BOUNDS'
214
+ );
215
+ }
216
+ }
217
+
218
+ static validateBox(params) {
219
+ this.VectorSchema.parse(params.vertex);
220
+ this.VectorSchema.parse(params.edge_1);
221
+ this.VectorSchema.parse(params.edge_2);
222
+ this.VectorSchema.parse(params.edge_3);
223
+
224
+ // エッジベクトルの長さチェック
225
+ const edges = [params.edge_1, params.edge_2, params.edge_3];
226
+ for (let i = 0; i < 3; i++) {
227
+ const [x, y, z] = edges[i].split(/\s+/).map(Number);
228
+ const length = Math.sqrt(x*x + y*y + z*z);
229
+ if (length === 0) {
230
+ throw new PhysicsError(
231
+ `BOXのエッジ${i+1}ベクトルは零ベクトルにできません`,
232
+ 'ZERO_EDGE_VECTOR'
233
+ );
234
+ }
235
+ }
236
+ }
237
+
238
+ static validateCombination(params, context = null) {
239
+ if (!params.expression || typeof params.expression !== 'string' || params.expression.trim() === '') {
240
+ throw new ValidationError(
241
+ 'CMB型にはexpressionパラメータが必要です',
242
+ 'expression',
243
+ params.expression
244
+ );
245
+ }
246
+
247
+ // 新しいCMBValidatorを使用した詳細バリデーション
248
+ if (context && context.name && context.existingBodies) {
249
+ CMBValidator.validateExpression(params.expression, context.name, context.existingBodies);
250
+ } else {
251
+ // contextがない場合は基本チェックのみ
252
+ CMBValidator.validateBasicSyntax(params.expression);
253
+ }
254
+ }
255
+
256
+ static validateTorus(params) {
257
+ this.VectorSchema.parse(params.center);
258
+ this.VectorSchema.parse(params.normal);
259
+
260
+ const majorRadius = this.PositiveNumberSchema.parse(params.major_radius);
261
+ const minorRadiusH = this.PositiveNumberSchema.parse(params.minor_radius_horizontal);
262
+ const minorRadiusV = this.PositiveNumberSchema.parse(params.minor_radius_vertical);
263
+
264
+ // 法線ベクトルの長さチェック
265
+ const [nx, ny, nz] = params.normal.split(/\s+/).map(Number);
266
+ const normalLength = Math.sqrt(nx*nx + ny*ny + nz*nz);
267
+ if (normalLength === 0) {
268
+ throw new PhysicsError(
269
+ 'TORの法線ベクトルは零ベクトルにできません',
270
+ 'ZERO_NORMAL_VECTOR'
271
+ );
272
+ }
273
+
274
+ // 主半径と副半径の関係チェック
275
+ if (majorRadius <= Math.max(minorRadiusH, minorRadiusV)) {
276
+ throw new PhysicsError(
277
+ 'TORの主半径は副半径より大きくなければなりません',
278
+ 'INVALID_TORUS_RADII'
279
+ );
280
+ }
281
+ }
282
+
283
+ static validateEllipsoid(params) {
284
+ this.VectorSchema.parse(params.center);
285
+ this.VectorSchema.parse(params.radius_vector_1);
286
+ this.VectorSchema.parse(params.radius_vector_2);
287
+ this.VectorSchema.parse(params.radius_vector_3);
288
+
289
+ // 半径ベクトルの長さチェック
290
+ const vectors = [params.radius_vector_1, params.radius_vector_2, params.radius_vector_3];
291
+ for (let i = 0; i < 3; i++) {
292
+ const [x, y, z] = vectors[i].split(/\s+/).map(Number);
293
+ const length = Math.sqrt(x*x + y*y + z*z);
294
+ if (length === 0) {
295
+ throw new PhysicsError(
296
+ `ELLの半径ベクトル${i+1}は零ベクトルにできません`,
297
+ 'ZERO_RADIUS_VECTOR'
298
+ );
299
+ }
300
+ }
301
+ }
302
+
303
+ static validateEllipticalCylinder(params) {
304
+ this.VectorSchema.parse(params.bottom_center);
305
+ this.VectorSchema.parse(params.height_vector);
306
+ this.VectorSchema.parse(params.radius_vector_1);
307
+ this.VectorSchema.parse(params.radius_vector_2);
308
+
309
+ // 高さベクトルの長さチェック
310
+ const [hx, hy, hz] = params.height_vector.split(/\s+/).map(Number);
311
+ const height = Math.sqrt(hx*hx + hy*hy + hz*hz);
312
+ if (height === 0) {
313
+ throw new PhysicsError(
314
+ 'RECの高さベクトルは零ベクトルにできません',
315
+ 'ZERO_HEIGHT_VECTOR'
316
+ );
317
+ }
318
+
319
+ // 半径ベクトルの長さチェック
320
+ const vectors = [params.radius_vector_1, params.radius_vector_2];
321
+ for (let i = 0; i < 2; i++) {
322
+ const [x, y, z] = vectors[i].split(/\s+/).map(Number);
323
+ const length = Math.sqrt(x*x + y*y + z*z);
324
+ if (length === 0) {
325
+ throw new PhysicsError(
326
+ `RECの半径ベクトル${i+1}は零ベクトルにできません`,
327
+ 'ZERO_RADIUS_VECTOR'
328
+ );
329
+ }
330
+ }
331
+ }
332
+
333
+ static validateTruncatedCone(params) {
334
+ this.VectorSchema.parse(params.bottom_center);
335
+ this.VectorSchema.parse(params.height_vector);
336
+
337
+ const bottomRadius = this.PositiveNumberSchema.parse(params.bottom_radius);
338
+ const topRadius = this.PositiveNumberSchema.parse(params.top_radius);
339
+
340
+ // 高さベクトルの長さチェック
341
+ const [hx, hy, hz] = params.height_vector.split(/\s+/).map(Number);
342
+ const height = Math.sqrt(hx*hx + hy*hy + hz*hz);
343
+ if (height === 0) {
344
+ throw new PhysicsError(
345
+ 'TRCの高さベクトルは零ベクトルにできません',
346
+ 'ZERO_HEIGHT_VECTOR'
347
+ );
348
+ }
349
+
350
+ // 半径の物理的妃当性チェック
351
+ if (bottomRadius === topRadius) {
352
+ logger.warn('TRCの上下半径が同じです。RCCの使用を検討してください', {
353
+ bottomRadius, topRadius
354
+ });
355
+ }
356
+ }
357
+
358
+ static validateWedge(params) {
359
+ this.VectorSchema.parse(params.vertex);
360
+ this.VectorSchema.parse(params.width_vector);
361
+ this.VectorSchema.parse(params.height_vector);
362
+ this.VectorSchema.parse(params.depth_vector);
363
+
364
+ // ベクトルの長さチェック
365
+ const vectors = [
366
+ { name: 'width_vector', value: params.width_vector },
367
+ { name: 'height_vector', value: params.height_vector },
368
+ { name: 'depth_vector', value: params.depth_vector }
369
+ ];
370
+
371
+ for (const vector of vectors) {
372
+ const [x, y, z] = vector.value.split(/\s+/).map(Number);
373
+ const length = Math.sqrt(x*x + y*y + z*z);
374
+ if (length === 0) {
375
+ throw new PhysicsError(
376
+ `WEDの${vector.name}は零ベクトルにできません`,
377
+ 'ZERO_VECTOR'
378
+ );
379
+ }
380
+ }
381
+
382
+ // ベクトルの線形独立性チェック(簡単なチェック)
383
+ const [wx, wy, wz] = params.width_vector.split(/\s+/).map(Number);
384
+ const [hx, hy, hz] = params.height_vector.split(/\s+/).map(Number);
385
+ const [dx, dy, dz] = params.depth_vector.split(/\s+/).map(Number);
386
+
387
+ // 外積で平行性をチェック
388
+ const cross1 = [
389
+ wy * hz - wz * hy,
390
+ wz * hx - wx * hz,
391
+ wx * hy - wy * hx
392
+ ];
393
+ const crossLength = Math.sqrt(cross1[0]*cross1[0] + cross1[1]*cross1[1] + cross1[2]*cross1[2]);
394
+ if (crossLength < 1e-10) {
395
+ throw new PhysicsError(
396
+ 'WEDのwidth_vectorとheight_vectorが平行です',
397
+ 'PARALLEL_VECTORS'
398
+ );
399
+ }
400
+ }
401
+ }
@@ -0,0 +1,87 @@
1
+ // src/validators/PhysicsValidatorSimple.js
2
+ import { z } from 'zod';
3
+ import { ValidationError, PhysicsError } from '../utils/errors.js';
4
+ import { logger } from '../utils/logger.js';
5
+
6
+ export class PhysicsValidator {
7
+ static MATERIAL_PROPERTIES = {
8
+ 'Concrete': { densityRange: { min: 1.8, max: 2.5 }, type: 'structural' },
9
+ 'Iron': { densityRange: { min: 7.6, max: 7.9 }, type: 'metal' },
10
+ 'Lead': { densityRange: { min: 11.0, max: 11.4 }, type: 'heavy_metal' },
11
+ 'Water': { densityRange: { min: 0.95, max: 1.05 }, type: 'liquid' },
12
+ 'Air': { densityRange: { min: 0.001, max: 0.002 }, type: 'gas' },
13
+ 'VOID': { densityRange: { min: 0, max: 0 }, type: 'void' }
14
+ };
15
+
16
+ static GEOMETRY_TYPES = {
17
+ 'SPH': { requiredParams: ['center', 'radius'] },
18
+ 'RCC': { requiredParams: ['bottom_center', 'height_vector', 'radius'] },
19
+ 'RPP': { requiredParams: ['min', 'max'] },
20
+ 'BOX': { requiredParams: ['vertex', 'edge_1', 'edge_2', 'edge_3'] },
21
+ 'CMB': { requiredParams: ['expression'] }
22
+ };
23
+
24
+ static VectorSchema = z.string().refine(val => {
25
+ const parts = val.trim().split(/\s+/);
26
+ return parts.length === 3 && parts.every(x => !isNaN(Number(x)));
27
+ }, "3つの数値(空白区切り)で指定してください");
28
+
29
+ static PositiveNumberSchema = z.union([
30
+ z.number().positive(),
31
+ z.string().refine(val => !isNaN(Number(val)) && Number(val) > 0, "正の数値")
32
+ ]).transform(val => Number(val));
33
+
34
+ static validateMaterialDensity(material, density) {
35
+ const properties = this.MATERIAL_PROPERTIES[material];
36
+
37
+ if (!properties) {
38
+ throw new ValidationError(`未知の材料: ${material}`, 'material', material);
39
+ }
40
+
41
+ if (material === 'VOID') {
42
+ if (density !== undefined && density !== 0) {
43
+ throw new PhysicsError('VOID材料の密度は0である必要があります');
44
+ }
45
+ return true;
46
+ }
47
+
48
+ if (density === undefined || density === null) {
49
+ throw new ValidationError(`材料 ${material} には密度が必要です`, 'density', density);
50
+ }
51
+
52
+ const numDensity = Number(density);
53
+ const { min, max } = properties.densityRange;
54
+
55
+ if (numDensity < min || numDensity > max) {
56
+ throw new PhysicsError(
57
+ `材料 ${material} の密度 ${numDensity} は範囲 [${min}, ${max}] g/cm³ を外れています`
58
+ );
59
+ }
60
+
61
+ return true;
62
+ }
63
+
64
+ static validateGeometry(type, params) {
65
+ const geometryDef = this.GEOMETRY_TYPES[type];
66
+
67
+ if (!geometryDef) {
68
+ throw new ValidationError(`未サポートの形状: ${type}`, 'type', type);
69
+ }
70
+
71
+ for (const param of geometryDef.requiredParams) {
72
+ if (!(param in params) || params[param] === undefined) {
73
+ throw new ValidationError(`${type}型には${param}が必須です`, param, undefined);
74
+ }
75
+ }
76
+
77
+ // 基本的な値のバリデーション
78
+ if (type === 'SPH' && params.radius) {
79
+ this.PositiveNumberSchema.parse(params.radius);
80
+ }
81
+ if (type === 'RCC' && params.radius) {
82
+ this.PositiveNumberSchema.parse(params.radius);
83
+ }
84
+
85
+ return true;
86
+ }
87
+ }