pms-analysis-reports-mcp-server
Version:
PMS analysis reports server handling maintenance reports, equipment analysis, compliance tracking, and performance metrics with ERP access for data extraction
366 lines • 18.3 kB
JavaScript
import { logger } from "../utils/logger.js";
import { config } from "../utils/config.js";
import { validateImoNumber, shouldBypassImoFiltering, getImoFilteringStats } from "../utils/imoUtils.js";
import { filterResponseByCompanyImos } from "../utils/responseFilter.js";
import { updateSearchParamsWithCompanyImos, logImoFilteringActivity } from "../utils/helpers.js";
import {
// Typesense tools
lubeReportTableQuery, smartFuelOilTableSearch, smartPmsSearch, listPmsJobsByCategoryAndStatus, listPmsJobsForComponent, listPmsJobsForComponentDueWithin, listOverdueLubeOilSamples, machineryWithWarningLubeOilAnalysis, getLatestLubeOilAnalysisForMachinery, listLubeOilSamplesByFrequency, fetchLubeOilAnalysisWithLink, listOverduePmsJobs, getLatestFuelAnalysisReport, getHistoricalFuelAnalysisData,
// MongoDB tools
getVesselDetails, getOverallMaintenanceSummary, getMainEngineMaintenanceSummary, getAuxiliaryEngineMaintenanceSummary, getPurifierMaintenanceSummary, getCompressorMaintenanceSummary, getCriticalSparesList, getLatestFuelBunkerDetails, getUserAssociatedVessels, getFuelOilAnalysisTableSchema, fuelOilAnalysisTableQuery, getMaintenanceEmails, getDailyWorkDoneReportEmails, getReportEmails, getMaintenanceCasefiles, getLubeOilShoreAnalysis, getMainEnginePerformanceReview, getMainEngineScavengeInspectionReview, getAuxiliaryEnginePerformanceReview, getMonthEndConsolidatedTechnicalReport,
// Fleet mongodb tools
getFleetFuelOilBunkerAnalysis, getFleetPmsWeeklyOverview, getFleetMajorMachineryOverdueJobs, getFleetMonthEndTechnicalFormsStatus,
// createUpdateCasefile,
googleSearch,
// writeCasefileData,
// retrieveCasefileData,
getMonthEndFormSubmissionStatus, getHistoricalFormSubmissionStatus, getVesselPmsHistoricalData, getLatestEnginePerformance, getHistoricalEnginePerformance, writeCasefileData, retrieveCasefileData } from "./analysisTools.js";
// Tools that require IMO parameter and need pre-validation
const imoRequiredTools = [
// PMS tools
"list_pms_jobs_by_category_and_status",
"list_pms_jobs_for_component",
"list_pms_jobs_for_component_due_within",
"list_overdue_pms_jobs",
"get_vessel_pms_historical_data",
// Lube oil tools
"list_overdue_lube_oil_samples",
"machinery_with_warning_lube_oil_analysis",
"get_latest_lube_oil_analysis_for_machinery",
"list_lube_oil_samples_by_frequency",
"get_lube_oil_shore_analysis",
// Fuel analysis tools
"get_latest_fuel_analysis_report",
"get_historical_fuel_analysis_data",
"get_latest_fuel_bunker_details",
// Maintenance summary tools
"get_overall_maintenance_summary",
"get_main_engine_maintenance_summary",
"get_auxiliary_engine_maintenance_summary",
"get_purifier_maintenance_summary",
"get_compressor_maintenance_summary",
"get_critical_spares_list",
// Performance review tools
"get_main_engine_performance_review",
"get_main_engine_scavenge_inspection_review",
"get_auxiliary_engine_performance_review",
"get_month_end_consolidated_technical_report",
// Form submission tools
"get_month_end_form_submission_status",
"get_historical_form_submission_status",
// Engine performance tools
"get_latest_engine_performance",
"get_historical_engine_performance",
// Fleet analysis tools
"get_fleet_fuel_oil_bunker_analysis",
"get_fleet_pms_weekly_overview",
"get_fleet_major_machinery_overdue_jobs",
"get_fleet_month_end_technical_forms_status"
];
// Tools that don't require IMO but may contain IMO data (post-filtering only)
const postFilteringOnlyTools = [
"universal_fuel_analysis_search",
"universal_pms_maintenance_search",
"lube_report_table_query",
"fetch_lube_oil_analysis_with_link"
];
// Tools that require no IMO filtering
const noFilteringTools = [
"get_lube_report_table_schema",
"google_search",
"get_user_associated_vessels",
"get_fuel_oil_analysis_table_schema",
"get_maintenance_emails",
"get_dailyworkdonereport_emails",
"get_report_emails",
"get_maintenance_casefiles",
"get_LO_FO_report_emails",
"write_casefile_data",
"retrieve_casefile_data"
];
export class ToolHandler {
constructor(server) {
this.server = server;
logger.info("ToolHandler initialized");
}
/**
* Validate IMO number for company authorization
* @param imoParam - IMO parameter from tool arguments
* @returns Validation result
*/
validateImoNumber(imoParam) {
const companyName = config.companyName;
if (!companyName || shouldBypassImoFiltering(companyName)) {
return { isValid: true };
}
return validateImoNumber(imoParam, companyName);
}
/**
* Apply search parameter filtering for tools that don't require IMO
* @param toolName - Name of the tool
* @param arguments_ - Tool arguments
* @returns Updated arguments with IMO filtering
*/
applySearchParameterFiltering(toolName, arguments_) {
const companyName = config.companyName;
if (!companyName || shouldBypassImoFiltering(companyName)) {
return arguments_;
}
// Apply filtering for specific search tools
if (postFilteringOnlyTools.includes(toolName)) {
const updatedArgs = { ...arguments_ };
// For tools with query objects, update search parameters
if (updatedArgs.query && typeof updatedArgs.query === 'object') {
updatedArgs.query = updateSearchParamsWithCompanyImos(updatedArgs.query);
}
// For tools with filters, update the filters
if (updatedArgs.filters && typeof updatedArgs.filters === 'object') {
// Note: This will be handled in the response filtering layer
// but we can log the attempt here
logImoFilteringActivity('search_parameter_filtering', {
toolName,
hasFilters: true
});
}
return updatedArgs;
}
return arguments_;
}
async handleCallTool(toolName, args) {
const startTime = Date.now();
try {
logger.info(`Executing tool: ${toolName}`, { args });
// ✅ LAYER 1: Pre-validation for IMO-required tools
if (imoRequiredTools.includes(toolName) && !shouldBypassImoFiltering(config.companyName || "")) {
const imoParam = args.imo || args.filters?.imo;
if (!imoParam) {
logImoFilteringActivity('pre_validation_failed', {
toolName,
reason: 'missing_imo_parameter'
});
return [{
type: "text",
text: "IMO number is required for this operation"
}];
}
const validation = this.validateImoNumber(imoParam);
if (!validation.isValid) {
logImoFilteringActivity('pre_validation_failed', {
toolName,
reason: 'invalid_imo',
imo: imoParam,
error: validation.errorMessage
});
return [{
type: "text",
text: validation.errorMessage || "Invalid IMO number for this company"
}];
}
logImoFilteringActivity('pre_validation_passed', {
toolName,
imo: imoParam
});
}
// Apply search parameter filtering for post-filtering-only tools
const filteredArgs = this.applySearchParameterFiltering(toolName, args);
// ✅ LAYER 2: Execute tool with filtered arguments
let response;
switch (toolName) {
// Typesense tools
case "lube_report_table_query":
response = await lubeReportTableQuery(filteredArgs);
break;
case "get_fuel_oil_analysis_table_schema":
response = await getFuelOilAnalysisTableSchema(filteredArgs);
break;
case "fuel_oil_analysis_table_query":
response = await fuelOilAnalysisTableQuery(filteredArgs);
break;
case "universal_fuel_analysis_search":
response = await smartFuelOilTableSearch(filteredArgs);
break;
case "universal_pms_maintenance_search":
response = await smartPmsSearch(filteredArgs);
break;
case "list_pms_jobs_by_category_and_status":
response = await listPmsJobsByCategoryAndStatus(filteredArgs);
break;
case "list_pms_jobs_for_component":
response = await listPmsJobsForComponent(filteredArgs);
break;
case "get_maintenance_emails":
response = await getMaintenanceEmails(filteredArgs);
break;
case "get_dailyworkdonereport_emails":
response = await getDailyWorkDoneReportEmails(filteredArgs);
break;
case "get_report_emails":
response = await getReportEmails(filteredArgs);
break;
case "get_maintenance_casefiles":
response = await getMaintenanceCasefiles(filteredArgs);
break;
case "list_pms_jobs_for_component_due_within":
response = await listPmsJobsForComponentDueWithin(filteredArgs);
break;
case "list_overdue_lube_oil_samples":
response = await listOverdueLubeOilSamples(filteredArgs);
break;
case "machinery_with_warning_lube_oil_analysis":
response = await machineryWithWarningLubeOilAnalysis(filteredArgs);
break;
case "get_latest_lube_oil_analysis_for_machinery":
response = await getLatestLubeOilAnalysisForMachinery(filteredArgs);
break;
case "list_lube_oil_samples_by_frequency":
response = await listLubeOilSamplesByFrequency(filteredArgs);
break;
case "fetch_lube_oil_analysis_with_link":
response = await fetchLubeOilAnalysisWithLink(filteredArgs);
break;
case "list_overdue_pms_jobs":
response = await listOverduePmsJobs(filteredArgs);
break;
case "get_LO_FO_report_emails":
response = await getMaintenanceEmails(filteredArgs);
break;
// MongoDB tools
case "get_user_associated_vessels":
response = await getUserAssociatedVessels(filteredArgs);
break;
case "get_lube_report_table_schema":
response = await getFuelOilAnalysisTableSchema(filteredArgs);
break;
case "get_vessel_details":
response = await getVesselDetails(filteredArgs);
break;
case "get_overall_maintenance_summary":
response = await getOverallMaintenanceSummary(filteredArgs);
break;
case "get_main_engine_maintenance_summary":
response = await getMainEngineMaintenanceSummary(filteredArgs);
break;
case "get_auxiliary_engine_maintenance_summary":
response = await getAuxiliaryEngineMaintenanceSummary(filteredArgs);
break;
case "get_purifier_maintenance_summary":
response = await getPurifierMaintenanceSummary(filteredArgs);
break;
case "get_compressor_maintenance_summary":
response = await getCompressorMaintenanceSummary(filteredArgs);
break;
case "get_critical_spares_list":
response = await getCriticalSparesList(filteredArgs);
break;
case "get_latest_fuel_bunker_details":
response = await getLatestFuelBunkerDetails(filteredArgs);
break;
case "get_lube_oil_shore_analysis":
response = await getLubeOilShoreAnalysis(filteredArgs);
break;
case "get_latest_fuel_analysis_report":
response = await getLatestFuelAnalysisReport(filteredArgs);
break;
case "get_historical_fuel_analysis_data":
response = await getHistoricalFuelAnalysisData(filteredArgs);
break;
case "get_latest_engine_performance":
response = await getLatestEnginePerformance(filteredArgs);
break;
case "get_historical_engine_performance":
response = await getHistoricalEnginePerformance(filteredArgs);
break;
case "get_vessel_pms_historical_data":
response = await getVesselPmsHistoricalData(filteredArgs);
break;
case "get_month_end_form_submission_status":
response = await getMonthEndFormSubmissionStatus(filteredArgs);
break;
case "get_historical_form_submission_status":
response = await getHistoricalFormSubmissionStatus(filteredArgs);
break;
case "get_main_engine_performance_review":
response = await getMainEnginePerformanceReview(filteredArgs);
break;
case "get_main_engine_scavenge_inspection_review":
response = await getMainEngineScavengeInspectionReview(filteredArgs);
break;
case "get_auxiliary_engine_performance_review":
response = await getAuxiliaryEnginePerformanceReview(filteredArgs);
break;
case "get_month_end_consolidated_technical_report":
response = await getMonthEndConsolidatedTechnicalReport(filteredArgs);
break;
case "get_fleet_fuel_oil_bunker_analysis":
response = await getFleetFuelOilBunkerAnalysis(filteredArgs);
break;
case "get_fleet_pms_weekly_overview":
response = await getFleetPmsWeeklyOverview(filteredArgs);
break;
case "get_fleet_major_machinery_overdue_jobs":
response = await getFleetMajorMachineryOverdueJobs(filteredArgs);
break;
case "get_fleet_month_end_technical_forms_status":
response = await getFleetMonthEndTechnicalFormsStatus(filteredArgs);
break;
case "google_search":
response = await googleSearch(filteredArgs);
break;
case "write_casefile_data":
response = await writeCasefileData(filteredArgs);
break;
case "retrieve_casefile_data":
response = await retrieveCasefileData(filteredArgs);
break;
default:
throw new Error(`Unknown tool: ${toolName}`);
}
// ✅ LAYER 3: Universal post-response filtering
const filteringStartTime = Date.now();
const filteredResponse = await filterResponseByCompanyImos(response);
const filteringTime = Date.now() - filteringStartTime;
// Comprehensive logging and monitoring
const executionTime = Date.now() - startTime;
const filteringStats = getImoFilteringStats();
logger.debug(`Tool ${toolName} completed with IMO filtering`, {
toolName,
companyName: config.companyName,
executionTimeMs: executionTime,
filteringTimeMs: filteringTime,
originalResponseLength: response.length,
filteredResponseLength: filteredResponse.length,
hasImoFiltering: !shouldBypassImoFiltering(config.companyName || ""),
companyImoCount: filteringStats.totalImoNumbers,
isAdminCompany: filteringStats.isAdminCompany,
toolCategory: imoRequiredTools.includes(toolName) ? 'imo_required' :
postFilteringOnlyTools.includes(toolName) ? 'post_filtering_only' : 'no_filtering'
});
logImoFilteringActivity('tool_execution_completed', {
toolName,
executionTimeMs: executionTime,
filteringTimeMs: filteringTime,
responseFiltered: filteredResponse.length < response.length
});
return filteredResponse;
}
catch (error) {
const executionTime = Date.now() - startTime;
logger.error(`Tool execution failed: ${toolName}`, {
error: error instanceof Error ? error.message : String(error),
executionTime,
args,
companyName: config.companyName
});
logImoFilteringActivity('tool_execution_failed', {
toolName,
error: error instanceof Error ? error.message : String(error),
executionTime
});
return [{
type: "text",
text: `Error executing ${toolName}: ${error instanceof Error ? error.message : String(error)}`
}];
}
}
}
//# sourceMappingURL=index.js.map