UNPKG

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
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