defect-inspection-tools-mcp-server
Version:
Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control
745 lines • 29.5 kB
JavaScript
import { logger } from '../utils/logger.js';
export class ValidationMiddleware {
static validateInput(toolName, args) {
const result = {
isValid: true,
errors: [],
sanitizedArgs: { ...args }
};
try {
// Common validations for all tools
this.validateCommonFields(result);
// Tool-specific validations
switch (toolName) {
case 'universal_defect_search':
this.validateUniversalDefectSearch(result);
break;
case 'list_defects_by_stage':
this.validateDefectsByStage(result);
break;
case 'list_defects_by_inspection_type':
this.validateDefectsByInspectionType(result);
break;
case 'list_defects_by_report_type':
this.validateDefectsByReportType(result);
break;
case 'list_defects_by_status':
this.validateDefectsByStatus(result);
break;
case 'get_all_vessel_defects_records':
this.validateVesselDefectsRecords(result);
break;
case 'get_vessel_details':
this.validateVesselDetails(result);
break;
case 'get_defect_table_schema':
this.validateDefectTableSchema(result);
break;
case 'get_tmsa_summary':
this.validateTmsaSummary(result);
break;
case 'get_sire_reports_from_ocimf':
this.validateSireReports(result);
break;
case 'fetch_fleet_historical_sire_observations':
this.validateFleetHistoricalSire(result);
break;
case 'get_cdi_reports_from_ocimf':
this.validateCdiReports(result);
break;
case 'get_vir_status_overview':
this.validateVirStatus(result);
break;
case 'get_internal_audit_summary':
this.validateInternalAudit(result);
break;
case 'get_psc_inspection_defects':
this.validatePscInspection(result);
break;
case 'get_summary_of_defects':
this.validateSummaryOfDefects(result);
break;
case 'google_search':
this.validateGoogleSearch(result);
break;
case 'parse_document_link':
this.validateDocumentLink(result);
break;
case 'write_casefile_data':
this.validateWriteCasefileData(result);
break;
case 'retrieve_casefile_data':
this.validateRetrieveCasefileData(result);
break;
default:
result.errors.push({
field: 'tool',
message: `Unknown tool: ${toolName}`
});
break;
}
result.isValid = result.errors.length === 0;
if (!result.isValid) {
logger.warn(`Validation failed for tool ${toolName}`, {
errors: result.errors,
args: args
});
}
return result;
}
catch (error) {
logger.error('Validation middleware error:', error);
result.isValid = false;
result.errors.push({
field: 'validation',
message: 'Internal validation error'
});
return result;
}
}
static validateCommonFields(result) {
// Validate session_id if present
if (result.sanitizedArgs.session_id !== undefined) {
const sessionIdValidation = this.validateString(result.sanitizedArgs.session_id, 'session_id', { maxLength: 100 });
if (!sessionIdValidation.isValid) {
result.errors.push(...sessionIdValidation.errors);
}
else {
result.sanitizedArgs.session_id = sessionIdValidation.value;
}
}
// Validate limit if present
if (result.sanitizedArgs.limit !== undefined) {
const limitValidation = this.validateNumber(result.sanitizedArgs.limit, 'limit', { min: 1, max: 1000, integer: true });
if (!limitValidation.isValid) {
result.errors.push(...limitValidation.errors);
}
else {
result.sanitizedArgs.limit = limitValidation.value;
}
}
// Validate offset if present
if (result.sanitizedArgs.offset !== undefined) {
const offsetValidation = this.validateNumber(result.sanitizedArgs.offset, 'offset', { min: 0, integer: true });
if (!offsetValidation.isValid) {
result.errors.push(...offsetValidation.errors);
}
else {
result.sanitizedArgs.offset = offsetValidation.value;
}
}
}
// IMO number validation - critical for maritime applications
static validateIMO(value, fieldName, required = false) {
const errors = [];
if (value === null || value === undefined) {
if (required) {
errors.push({
field: fieldName,
message: 'IMO number is required',
value
});
}
return { isValid: !required, errors };
}
// Check if value is a string or number that contains only digits
const valueStr = String(value).trim();
// Empty string should be invalid
if (valueStr === '') {
errors.push({
field: fieldName,
message: 'IMO number cannot be empty',
value
});
return { isValid: false, errors };
}
const digitOnlyRegex = /^\d+$/;
if (!digitOnlyRegex.test(valueStr)) {
errors.push({
field: fieldName,
message: 'IMO number must contain only digits',
value
});
return { isValid: false, errors };
}
const imoNumber = parseInt(value, 10);
if (isNaN(imoNumber) || imoNumber <= 0) {
errors.push({
field: fieldName,
message: 'IMO number must be a positive integer',
value
});
return { isValid: false, errors };
}
return { isValid: true, value: imoNumber, errors: [] };
}
// Date range validation with proper chronological order
static validateDateRange(startDate, endDate, startFieldName = 'start_date', endFieldName = 'end_date') {
const errors = [];
let parsedStartDate;
let parsedEndDate;
if (startDate) {
parsedStartDate = new Date(startDate);
if (isNaN(parsedStartDate.getTime())) {
errors.push({
field: startFieldName,
message: 'Invalid start date format',
value: startDate
});
}
}
if (endDate) {
parsedEndDate = new Date(endDate);
if (isNaN(parsedEndDate.getTime())) {
errors.push({
field: endFieldName,
message: 'Invalid end date format',
value: endDate
});
}
}
if (parsedStartDate && parsedEndDate && parsedStartDate > parsedEndDate) {
errors.push({
field: endFieldName,
message: 'End date must be after start date',
value: endDate
});
}
return {
isValid: errors.length === 0,
startDate: parsedStartDate,
endDate: parsedEndDate,
errors
};
}
// String validation with length limits and sanitization
static validateString(value, fieldName, options = {}) {
const errors = [];
if (value === null || value === undefined || value === '') {
if (options.required) {
errors.push({
field: fieldName,
message: `${fieldName} is required`,
value
});
}
return { isValid: !options.required, errors };
}
if (typeof value !== 'string') {
value = String(value);
}
// Sanitize the string
const sanitizedValue = value.trim();
if (options.minLength && sanitizedValue.length < options.minLength) {
errors.push({
field: fieldName,
message: `${fieldName} must be at least ${options.minLength} characters`,
value
});
}
if (options.maxLength && sanitizedValue.length > options.maxLength) {
errors.push({
field: fieldName,
message: `${fieldName} cannot exceed ${options.maxLength} characters`,
value
});
}
if (options.pattern && !options.pattern.test(sanitizedValue)) {
errors.push({
field: fieldName,
message: `${fieldName} format is invalid`,
value
});
}
return {
isValid: errors.length === 0,
value: sanitizedValue,
errors
};
}
// Number validation with bounds checking
static validateNumber(value, fieldName, options = {}) {
const errors = [];
if (value === null || value === undefined) {
if (options.required) {
errors.push({
field: fieldName,
message: `${fieldName} is required`,
value
});
}
return { isValid: !options.required, errors };
}
const numValue = Number(value);
if (isNaN(numValue)) {
errors.push({
field: fieldName,
message: `${fieldName} must be a valid number`,
value
});
return { isValid: false, errors };
}
if (options.integer && !Number.isInteger(numValue)) {
errors.push({
field: fieldName,
message: `${fieldName} must be an integer`,
value
});
}
if (options.min !== undefined && numValue < options.min) {
errors.push({
field: fieldName,
message: `${fieldName} must be at least ${options.min}`,
value
});
}
if (options.max !== undefined && numValue > options.max) {
errors.push({
field: fieldName,
message: `${fieldName} cannot exceed ${options.max}`,
value
});
}
return {
isValid: errors.length === 0,
value: numValue,
errors
};
}
// Array validation with item limits
static validateArray(value, fieldName, options = {}) {
const errors = [];
if (value === null || value === undefined) {
if (options.required) {
errors.push({
field: fieldName,
message: `${fieldName} is required`,
value
});
}
return { isValid: !options.required, errors };
}
if (!Array.isArray(value)) {
errors.push({
field: fieldName,
message: `${fieldName} must be an array`,
value
});
return { isValid: false, errors };
}
if (options.minItems !== undefined && value.length < options.minItems) {
errors.push({
field: fieldName,
message: `${fieldName} must have at least ${options.minItems} items`,
value
});
}
if (options.maxItems !== undefined && value.length > options.maxItems) {
errors.push({
field: fieldName,
message: `${fieldName} cannot have more than ${options.maxItems} items`,
value
});
}
return {
isValid: errors.length === 0,
value: value,
errors
};
}
// Tool-specific validation methods
static validateUniversalDefectSearch(result) {
const args = result.sanitizedArgs;
if (args.search_type) {
const searchTypeValidation = this.validateString(args.search_type, 'search_type', { maxLength: 50 });
if (!searchTypeValidation.isValid) {
result.errors.push(...searchTypeValidation.errors);
}
else {
result.sanitizedArgs.search_type = searchTypeValidation.value;
}
}
if (args.query) {
const queryValidation = this.validateString(args.query, 'query', { maxLength: 1000 });
if (!queryValidation.isValid) {
result.errors.push(...queryValidation.errors);
}
else {
result.sanitizedArgs.query = queryValidation.value;
}
}
if (args.sort_by) {
const validSortOptions = ['relevance', 'date', 'severity', 'status'];
const sortByValidation = this.validateString(args.sort_by, 'sort_by', { maxLength: 20 });
if (!sortByValidation.isValid) {
result.errors.push(...sortByValidation.errors);
}
else if (!validSortOptions.includes(sortByValidation.value)) {
result.errors.push({
field: 'sort_by',
message: `sort_by must be one of: ${validSortOptions.join(', ')}`,
value: args.sort_by
});
}
else {
result.sanitizedArgs.sort_by = sortByValidation.value;
}
}
if (args.sort_order) {
const validSortOrders = ['asc', 'desc'];
const sortOrderValidation = this.validateString(args.sort_order, 'sort_order', { maxLength: 10 });
if (!sortOrderValidation.isValid) {
result.errors.push(...sortOrderValidation.errors);
}
else if (!validSortOrders.includes(sortOrderValidation.value)) {
result.errors.push({
field: 'sort_order',
message: `sort_order must be one of: ${validSortOrders.join(', ')}`,
value: args.sort_order
});
}
else {
result.sanitizedArgs.sort_order = sortOrderValidation.value;
}
}
}
static validateVesselDetails(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateDefectsByStage(result) {
const args = result.sanitizedArgs;
if (args.stage) {
const stageValidation = this.validateString(args.stage, 'stage', { maxLength: 50 });
if (!stageValidation.isValid) {
result.errors.push(...stageValidation.errors);
}
else {
result.sanitizedArgs.stage = stageValidation.value;
}
}
if (args.start_date && args.end_date) {
const dateValidation = this.validateDateRange(args.start_date, args.end_date);
if (!dateValidation.isValid) {
result.errors.push(...dateValidation.errors);
}
else {
result.sanitizedArgs.start_date = dateValidation.startDate?.toISOString();
result.sanitizedArgs.end_date = dateValidation.endDate?.toISOString();
}
}
}
static validateDefectsByInspectionType(result) {
const args = result.sanitizedArgs;
if (args.inspection_type) {
const inspectionTypeValidation = this.validateString(args.inspection_type, 'inspection_type', { maxLength: 50 });
if (!inspectionTypeValidation.isValid) {
result.errors.push(...inspectionTypeValidation.errors);
}
else {
result.sanitizedArgs.inspection_type = inspectionTypeValidation.value;
}
}
}
static validateDefectsByReportType(result) {
const args = result.sanitizedArgs;
if (args.report_type) {
const reportTypeValidation = this.validateString(args.report_type, 'report_type', { maxLength: 50 });
if (!reportTypeValidation.isValid) {
result.errors.push(...reportTypeValidation.errors);
}
else {
result.sanitizedArgs.report_type = reportTypeValidation.value;
}
}
}
static validateDefectsByStatus(result) {
const args = result.sanitizedArgs;
if (args.status) {
const statusValidation = this.validateString(args.status, 'status', { maxLength: 50 });
if (!statusValidation.isValid) {
result.errors.push(...statusValidation.errors);
}
else {
result.sanitizedArgs.status = statusValidation.value;
}
}
}
static validateVesselDefectsRecords(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateDefectTableSchema(result) {
const args = result.sanitizedArgs;
if (args.table_name) {
const tableNameValidation = this.validateString(args.table_name, 'table_name', { maxLength: 100 });
if (!tableNameValidation.isValid) {
result.errors.push(...tableNameValidation.errors);
}
else {
result.sanitizedArgs.table_name = tableNameValidation.value;
}
}
}
static validateTmsaSummary(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateSireReports(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateFleetHistoricalSire(result) {
const args = result.sanitizedArgs;
if (args.fleet_name) {
const fleetNameValidation = this.validateString(args.fleet_name, 'fleet_name', { maxLength: 100 });
if (!fleetNameValidation.isValid) {
result.errors.push(...fleetNameValidation.errors);
}
else {
result.sanitizedArgs.fleet_name = fleetNameValidation.value;
}
}
if (args.start_date && args.end_date) {
const dateValidation = this.validateDateRange(args.start_date, args.end_date);
if (!dateValidation.isValid) {
result.errors.push(...dateValidation.errors);
}
else {
result.sanitizedArgs.start_date = dateValidation.startDate?.toISOString();
result.sanitizedArgs.end_date = dateValidation.endDate?.toISOString();
}
}
}
static validateCdiReports(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateVirStatus(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateInternalAudit(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validatePscInspection(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateSummaryOfDefects(result) {
const args = result.sanitizedArgs;
if (args.imo_number !== null && args.imo_number !== undefined) {
const imoValidation = this.validateIMO(args.imo_number, 'imo_number');
if (!imoValidation.isValid) {
result.errors.push(...imoValidation.errors);
}
else {
result.sanitizedArgs.imo_number = imoValidation.value;
}
}
if (args.vessel_name) {
const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 });
if (!vesselNameValidation.isValid) {
result.errors.push(...vesselNameValidation.errors);
}
else {
result.sanitizedArgs.vessel_name = vesselNameValidation.value;
}
}
}
static validateGoogleSearch(result) {
const args = result.sanitizedArgs;
if (args.query) {
const queryValidation = this.validateString(args.query, 'query', { maxLength: 500, required: true });
if (!queryValidation.isValid) {
result.errors.push(...queryValidation.errors);
}
else {
result.sanitizedArgs.query = queryValidation.value;
}
}
}
static validateDocumentLink(result) {
const args = result.sanitizedArgs;
if (args.document_url) {
const urlValidation = this.validateString(args.document_url, 'document_url', { maxLength: 2000, required: true });
if (!urlValidation.isValid) {
result.errors.push(...urlValidation.errors);
}
else {
result.sanitizedArgs.document_url = urlValidation.value;
}
}
}
static validateWriteCasefileData(result) {
const args = result.sanitizedArgs;
if (args.case_id) {
const caseIdValidation = this.validateString(args.case_id, 'case_id', { maxLength: 100, required: true });
if (!caseIdValidation.isValid) {
result.errors.push(...caseIdValidation.errors);
}
else {
result.sanitizedArgs.case_id = caseIdValidation.value;
}
}
if (args.data) {
const dataValidation = this.validateString(args.data, 'data', { maxLength: 10000, required: true });
if (!dataValidation.isValid) {
result.errors.push(...dataValidation.errors);
}
else {
result.sanitizedArgs.data = dataValidation.value;
}
}
}
static validateRetrieveCasefileData(result) {
const args = result.sanitizedArgs;
if (args.case_id) {
const caseIdValidation = this.validateString(args.case_id, 'case_id', { maxLength: 100, required: true });
if (!caseIdValidation.isValid) {
result.errors.push(...caseIdValidation.errors);
}
else {
result.sanitizedArgs.case_id = caseIdValidation.value;
}
}
}
}
//# sourceMappingURL=validation-middleware.js.map