UNPKG

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

328 lines 17.6 kB
import { getTypesenseClient, updateTypesenseFilterWithCompanyImos, processTypesenseResults } from "syia-mcp-utils"; import { logger } from "../../index.js"; // Universal Tools Handler Class export class UniversalToolsHandler { constructor() { this.typesenseClient = getTypesenseClient(); } async universalLubeOilAnalysisSearchHandler(arguments_) { const collection = "lube_oil_reports"; const session_id = arguments_.session_id || "testing"; // Extract new schema parameters const args = arguments_; const query_by = args.query_by; const query_text = (args.q || "").trim() || "*"; // Validate that query_by is provided when q is specified if (args.q && args.q.trim() !== "*" && !query_by) { throw new Error("query_by parameter is required when using full-text search with q parameter"); } const filter_by = args.filter_by || ""; const sort_by = args.sort_by || "relevance"; const facet_by = args.facet_by || ""; const max_facet_values = args.max_facet_values || 10; const page = args.page || 1; const per_page = args.per_page || 50; try { let filterBy = filter_by; // Convert date fields from yyyy-mm-dd to Unix timestamps in filter_by if (filterBy) { const dateFields = ["sampleDate", "nextDue"]; for (const dateField of dateFields) { // Handle date range operations (>=, <=, >, <, =) const dateRegexPatterns = [ { pattern: new RegExp(`${dateField}:>=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>=' }, { pattern: new RegExp(`${dateField}:<=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<=' }, { pattern: new RegExp(`${dateField}:>(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>' }, { pattern: new RegExp(`${dateField}:<(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<' }, { pattern: new RegExp(`${dateField}:(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':' } ]; for (const { pattern, operator } of dateRegexPatterns) { filterBy = filterBy.replace(pattern, (match, dateStr) => { try { const timestamp = Math.floor(new Date(dateStr + 'T00:00:00.000Z').getTime() / 1000); return `${dateField}${operator}${timestamp}`; } catch (error) { logger.warn(`Failed to convert date ${dateStr} for field ${dateField}:`, error); return match; // Keep original if conversion fails } }); } } } // Apply company IMO restrictions filterBy = await updateTypesenseFilterWithCompanyImos(filterBy || ""); // Set up all available fields for inclusion (based on schema) const includeFields = "imo,vesselName,machineryName,reportStatus,sampleDate,nextDue,frequency,dueStatus,testLab,report"; const excludeFields = "embedding"; const query = { q: query_text, query_by: query_by, include_fields: includeFields, exclude_fields: excludeFields, page: page, per_page: per_page }; if (filterBy) { query.filter_by = filterBy; } // Handle sorting if (sort_by && sort_by !== "relevance") { query.sort_by = sort_by; } // Handle faceting if (facet_by) { query.facet_by = facet_by; query.max_facet_values = max_facet_values; } logger.debug(`[Typesense Query] ${JSON.stringify(query)}`); const results = await this.typesenseClient.collections(collection).documents().search(query); // Debug: Log the structure of the search results logger.info(`UniversalLubeOilSearchHandler - Results structure: ${JSON.stringify(Object.keys(results))}`); if (results.hits && results.hits.length > 0) { const sampleHit = results.hits[0]; logger.info(`UniversalLubeOilSearchHandler - Sample hit structure: ${JSON.stringify(Object.keys(sampleHit))}`); } if (!results || !results.hits || results.hits.length === 0) { return [{ type: "text", text: `No lube oil records found for query '${query_text}' with query_by fields '${query_by}'.`, title: "No Results Found", format: "json" }]; } // Format results using the utility function let title = `Universal Lube Oil Search Results for '${query_text}'`; if (facet_by) { title += ` (Faceted by: ${facet_by})`; } const linkHeader = `Universal lube oil search result for query: '${query_text}' in fields: ${query_by}`; return await processTypesenseResults(results, "universal_lube_oil_search", title, session_id, linkHeader); } catch (error) { logger.error('Error performing universal lube oil search:', error); return [{ type: "text", text: `Error performing search: ${error.message}`, title: "Error", format: "json" }]; } } async universalFuelAnalysisSearchHandler(arguments_) { const collection = "fuel_oil_data"; const session_id = arguments_.session_id || "testing"; // Extract new schema parameters const args = arguments_; const query_by = args.query_by; const query_text = (args.q || "").trim() || "*"; // Validate that query_by is provided when q is specified if (args.q && args.q.trim() !== "*" && !query_by) { throw new Error("query_by parameter is required when using full-text search with q parameter"); } const filter_by = args.filter_by || ""; const sort_by = args.sort_by || "relevance"; const facet_by = args.facet_by || ""; const max_facet_values = args.max_facet_values || 10; const page = args.page || 1; const per_page = args.per_page || 50; try { let filterBy = filter_by; // Convert date fields from yyyy-mm-dd to Unix timestamps in filter_by if (filterBy) { const dateFields = ["bunkerDate"]; for (const dateField of dateFields) { // Handle date range operations (>=, <=, >, <, =) const dateRegexPatterns = [ { pattern: new RegExp(`${dateField}:>=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>=' }, { pattern: new RegExp(`${dateField}:<=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<=' }, { pattern: new RegExp(`${dateField}:>(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>' }, { pattern: new RegExp(`${dateField}:<(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<' }, { pattern: new RegExp(`${dateField}:(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':' } ]; for (const { pattern, operator } of dateRegexPatterns) { filterBy = filterBy.replace(pattern, (match, dateStr) => { try { const timestamp = Math.floor(new Date(dateStr + 'T00:00:00.000Z').getTime() / 1000); return `${dateField}${operator}${timestamp}`; } catch (error) { logger.warn(`Failed to convert date ${dateStr} for field ${dateField}:`, error); return match; // Keep original if conversion fails } }); } } } // Apply company IMO restrictions filterBy = await updateTypesenseFilterWithCompanyImos(filterBy || ""); // Set up all available fields for inclusion (based on schema) const includeFields = "imo,vesselName,sampleId,bunkerDate,bunkerPort,fuelType,supplier,testLab,rating,sulfur," + "density@15°C,viscosity50C,viscosity100C,flashPoint,pourPoint,water,ash,microCarbonResidue,netSpecificEnergy," + "vanadium,aluminium,silicon,iron,nickel,calcium,sodium,magnesium,zinc,lead,phosphorus,potassium," + "calculatedCarbonAromaticityIndex,acidNumber,totalSedimentPotential,samplingMethod,samplingPoint," + "receivedDate,sentDate,storageTemperature,separationTemperature,bdnDensity@15°C,bdnSulfur," + "bdnViscosity,bdnWater,bdnReceiptNumber,barge,specificGravity,hydrogenSulphide"; const excludeFields = "embedding"; const query = { q: query_text, query_by: query_by, include_fields: includeFields, exclude_fields: excludeFields, page: page, per_page: per_page }; if (filterBy) { query.filter_by = filterBy; } // Handle sorting if (sort_by && sort_by !== "relevance") { query.sort_by = sort_by; } // Handle faceting if (facet_by) { query.facet_by = facet_by; query.max_facet_values = max_facet_values; } logger.debug(`[Typesense Query] ${JSON.stringify(query)}`); const results = await this.typesenseClient.collections(collection).documents().search(query); // Debug: Log the structure of the search results logger.info(`UniversalFuelAnalysisSearchHandler - Results structure: ${JSON.stringify(Object.keys(results))}`); if (results.hits && results.hits.length > 0) { const sampleHit = results.hits[0]; logger.info(`UniversalFuelAnalysisSearchHandler - Sample hit structure: ${JSON.stringify(Object.keys(sampleHit))}`); } if (!results || !results.hits || results.hits.length === 0) { return [{ type: "text", text: `No fuel analysis records found for query '${query_text}' with query_by fields '${query_by}'.`, title: "No Results Found", format: "json" }]; } // Format results using the utility function let title = `Universal Fuel Analysis Search Results for '${query_text}'`; if (facet_by) { title += ` (Faceted by: ${facet_by})`; } const linkHeader = `Universal fuel analysis search result for query: '${query_text}' in fields: ${query_by}`; return await processTypesenseResults(results, "universal_fuel_analysis_search", title, session_id, linkHeader); } catch (error) { logger.error('Error performing universal fuel analysis search:', error); return [{ type: "text", text: `Error performing search: ${error.message}`, title: "Error", format: "json" }]; } } async universalPmsMaintenanceSearchHandler(arguments_) { const collection = "pms"; const session_id = arguments_.session_id || "testing"; // Extract new schema parameters const args = arguments_; const query_by = args.query_by; const query_text = (args.q || "").trim() || "*"; // Validate that query_by is provided when q is specified if (args.q && args.q.trim() !== "*" && !query_by) { throw new Error("query_by parameter is required when using full-text search with q parameter"); } const filter_by = args.filter_by || ""; const sort_by = args.sort_by || "relevance"; const facet_by = args.facet_by || ""; const max_facet_values = args.max_facet_values || 10; const page = args.page || 1; const per_page = args.per_page || 50; try { let filterBy = filter_by; // Convert date fields from yyyy-mm-dd to Unix timestamps in filter_by if (filterBy) { const dateFields = ["jobDueDate", "jobDoneDate", "defermentRequestedDate"]; for (const dateField of dateFields) { // Handle date range operations (>=, <=, >, <, =) const dateRegexPatterns = [ { pattern: new RegExp(`${dateField}:>=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>=' }, { pattern: new RegExp(`${dateField}:<=(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<=' }, { pattern: new RegExp(`${dateField}:>(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':>' }, { pattern: new RegExp(`${dateField}:<(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':<' }, { pattern: new RegExp(`${dateField}:(\\d{4}-\\d{2}-\\d{2})`, 'g'), operator: ':' } ]; for (const { pattern, operator } of dateRegexPatterns) { filterBy = filterBy.replace(pattern, (match, dateStr) => { try { const timestamp = Math.floor(new Date(dateStr + 'T00:00:00.000Z').getTime() / 1000); return `${dateField}${operator}${timestamp}`; } catch (error) { logger.warn(`Failed to convert date ${dateStr} for field ${dateField}:`, error); return match; // Keep original if conversion fails } }); } } } // Apply company IMO restrictions filterBy = await updateTypesenseFilterWithCompanyImos(filterBy || ""); // Set up all available fields for inclusion (based on schema) const includeFields = "imo,vesselName,jobTitle,jobCategory,intervalType,jobStatus,type,component," + "remainingHrsNextOverhaul,remainingDaysNextOverhaul,jobDueDate,jobDoneDate," + "defermentApplied,defermentRequestedDate,defermentStatus"; const excludeFields = "embedding"; const query = { q: query_text, query_by: query_by, include_fields: includeFields, exclude_fields: excludeFields, page: page, per_page: per_page }; if (filterBy) { query.filter_by = filterBy; } // Handle sorting if (sort_by && sort_by !== "relevance") { query.sort_by = sort_by; } // Handle faceting if (facet_by) { query.facet_by = facet_by; query.max_facet_values = max_facet_values; } logger.debug(`[Typesense Query] ${JSON.stringify(query)}`); const results = await this.typesenseClient.collections(collection).documents().search(query); // Debug: Log the structure of the search results logger.info(`UniversalPmsMaintenanceSearchHandler - Results structure: ${JSON.stringify(Object.keys(results))}`); if (results.hits && results.hits.length > 0) { const sampleHit = results.hits[0]; logger.info(`UniversalPmsMaintenanceSearchHandler - Sample hit structure: ${JSON.stringify(Object.keys(sampleHit))}`); } if (!results || !results.hits || results.hits.length === 0) { return [{ type: "text", text: `No PMS maintenance records found for query '${query_text}' with query_by fields '${query_by}'.`, title: "No Results Found", format: "json" }]; } // Format results using the utility function let title = `Universal PMS Maintenance Search Results for '${query_text}'`; if (facet_by) { title += ` (Faceted by: ${facet_by})`; } const linkHeader = `Universal PMS maintenance search result for query: '${query_text}' in fields: ${query_by}`; return await processTypesenseResults(results, "universal_pms_maintenance_search", title, session_id, linkHeader); } catch (error) { logger.error('Error performing universal PMS maintenance search:', error); return [{ type: "text", text: `Error performing search: ${error.message}`, title: "Error", format: "json" }]; } } } //# sourceMappingURL=universalTools.js.map