Skip to main content
Glama

Poker Task Management MCP

by Hirao-Y
UnitValidator.js16.1 kB
// validators/UnitValidator.js import { PokerMcpError } from '../utils/mcpErrors.js'; import { ManifestValidator } from './ManifestValidator.js'; /** * Unit系の4キー完全性保証バリデーター * length, angle, density, radioactivityの4キー必須性を厳格管理 */ export class UnitValidator { /** * 必須4キーの完全定義(遅延初期化版) * マニフェスト仕様に完全準拠 */ static get REQUIRED_UNIT_KEYS() { if (!this._cachedUnitKeys) { this._cachedUnitKeys = { length: { description: '長さの単位', allowedValues: ['m', 'cm', 'mm'], default: 'cm', physicalDimension: 'L', siBase: 'm', conversionFactors: { 'm': 1.0, 'cm': 0.01, 'mm': 0.001 } }, angle: { description: '角度の単位', allowedValues: ['radian', 'degree'], default: 'radian', physicalDimension: 'dimensionless', siBase: 'radian', conversionFactors: { 'radian': 1.0, 'degree': Math.PI / 180.0 // 遅延計算 } }, density: { description: '密度の単位', allowedValues: ['g/cm3'], default: 'g/cm3', physicalDimension: 'ML^-3', siBase: 'kg/m3', conversionFactors: { 'g/cm3': 1000.0 // g/cm³ to kg/m³ } }, radioactivity: { description: '放射能の単位', allowedValues: ['Bq'], default: 'Bq', physicalDimension: 'T^-1', siBase: 'Bq', conversionFactors: { 'Bq': 1.0 } } }; } return this._cachedUnitKeys; } /** * 単位システムの物理的整合性チェック用定数 */ static PHYSICAL_CONSISTENCY = { dimensionalAnalysis: { 'L': 'length', // 長さ次元 'T': 'time', // 時間次元 'M': 'mass', // 質量次元 'ML^-3': 'density', // 密度次元 'T^-1': 'frequency' // 頻度次元(放射能) }, unitCompatibility: { volume: ['length^3'], area: ['length^2'], mass: ['density * volume'], activity: ['radioactivity'] } }; /** * 4キー完全性の基本検証 * 全ての必須キーの存在と型を確認 */ static validateFourKeyCompleteness(unitData) { if (!unitData || typeof unitData !== 'object') { throw PokerMcpError.validationError( 'Unit data must be an object with all 4 required keys', 'unitData', unitData ); } const providedKeys = Object.keys(unitData); const requiredKeys = Object.keys(this.REQUIRED_UNIT_KEYS); // 必須キーの存在チェック const missingKeys = requiredKeys.filter(key => !providedKeys.includes(key)); if (missingKeys.length > 0) { throw PokerMcpError.validationError( `Missing required unit keys: ${missingKeys.join(', ')}. All 4 keys (length, angle, density, radioactivity) are mandatory`, 'missingKeys', missingKeys ); } // 余分なキーの検出 const extraKeys = providedKeys.filter(key => !requiredKeys.includes(key)); if (extraKeys.length > 0) { throw PokerMcpError.validationError( `Unknown unit keys found: ${extraKeys.join(', ')}. Only the 4 required keys are allowed`, 'extraKeys', extraKeys ); } // 各キーの値検証 for (const [key, value] of Object.entries(unitData)) { this.validateSingleUnitKey(key, value); } return true; } /** * 単一単位キーの詳細検証 */ static validateSingleUnitKey(key, value) { const keySpec = this.REQUIRED_UNIT_KEYS[key]; if (!keySpec) { throw PokerMcpError.validationError( `Invalid unit key: ${key}`, 'key', key ); } // 値の型チェック if (typeof value !== 'string') { throw PokerMcpError.validationError( `Unit key '${key}' value must be a string`, key, value ); } // 値の空文字チェック if (!value.trim()) { throw PokerMcpError.validationError( `Unit key '${key}' cannot be empty`, key, value ); } // 許可値チェック if (!keySpec.allowedValues.includes(value)) { throw PokerMcpError.validationError( `Invalid value '${value}' for unit key '${key}'. Allowed values: ${keySpec.allowedValues.join(', ')}`, key, value, `Expected one of: ${keySpec.allowedValues.join(', ')}` ); } return true; } /** * Unit構造の物理的整合性検証 * 単位系の物理的な一貫性をチェック */ static validatePhysicalConsistency(unitData) { this.validateFourKeyCompleteness(unitData); const consistencyReport = { isConsistent: true, warnings: [], recommendations: [] }; // SI単位系との整合性チェック for (const [key, value] of Object.entries(unitData)) { const keySpec = this.REQUIRED_UNIT_KEYS[key]; const conversionFactor = keySpec.conversionFactors[value]; if (Math.abs(conversionFactor - 1.0) > 1e-10) { consistencyReport.warnings.push({ type: 'non_si_unit', key, value, message: `Using non-SI unit '${value}' for ${key}. SI base unit is '${keySpec.siBase}'`, conversionFactor }); } } // 単位系の組み合わせ整合性 const lengthUnit = unitData.length; const densityUnit = unitData.density; // 密度と長さの単位の組み合わせチェック if (lengthUnit === 'cm' && densityUnit === 'g/cm3') { // CGS系の組み合わせ - 良好 consistencyReport.recommendations.push({ type: 'unit_system_consistency', message: 'Using consistent CGS unit system (cm + g/cm³)' }); } else if (lengthUnit === 'm' && densityUnit === 'g/cm3') { // 混合系 - 注意喚起 consistencyReport.warnings.push({ type: 'mixed_unit_system', message: 'Mixing SI length (m) with CGS density (g/cm³). Consider using consistent unit system' }); } // 角度単位の使用状況チェック const angleUnit = unitData.angle; if (angleUnit === 'degree') { consistencyReport.warnings.push({ type: 'angle_unit_warning', message: 'Using degree for angles. Radian is preferred for scientific calculations' }); } return consistencyReport; } /** * Unit更新時の4キー保持検証 * 部分更新でも4キー構造の維持を保証 */ static validatePartialUpdate(currentUnits, updates) { if (!currentUnits) { throw PokerMcpError.validationError( 'Current unit data is required for partial update validation', 'currentUnits', currentUnits ); } // 現在の単位データの完全性を事前確認 this.validateFourKeyCompleteness(currentUnits); if (!updates || typeof updates !== 'object') { throw PokerMcpError.validationError( 'Update data must be an object', 'updates', updates ); } const updateKeys = Object.keys(updates); // 更新キーの妥当性チェック const invalidKeys = updateKeys.filter(key => !Object.keys(this.REQUIRED_UNIT_KEYS).includes(key) ); if (invalidKeys.length > 0) { throw PokerMcpError.validationError( `Invalid unit keys in update: ${invalidKeys.join(', ')}`, 'invalidKeys', invalidKeys ); } // 更新後の仮想データを構築して検証 const updatedUnits = { ...currentUnits, ...updates }; this.validateFourKeyCompleteness(updatedUnits); // 更新される各キーの個別検証 for (const [key, value] of Object.entries(updates)) { this.validateSingleUnitKey(key, value); } return { isValid: true, updatedStructure: updatedUnits, changedKeys: updateKeys, preservedIntegrity: true }; } /** * Unit構造の正規化処理 * 4キー完全性を保持しつつデータを正規化 */ static normalizeUnitStructure(unitData) { // 基本検証 this.validateFourKeyCompleteness(unitData); const normalized = {}; // 必須4キーを順序保証で処理 const keyOrder = ['length', 'angle', 'density', 'radioactivity']; for (const key of keyOrder) { const value = unitData[key]; const keySpec = this.REQUIRED_UNIT_KEYS[key]; // 値の正規化(空白除去、小文字化) let normalizedValue = value.trim(); // 特別な正規化ルール if (key === 'angle') { if (normalizedValue === 'deg') normalizedValue = 'degree'; if (normalizedValue === 'rad') normalizedValue = 'radian'; } // 最終検証 this.validateSingleUnitKey(key, normalizedValue); normalized[key] = normalizedValue; } return normalized; } /** * Unit変換係数の計算 * 異なる単位系間の変換係数を提供 */ static calculateConversionFactors(fromUnits, toUnits) { this.validateFourKeyCompleteness(fromUnits); this.validateFourKeyCompleteness(toUnits); const conversionFactors = {}; for (const key of Object.keys(this.REQUIRED_UNIT_KEYS)) { const fromValue = fromUnits[key]; const toValue = toUnits[key]; const keySpec = this.REQUIRED_UNIT_KEYS[key]; const fromFactor = keySpec.conversionFactors[fromValue]; const toFactor = keySpec.conversionFactors[toValue]; // SI基準での変換係数計算 conversionFactors[key] = fromFactor / toFactor; } return { factors: conversionFactors, fromUnits, toUnits, isIdentity: Object.values(conversionFactors).every(f => Math.abs(f - 1.0) < 1e-10) }; } /** * Unit系のシステム全体整合性検証 * YAMLファイル内でのUnit使用状況を包括チェック */ static validateSystemUnitIntegrity(unitData, yamlData) { this.validateFourKeyCompleteness(unitData); const integrityReport = { unitStructure: 'valid', usageConsistency: [], potentialIssues: [], recommendations: [] }; // 長さ単位の使用状況チェック const lengthUnit = unitData.length; const lengthUsages = this.findLengthUsagesInYaml(yamlData); if (lengthUsages.length > 0) { integrityReport.usageConsistency.push({ unit: lengthUnit, usageCount: lengthUsages.length, contexts: lengthUsages.map(u => u.context) }); } // 密度単位の使用状況チェック const densityUnit = unitData.density; const densityUsages = this.findDensityUsagesInYaml(yamlData); if (densityUsages.length > 0) { integrityReport.usageConsistency.push({ unit: densityUnit, usageCount: densityUsages.length, contexts: densityUsages.map(u => u.context) }); } // 放射能単位の使用状況チェック const radioactivityUnit = unitData.radioactivity; const radioactivityUsages = this.findRadioactivityUsagesInYaml(yamlData); if (radioactivityUsages.length > 0) { integrityReport.usageConsistency.push({ unit: radioactivityUnit, usageCount: radioactivityUsages.length, contexts: radioactivityUsages.map(u => u.context) }); } // 角度単位の使用状況チェック(Transform) if (yamlData.transform && yamlData.transform.length > 0) { integrityReport.usageConsistency.push({ unit: unitData.angle, usageCount: yamlData.transform.length, contexts: ['transform_rotations'] }); } return integrityReport; } /** * YAML内の長さ使用箇所を検索 * @private */ static findLengthUsagesInYaml(yamlData) { const usages = []; // Body座標での使用 if (yamlData.body) { yamlData.body.forEach(body => { usages.push({ context: `body.${body.name}`, type: 'coordinates' }); }); } // Detector座標での使用 if (yamlData.detector) { yamlData.detector.forEach(detector => { usages.push({ context: `detector.${detector.name}`, type: 'coordinates' }); if (detector.grid) { detector.grid.forEach((_, idx) => { usages.push({ context: `detector.${detector.name}.grid[${idx}]`, type: 'edge_vector' }); }); } }); } // Source位置での使用 if (yamlData.source) { yamlData.source.forEach(source => { usages.push({ context: `source.${source.name}`, type: 'position' }); }); } return usages; } /** * YAML内の密度使用箇所を検索 * @private */ static findDensityUsagesInYaml(yamlData) { const usages = []; if (yamlData.zone) { yamlData.zone.forEach(zone => { if (zone.density !== undefined) { usages.push({ context: `zone.${zone.body_name}`, type: 'material_density' }); } }); } return usages; } /** * YAML内の放射能使用箇所を検索 * @private */ static findRadioactivityUsagesInYaml(yamlData) { const usages = []; if (yamlData.source) { yamlData.source.forEach(source => { if (source.inventory) { source.inventory.forEach((nuclide, idx) => { usages.push({ context: `source.${source.name}.inventory[${idx}]`, type: 'radioactivity', nuclide: nuclide.nuclide }); }); } }); } return usages; } /** * Unit系の完全性診断レポート * 4キー構造の健全性を包括的に診断 */ static generateIntegrityDiagnosticReport(unitData, yamlData = null) { const report = { timestamp: new Date().toISOString(), unitStructure: null, physicalConsistency: null, systemIntegrity: null, overallHealth: 'unknown', criticalIssues: [], warnings: [], recommendations: [] }; try { // 4キー完全性検証 this.validateFourKeyCompleteness(unitData); report.unitStructure = { status: 'valid', hasAllFourKeys: true, keys: Object.keys(unitData), values: unitData }; // 物理的整合性検証 report.physicalConsistency = this.validatePhysicalConsistency(unitData); // システム整合性検証(YAML提供時) if (yamlData) { report.systemIntegrity = this.validateSystemUnitIntegrity(unitData, yamlData); } // 総合健康度判定 const hasCriticalIssues = report.physicalConsistency.warnings .some(w => w.type === 'mixed_unit_system'); if (hasCriticalIssues) { report.overallHealth = 'warning'; report.warnings.push('Mixed unit systems detected - may affect calculation accuracy'); } else if (report.physicalConsistency.warnings.length > 0) { report.overallHealth = 'minor_warnings'; } else { report.overallHealth = 'excellent'; } // 推奨事項の生成 if (unitData.angle === 'degree') { report.recommendations.push('Consider using radian for angle unit in scientific calculations'); } if (unitData.length !== 'cm' || unitData.density !== 'g/cm3') { report.recommendations.push('Current setup uses mixed unit systems. CGS (cm + g/cm³) is commonly preferred in radiation calculations'); } } catch (error) { report.unitStructure = { status: 'invalid', error: error.message }; report.overallHealth = 'critical'; report.criticalIssues.push(error.message); } return report; } }

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