Skip to main content
Glama
Hirao-Y

Poker Task Management MCP

by Hirao-Y

poker_executeCalculation

Execute radiation shielding calculations using YAML configuration files to generate dose summaries and output files for analysis.

Instructions

作成したYAMLファイルを使用してpoker_cuiで放射線遮蔽計算を実行します

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
output_filesNo出力ファイル指定(入力YAMLファイルと同じフォルダに自動作成、全てYAML形式)
summary_optionsNoサマリー出力オプション(注意: show_source_dataとshow_total_doseの少なくとも一方は必須です)
yaml_fileYes計算に使用するYAMLファイル名(拡張子.yamlを含む)

Implementation Reference

  • MCP tool handler that validates input arguments, performs pre-validation checks, executes calculation via service, verifies outputs, and returns formatted response with error handling.
    async executeCalculation(args) {
      try {
        // 引数検証
        if (!args.yaml_file) {
          throw new ValidationError('YAML file is required', 'yaml_file', args.yaml_file);
        }
    
        // 絶対パス形式の事前チェック(詳細検証はサービス層で実行)
        const absolutePathPattern = /^([a-zA-Z]:[\\\/]|\/)/;
        if (!absolutePathPattern.test(args.yaml_file)) {
          throw new ValidationError(
            'YAML file must be specified with absolute path. Examples: C:\\path\\to\\file.yaml or /path/to/file.yaml',
            'yaml_file',
            args.yaml_file
          );
        }
    
        // YAML拡張子チェック
        if (!args.yaml_file.match(/\.(yaml|yml)$/i)) {
          throw new ValidationError(
            'YAML file must have .yaml or .yml extension',
            'yaml_file',
            args.yaml_file
          );
        }
    
        // サマリーオプション検証
        const summaryOptions = args.summary_options || {};
        if (summaryOptions.show_source_data === false && 
            summaryOptions.show_total_dose === false) {
          logger.warn('Both show_source_data and show_total_dose are false, this may result in minimal output');
        }
    
        // 出力ファイルの自動パス解決
        const outputFiles = args.output_files || {};
        const resolvedOutputFiles = {};
        
        if (Object.keys(outputFiles).length > 0) {
          // 入力YAMLファイルのディレクトリを取得
          const inputDir = path.dirname(args.yaml_file);
          
          // 各出力ファイルの絶対パス化
          for (const [key, fileName] of Object.entries(outputFiles)) {
            if (fileName) {
              // ファイル名の拡張子チェック
              if (!fileName.match(/\.(yaml|yml)$/i)) {
                throw new ValidationError(
                  `Output file ${key} must have .yaml or .yml extension`,
                  `output_files.${key}`,
                  fileName
                );
              }
              
              // 絶対パスに解決
              resolvedOutputFiles[key] = path.join(inputDir, fileName);
              
              logger.debug('Output file path resolved', {
                key,
                fileName,
                inputDir,
                resolvedPath: resolvedOutputFiles[key]
              });
            }
          }
        }
    
        // 出力ファイル検証(従来の検証は不要、上記で実施済み)
    
        logger.info('Starting radiation shielding calculation', {
          yamlFile: args.yaml_file,
          summaryOptions,
          outputFiles: outputFiles,
          resolvedOutputFiles
        });
    
        // 計算実行(統合検証付き、絶対パス化済みの出力ファイルパスを使用)
        const result = await calculationService.executeWithValidation(
          args.yaml_file,
          summaryOptions,
          resolvedOutputFiles,  // 絶対パス化済み
          undefined,           // timeout(デフォルト値を使用)
          taskManager.dataManager  // 事前検証用DataManager
        );
    
        // 事前検証で重大エラーが検出された場合の処理
        if (result.success === false && result.stage === 'pre_validation') {
          logger.error('Pre-calculation validation failed', {
            yamlFile: args.yaml_file,
            criticalErrors: result.criticalErrors
          });
          
          return {
            success: false,
            error: result.error,
            error_type: 'PRE_VALIDATION_FAILED',
            message: result.message,
            mcp_error_code: -32048, // Custom code for pre-validation failure
            validation_details: result.validationResult,
            critical_errors: result.criticalErrors,
            details: {
              category: 'pre_validation_error',
              recoverable: true,
              suggestions: [
                'Check daughter nuclide configurations',
                'Review material definitions',
                'Ensure all required parameters are properly defined',
                'Note: Geometry collisions will not block calculation'
              ]
            }
          };
        }
    
        // 出力ファイル検証(絶対パス化済みのパスを使用)
        const fileVerification = await calculationService.verifyOutputFiles(resolvedOutputFiles);
    
        // 成功レスポンス
        const response = {
          success: true,
          message: 'Radiation shielding calculation completed successfully',
          calculation: {
            input_file: args.yaml_file,
            execution_time_ms: result.executionTime,
            timestamp: result.summary.timestamp,
            options: result.summary.options
          },
          validation: {
            pre_calculation_performed: result.preValidation?.performed || false,
            pre_calculation_passed: result.preValidation?.passed || false,
            details: result.preValidation?.details || 'No pre-validation performed'
          },
          outputs: {
            console: {
              stdout: result.outputs.stdout ? 'Available (see details)' : 'Empty',
              stderr: result.outputs.stderr ? 'Available (see details)' : 'Empty',
              stdout_length: result.outputs.stdout.length,
              stderr_length: result.outputs.stderr.length
            },
            files: fileVerification
          },
          details: {
            stdout: result.outputs.stdout,
            stderr: result.outputs.stderr,
            summary_options_applied: result.summary.options,
            poker_cui_command: 'poker_cui executed successfully'
          }
        };
    
        // 出力ファイル生成の警告
        if (!fileVerification.allFilesGenerated) {
          response.warnings = ['Some output files were not generated. Check calculation logs for details.'];
        }
    
        logger.info('Calculation completed successfully', {
          yamlFile: args.yaml_file,
          executionTime: result.executionTime,
          outputFiles: Object.keys(fileVerification).filter(k => k !== 'allFilesGenerated')
        });
    
        return response;
    
      } catch (error) {
        logger.error('executeCalculation handler error', { 
          args, 
          error: error.message,
          errorType: error.constructor.name
        });
    
        // CalculationErrorの場合はMCPエラーコードも含める
        if (error instanceof CalculationError) {
          return {
            success: false,
            error: error.message,
            error_type: error.code,
            context: error.context,
            mcp_error_code: error.mcpErrorCode,
            details: {
              category: 'calculation_error',
              recoverable: this._isRecoverableError(error.code),
              suggestions: this._getErrorSuggestions(error.code, error.context)
            }
          };
        }
    
        // ValidationErrorの場合
        if (error instanceof ValidationError) {
          return {
            success: false,
            error: error.message,
            error_type: 'VALIDATION_ERROR',
            field: error.field,
            value: error.value,
            mcp_error_code: -32047, // Invalid calculation parameters
            details: {
              category: 'parameter_error',
              recoverable: true,
              suggestions: ['Check parameter format and values', 'Ensure all required parameters are provided']
            }
          };
        }
    
        // その他のエラー
        throw error;
      }
    },
  • Tool schema defining input parameters: yaml_file (required), summary_options, output_files with validation patterns and descriptions.
    name: 'poker_executeCalculation',
    description: '作成したYAMLファイルを使用してpoker_cuiで放射線遮蔽計算を実行します',
    inputSchema: {
      type: 'object',
      properties: {
        yaml_file: {
          type: 'string',
          description: '計算に使用するYAMLファイル名(拡張子.yamlを含む)',
          pattern: '^[a-zA-Z0-9_\\-\\.]+\\.(yaml|yml)$'
        },
        summary_options: {
          type: 'object',
          description: 'サマリー出力オプション(注意: show_source_dataとshow_total_doseの少なくとも一方は必須です)',
          properties: {
            show_parameters: {
              type: 'boolean',
              description: '入力パラメータをサマリーに表示(-pオプション)',
              default: false
            },
            show_source_data: {
              type: 'boolean',
              description: '各線源による計算データ及び線量をサマリーに表示(-sオプション)',
              default: true
            },
            show_total_dose: {
              type: 'boolean',
              description: '各検出器に対する全線源からの総和線量をサマリーに表示(-tオプション)',
              default: true
            }
          },
          additionalProperties: false
        },
        output_files: {
          type: 'object',
          description: '出力ファイル指定(入力YAMLファイルと同じフォルダに自動作成、全てYAML形式)',
          properties: {
            summary_file: {
              type: 'string',
              description: '出力サマリーファイル名(-oオプション、ファイル名のみ指定、自動的に入力ファイルと同じディレクトリに配置)',
              pattern: '^[a-zA-Z0-9_\\-\\.]+\\.(yaml|yml)$'
            },
            dose_file: {
              type: 'string',
              description: '線量ファイル名(-dオプション、ファイル名のみ指定、自動的に入力ファイルと同じディレクトリに配置)',
              pattern: '^[a-zA-Z0-9_\\-\\.]+\\.(yaml|yml)$'
            }
          },
          additionalProperties: false
        }
      },
      required: ['yaml_file'],
      additionalProperties: false
    }
  • Registers the tool call handler in MCP server, mapping 'poker_executeCalculation' to 'executeCalculation' by removing 'poker_' prefix and invoking the corresponding handler function.
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
      
      logger.info(`MCP Tool実行: ${name}`, { args });
      
      // ハンドラー名をツール名から生成(プレフィックス除去)
      const handlerName = name.replace('poker_', '');
      
      const handler = this.handlers[handlerName];
      if (!handler) {
        throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
      }
      
      return await safeExecute(async () => handler(args), { tool: name })();
    });
  • Helper service implementing integrated validation (daughter nuclides, geometry), executes core calculation, handles blocking conditions like daughter nuclide confirmation.
    async executeWithValidation(yamlFile, summaryOptions = {}, outputFiles = {}, timeout = this.defaultTimeout, dataManager = null) {
      try {
        logger.info('統合検証付き計算実行を開始', { yamlFile });
    
        // 事前検証の実行
        if (dataManager) {
          logger.info('計算実行前の統合検証を実行中...');
          
          const validationResult = await dataManager.performPreCalculationValidation();
          
          // 立体衝突チェックは警告のみとし、計算を中断しない
          if (validationResult.collisionCheck?.hasCollisions) {
            logger.warn('立体干渉が検出されましたが、計算を続行します', {
              collisions: validationResult.collisionCheck.collisions,
              totalIssues: validationResult.collisionCheck.totalIssues
            });
          }
          
          // 子孫核種チェックは維持(物理的に必須)
          if (validationResult.daughterNuclideCheck?.totalAdditions > 0 && 
              !dataManager.daughterNuclideCheckDisabled) {
            logger.info('子孫核種が検出されました - 計算を中断してユーザー確認を要求');
            
            return {
              success: false,
              stage: 'requires_confirmation',
              status: 'requires_confirmation',
              calculation_blocked: true,
              error: 'DAUGHTER_NUCLIDE_CONFIRMATION_REQUIRED',
              message: '子孫核種が検出されました。poker_confirmDaughterNuclidesで確認してください',
              daughter_nuclide_suggestions: this.formatDaughterNuclideSuggestions(validationResult.daughterNuclideCheck),
              total_additions: validationResult.daughterNuclideCheck.totalAdditions,
              available_actions: [
                'poker_confirmDaughterNuclides action="check" - 詳細確認',
                'poker_confirmDaughterNuclides action="confirm" - 承認して適用',
                'poker_confirmDaughterNuclides action="confirm_with_modifications" - 修正して適用',
                'poker_confirmDaughterNuclides action="reject" - 拒否'
              ],
              next_step: 'poker_confirmDaughterNuclidesを実行後、再度poker_executeCalculationを実行してください'
            };
          }
          
          // 立体衝突以外の重大エラーのみチェック
          const nonCollisionErrors = validationResult.criticalErrors?.filter(
            error => error.type !== 'collision_detected'
          ) || [];
          
          if (nonCollisionErrors.length > 0) {
            logger.error('計算実行前に解決が必要な重大エラーを検出', {
              criticalErrorCount: nonCollisionErrors.length
            });
            
            return {
              success: false,
              stage: 'pre_validation',
              error: 'CRITICAL_VALIDATION_ERRORS',
              message: '計算実行前に解決が必要な重大エラーがあります',
              validationResult,
              criticalErrors: nonCollisionErrors
            };
          }
    
          if (!validationResult.overall) {
            logger.warn('検証で警告が検出されましたが、計算を継続します', {
              warningCount: validationResult.warnings.length
            });
          }
    
          // 検証結果をログに記録
          logger.info('事前検証完了', {
            overall: validationResult.overall,
            collisionCheck: validationResult.collisionCheck?.hasCollisions || false,
            daughterNuclideIssues: validationResult.daughterNuclideCheck?.totalAdditions || 0,
            enhancedValidationPassed: validationResult.enhancedValidation?.overall || false
          });
        }
    
        // 通常の計算実行
        const calculationResult = await this.executeCalculation(yamlFile, summaryOptions, outputFiles, timeout);
    
        // poker_cuiがエラーを返した場合、事後診断を実行
        if (!calculationResult.success && dataManager) {
          logger.info('計算エラーが発生したため、事後診断を実行します');
          
          const postValidation = await dataManager.performPreCalculationValidation();
          
          // エラー診断情報を結果に追加
          calculationResult.diagnostics = {
            performed: true,
            collisionCheck: postValidation.collisionCheck,
            suggestions: []
          };
          
          if (postValidation.collisionCheck?.hasCollisions) {
            calculationResult.diagnostics.suggestions.push(
              '立体干渉が検出されました。組み合わせ立体(CMB)の使用を検討してください'
            );
          }
        }
    
        // 結果に検証情報を追加
        if (dataManager) {
          calculationResult.preValidation = {
            performed: true,
            passed: true,
            details: 'Pre-calculation validation completed with warnings only'
          };
        }
    
        return calculationResult;
    
      } catch (error) {
        logger.error('統合検証付き計算実行でエラー', { error: error.message, yamlFile });
        throw new CalculationError(`統合計算実行エラー: ${error.message}`, 'INTEGRATED_EXECUTION_ERROR');
      }
    }
  • Low-level helper that spawns the 'poker_cui' external command, manages process I/O, timeout, and error conditions.
    async _executePokercui(args, timeout, context) {
      return new Promise((resolve, reject) => {
        const startTime = Date.now();
        let isTimedOut = false;
        let isCompleted = false;
        
        const child = spawn(this.pokercuiCommand, args, {
          stdio: 'pipe',
          shell: true,
          env: { ...process.env }
        });
    
        let stdout = '';
        let stderr = '';
    
        // データ収集
        child.stdout.on('data', (data) => {
          const chunk = data.toString();
          stdout += chunk;
          logger.debug('poker_cui stdout chunk', { chunk: chunk.substring(0, 200) });
        });
    
        child.stderr.on('data', (data) => {
          const chunk = data.toString();
          stderr += chunk;
          logger.debug('poker_cui stderr chunk', { chunk: chunk.substring(0, 200) });
        });
    
        // プロセス終了処理
        child.on('close', (code, signal) => {
          if (isCompleted) return;
          isCompleted = true;
    
          const endTime = Date.now();
          const executionTime = endTime - startTime;
    
          logger.info('poker_cui execution completed', {
            code,
            signal,
            executionTime,
            stdoutLength: stdout.length,
            stderrLength: stderr.length
          });
    
          if (isTimedOut) {
            reject(new CalculationError(
              `Calculation timed out after ${timeout}ms`,
              'CALCULATION_TIMEOUT',
              { ...context, executionTime, timeout },
              -32044
            ));
            return;
          }
    
          if (code !== 0) {
            reject(new CalculationError(
              `poker_cui execution failed with exit code ${code}`,
              'CALCULATION_EXECUTION_FAILED',
              { 
                ...context, 
                exitCode: code, 
                signal,
                stdout: stdout.substring(0, 1000),
                stderr: stderr.substring(0, 1000),
                executionTime
              },
              -32043
            ));
            return;
          }
    
          // 成功時の結果処理
          resolve({
            success: true,
            executionTime,
            outputs: {
              stdout: stdout.trim(),
              stderr: stderr.trim()
            },
            files: this._identifyOutputFiles(context.outputFiles),
            summary: {
              message: 'Calculation completed successfully',
              yamlFile: context.yamlFile,
              options: context.summaryOptions,
              timestamp: new Date().toISOString()
            }
          });
        });
    
        // エラー処理
        child.on('error', (error) => {
          if (isCompleted) return;
          isCompleted = true;
    
          logger.error('poker_cui process error', { error: error.message, context });
          
          if (error.code === 'ENOENT') {
            reject(new CalculationError(
              'poker_cui command not found',
              'POKER_CUI_NOT_AVAILABLE',
              { ...context, systemError: error.message },
              -32042
            ));
          } else {
            reject(new CalculationError(
              `Process execution error: ${error.message}`,
              'CALCULATION_EXECUTION_FAILED',
              { ...context, systemError: error.message },
              -32043
            ));
          }
        });
    
        // タイムアウト処理
        const timeoutId = setTimeout(() => {
          if (isCompleted) return;
          
          isTimedOut = true;
          logger.warn('poker_cui calculation timeout, killing process', { 
            timeout, 
            context 
          });
          
          child.kill('SIGKILL');
        }, timeout);
    
        // プロセス完了時にタイマーをクリア
        child.on('close', () => {
          clearTimeout(timeoutId);
        });
      });
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hirao-Y/poker_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server