voyage-and-consumption-mcp-server
Version:
Voyage and consumption management server handling vessel voyages, fuel consumption, performance monitoring, and operational data with ERP access for data extraction
221 lines • 7.52 kB
JavaScript
/**
* Response Formatter Utility
*
* Centralizes response formatting logic that was duplicated across
* 76+ response patterns in the codebase.
*
* This utility extracts common patterns:
* - IMO-based title generation (20+ instances)
* - JSON response formatting with type/format structure
* - Dual response patterns (main + artifact data)
* - Error response formatting (40+ instances)
* - Consistent response object structures
*/
import { logger } from './logger.js';
// Error classes (re-exported for convenience)
export class MissingParameterError extends Error {
constructor(param, tool_name) {
super(`Missing required parameter '${param}' for tool '${tool_name}'`);
this.param = param;
this.tool_name = tool_name;
}
}
/**
* Creates a formatted JSON response with consistent structure
* @param data - The data to serialize as JSON
* @param title - Optional title for the response
* @returns Formatted response object
*/
export function formatJsonResponse(data, title) {
const response = {
type: "text",
text: JSON.stringify(data, null, 2),
format: "json"
};
if (title) {
response.title = title;
}
return response;
}
/**
* Creates a simple text response
* @param text - The text content
* @param title - Optional title for the response
* @returns Formatted response object
*/
export function formatTextResponse(text, title) {
const response = {
type: "text",
text: text
};
if (title) {
response.title = title;
}
return response;
}
/**
* Generates IMO-based titles with consistent formatting
* @param action - The action description (e.g., "Fuel consumption data", "Live position and ETA")
* @param imo - The IMO number (string or number)
* @returns Formatted title string
*/
export function generateImoTitle(action, imo) {
return `${action} for vessel with IMO ${imo}`;
}
/**
* Generates query-based titles for search operations
* @param action - The action description (e.g., "Smart voyage search results")
* @param query - The search query
* @returns Formatted title string
*/
export function generateQueryTitle(action, query) {
return `${action} for '${query}'`;
}
/**
* Creates a standard vessel data response with IMO-based title
* @param data - The vessel data to return
* @param action - The action description for the title
* @param imo - The IMO number
* @returns Formatted response array
*/
export function createVesselDataResponse(data, action, imo) {
const title = generateImoTitle(action, imo);
return [formatJsonResponse(data, title)];
}
/**
* Creates a dual response pattern (main data + artifact)
* @param mainData - The primary response data
* @param artifactData - The artifact data (usually with additional metadata)
* @param action - The action description for titles
* @param imo - The IMO number
* @returns Array with both main and artifact responses
*/
export function createDualResponse(mainData, artifactData, action, imo) {
const title = generateImoTitle(action, imo);
return [
{
type: "text",
text: typeof mainData === 'string' ? mainData : JSON.stringify(mainData, null, 2),
title: title,
format: typeof mainData === 'string' ? undefined : "json"
},
formatJsonResponse(artifactData, title)
];
}
/**
* Creates a search results response with conditional artifact
* @param mainContent - The primary search results
* @param artifactData - Optional artifact data
* @param query - The search query
* @param action - The action description
* @returns Response array with optional artifact
*/
export function createSearchResponse(mainContent, artifactData, query, action = "Search results") {
if (artifactData) {
const artifactTitle = generateQueryTitle(action, query);
const artifact = formatJsonResponse(artifactData, artifactTitle);
return [mainContent, artifact];
}
return [mainContent];
}
/**
* Creates an error response with consistent formatting
* @param error - The error object or message
* @param context - Optional context information
* @param imo - Optional IMO number for context
* @returns Formatted error response array
*/
export function createErrorResponse(error, context, imo) {
let errorMessage;
if (error instanceof MissingParameterError) {
errorMessage = `Error: ${error.message}`;
}
else if (typeof error === 'string') {
errorMessage = `Error: ${error}`;
}
else {
const baseMessage = context
? `Failed to ${context}${imo ? ` for IMO ${imo}` : ''}: ${error}`
: `Error: ${error.message}`;
errorMessage = baseMessage;
}
logger.error(errorMessage);
return [formatTextResponse(errorMessage)];
}
/**
* Creates a service-specific error response
* @param error - The error object
* @param serviceName - The name of the service that failed
* @param imo - Optional IMO number for context
* @returns Formatted error response array
*/
export function createServiceErrorResponse(error, serviceName, imo) {
const errorMessage = `Error with ${serviceName} service: ${error.message}`;
logger.error(errorMessage);
return [formatTextResponse(errorMessage)];
}
/**
* Creates artifact data with standard structure
* @param toolName - The name of the tool generating the artifact
* @param url - Optional URL for the artifact
* @returns Artifact object with standard metadata
*/
export function createArtifactData(toolName, url) {
return {
id: "msg_browser_ghi789",
parentTaskId: `task_${toolName}_${Date.now()}`,
timestamp: Math.floor(Date.now() / 1000),
agent: {
id: "agent_siya_browser",
name: "SIYA",
type: "qna"
},
url: url || `https://example.com/${toolName}`,
toolUsed: toolName,
status: "success"
};
}
/**
* Creates a historical data response with consistent formatting
* @param documents - Array of historical data documents
* @param action - The action description
* @param imo - The IMO number
* @param totalRecords - Optional total record count
* @returns Formatted response for historical data
*/
export function createHistoricalDataResponse(documents, action, imo, totalRecords) {
const response = {
success: true,
data: documents,
total_records: totalRecords || documents.length,
imo: imo
};
return createVesselDataResponse(response, action, imo);
}
/**
* Creates a casefile operation response
* @param data - The casefile data
* @param operation - The operation performed (e.g., "created", "updated")
* @param casefileId - The casefile ID
* @returns Formatted casefile response
*/
export function createCasefileResponse(data, operation, casefileId) {
const title = `Casefile ${operation}: ${casefileId}`;
return [formatJsonResponse(data, title)];
}
/**
* Wraps any response creation with error handling
* @param responseCreator - Function that creates the response
* @param errorContext - Context for error messages
* @param imo - Optional IMO for error context
* @returns Response array or error response
*/
export function withErrorHandling(responseCreator, errorContext, imo) {
try {
return responseCreator();
}
catch (error) {
return createErrorResponse(error, errorContext, imo);
}
}
//# sourceMappingURL=response-formatter.js.map