UNPKG

casefile-repository-mcp-server

Version:

Vessel casefile management system with MCP integration for maritime operations

230 lines 8.55 kB
import { isValidImoForCompany, shouldBypassImoFiltering, getCompanyImoNumbers } from './imoUtils.js'; import { config } from './config.js'; import { logger } from './logger.js'; // IMO field names to scan for in documents const IMO_FIELD_NAMES = [ 'imo', 'vesselImo', 'imoNumber', 'IMO', 'vessel_imo', 'imo_number', 'vesselIMO' ]; /** * Find IMO field in an object * @param obj - Object to search for IMO fields * @returns Found IMO field info or null */ function findImoField(obj) { if (!obj || typeof obj !== 'object') { return null; } for (const fieldName of IMO_FIELD_NAMES) { if (obj.hasOwnProperty(fieldName) && obj[fieldName] !== null && obj[fieldName] !== undefined) { return { fieldName, value: obj[fieldName] }; } } return null; } /** * Check if an object contains unauthorized IMO numbers (recursive) * @param obj - Object to check * @param path - Current path in the object (for logging) * @returns Result indicating if unauthorized IMO was found */ function checkForUnauthorizedImo(obj, path = 'root') { if (!obj || typeof obj !== 'object') { return { found: false, location: '' }; } // Check arrays if (Array.isArray(obj)) { for (let i = 0; i < obj.length; i++) { const result = checkForUnauthorizedImo(obj[i], `${path}[${i}]`); if (result.found) { return result; } } return { found: false, location: '' }; } // Check direct IMO fields const imoField = findImoField(obj); if (imoField && !isValidImoForCompany(imoField.value)) { return { found: true, location: `${path}.${imoField.fieldName}`, imoValue: imoField.value }; } // Recursively check nested objects for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (typeof value === 'object' && value !== null) { const result = checkForUnauthorizedImo(value, `${path}.${key}`); if (result.found) { return result; } } } } return { found: false, location: '' }; } /** * Filter array items by removing those with unauthorized IMO numbers * @param array - Array to filter * @returns Filtered array and statistics */ function filterArrayByImo(array) { const startTime = Date.now(); const stats = { totalItems: array.length, filteredItems: 0, unauthorizedImos: [], processingTimeMs: 0 }; const filtered = array.filter(item => { const result = checkForUnauthorizedImo(item); if (result.found) { stats.filteredItems++; stats.unauthorizedImos.push(`${result.location}: ${result.imoValue}`); return false; } return true; }); stats.processingTimeMs = Date.now() - startTime; return { filtered, stats }; } /** * Filter response content recursively * @param content - Content to filter * @returns Filtered content and statistics */ function filterResponseContent(content) { const startTime = Date.now(); const stats = { totalItems: 0, filteredItems: 0, unauthorizedImos: [], processingTimeMs: 0 }; if (Array.isArray(content)) { const result = filterArrayByImo(content); stats.totalItems = result.stats.totalItems; stats.filteredItems = result.stats.filteredItems; stats.unauthorizedImos = result.stats.unauthorizedImos; stats.processingTimeMs = Date.now() - startTime; return { filtered: result.filtered, stats }; } if (typeof content === 'object' && content !== null) { const filtered = { ...content }; // Special handling for common response structures if (content.hits && Array.isArray(content.hits)) { const originalHitsCount = content.hits.length; const result = filterArrayByImo(content.hits); filtered.hits = result.filtered; filtered.found = result.filtered.length; // Update found count stats.totalItems = result.stats.totalItems; stats.filteredItems = result.stats.filteredItems; stats.unauthorizedImos = result.stats.unauthorizedImos; // If all hits were filtered out, return informative message if (originalHitsCount > 0 && result.filtered.length === 0 && result.stats.filteredItems > 0) { const companyName = config.companyName || 'current company'; const unauthorizedImos = [...new Set(result.stats.unauthorizedImos.map(imo => imo.split(': ')[1]))]; let errorMessage; if (unauthorizedImos.length === 1) { errorMessage = `Vessel with this IMO ${unauthorizedImos[0]} is not part of ${companyName}`; } else { errorMessage = `Vessels with IMO ${unauthorizedImos.join(', ')} are not part of ${companyName}`; } stats.processingTimeMs = Date.now() - startTime; return { filtered: { found: 0, hits: [], message: errorMessage }, stats }; } } else { // Recursively filter other object properties Object.keys(content).forEach(key => { const result = filterResponseContent(content[key]); filtered[key] = result.filtered; stats.totalItems += result.stats.totalItems; stats.filteredItems += result.stats.filteredItems; stats.unauthorizedImos.push(...result.stats.unauthorizedImos); }); } stats.processingTimeMs = Date.now() - startTime; return { filtered, stats }; } stats.processingTimeMs = Date.now() - startTime; return { filtered: content, stats }; } /** * Main function to filter tool responses by company IMO numbers * @param response - Tool response to filter * @returns Filtered response */ export async function filterResponseByCompanyImos(response) { const companyName = config.companyName || ''; // Skip filtering for admin companies if (shouldBypassImoFiltering(companyName)) { logger.debug(`Bypassing IMO filtering for admin company: ${companyName}`); return response; } // Skip filtering if no company IMO numbers are available const companyImos = getCompanyImoNumbers(); if (companyImos.length === 0) { logger.debug('No company IMO numbers available, skipping filtering'); return response; } const filteredResponse = []; let totalStats = { totalItems: 0, filteredItems: 0, unauthorizedImos: [], processingTimeMs: 0 }; for (const item of response) { if (item.type === 'text') { try { const parsedContent = JSON.parse(item.text); const result = filterResponseContent(parsedContent); // Accumulate statistics totalStats.totalItems += result.stats.totalItems; totalStats.filteredItems += result.stats.filteredItems; totalStats.unauthorizedImos.push(...result.stats.unauthorizedImos); totalStats.processingTimeMs += result.stats.processingTimeMs; filteredResponse.push({ ...item, text: JSON.stringify(result.filtered, null, 2) }); } catch (parseError) { // Not JSON content, pass through unchanged filteredResponse.push(item); } } else { // Non-text content, pass through unchanged filteredResponse.push(item); } } // Log filtering results if (totalStats.filteredItems > 0) { logger.info(`IMO filtering applied: ${totalStats.filteredItems} items filtered out of ${totalStats.totalItems}`, { companyName, filteredItems: totalStats.filteredItems, totalItems: totalStats.totalItems, unauthorizedImos: totalStats.unauthorizedImos, processingTimeMs: totalStats.processingTimeMs }); } return filteredResponse; } //# sourceMappingURL=responseFilter.js.map