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