casefile-repository-mcp-server
Version:
Vessel casefile management system with MCP integration for maritime operations
230 lines • 8.55 kB
JavaScript
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