defect-inspection-tools-mcp-server
Version:
Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control
322 lines • 11.5 kB
JavaScript
import { MongoClient } from 'mongodb';
import axios from 'axios';
import { logger } from './logger.js';
import { config } from './config.js';
export function getArtifact(functionName, url) {
/**
* Handle get artifact tool using updated artifact format
*/
const artifact = {
id: "msg_browser_ghi789",
parentTaskId: "task_japan_itinerary_7d8f9g",
timestamp: Math.floor(Date.now() / 1000),
agent: {
id: "agent_siya_browser",
name: "SIYA",
type: "qna"
},
messageType: "action",
action: {
tool: "browser",
operation: "browsing",
params: {
url: url,
pageTitle: `Tool response for ${functionName}`,
visual: {
icon: "browser",
color: "#2D8CFF"
},
stream: {
type: "vnc",
streamId: "stream_browser_1",
target: "browser"
}
}
},
content: `Viewed page: ${functionName}`,
artifacts: [
{
id: "artifact_webpage_1746018877304_994",
type: "browser_view",
content: {
url: url,
title: functionName,
screenshot: "",
textContent: `Observed output of cmd \`${functionName}\` executed:`,
extractedInfo: {}
},
metadata: {
domainName: "example.com",
visitTimestamp: Date.now(),
category: "web_page"
}
}
],
status: "completed"
};
return artifact;
}
export async function getVesselQnaSnapshot(imoNumber, questionNo) {
/**
* Fetch vessel QnA snapshot data asynchronously.
*
* Args:
* imoNumber (string): The IMO number of the vessel
* questionNo (string): The question number to fetch
*
* Returns:
* Promise<any>: The response data from the snapshot API
*
* Throws:
* Error: If the API request fails
*/
// API endpoint
const snapshotUrl = `https://dev-api.siya.com/v1.0/vessel-info/qna-snapshot/${imoNumber}/${questionNo}`;
// Authentication token
const jwtToken = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoiNjRkMzdhMDM1Mjk5YjFlMDQxOTFmOTJhIiwiZmlyc3ROYW1lIjoiU3lpYSIsImxhc3ROYW1lIjoiRGV2IiwiZW1haWwiOiJkZXZAc3lpYS5haSIsInJvbGUiOiJhZG1pbiIsInJvbGVJZCI6IjVmNGUyODFkZDE4MjM0MzY4NDE1ZjViZiIsImlhdCI6MTc0MDgwODg2OH0sImlhdCI6MTc0MDgwODg2OCwiZXhwIjoxNzcyMzQ0ODY4fQ.1grxEO0aO7wfkSNDzpLMHXFYuXjaA1bBguw2SJS9r2M";
// Headers for the request
const headers = {
"Authorization": jwtToken
};
try {
const response = await axios.get(snapshotUrl, { headers });
// Parse and return the JSON response
const data = response.data;
if ("resultData" in data) {
return data.resultData;
}
return data;
}
catch (error) {
logger.error(`Failed to fetch vessel QnA snapshot: ${error}`);
return null;
}
}
async function getComponentData(componentId) {
// Parse the component_id into parts
const match = componentId.match(/^(\d+)_(\d+)_(\d+)$/);
if (!match) {
return `⚠️ Invalid component_id format: ${componentId}`;
}
const [, componentNumber, questionNumber, imo] = match;
const componentNo = `${componentNumber}_${questionNumber}_${imo}`;
// Connect to MongoDB using config
const mongoUri = config.secondaryMongoUri || config.mongoUri;
const dbName = config.secondaryDbName || config.dbName;
const client = new MongoClient(mongoUri);
try {
await client.connect();
const db = client.db(dbName);
const collection = db.collection("vesselinfocomponents");
// Fetch document
const doc = await collection.findOne({ "componentNo": componentNo });
if (!doc) {
return `⚠️ No component found for ID: ${componentId}`;
}
if (!doc.data) {
return "No data found in the table component";
}
// Extract headers excluding lineitem
const headers = doc.data.headers
.filter((h) => h.name !== "lineitem")
.map((h) => h.name);
const rows = doc.data.body;
// Build markdown table
let md = "| " + headers.join(" | ") + " |\n";
md += "| " + headers.map(() => "---").join(" | ") + " |\n";
for (const row of rows) {
const formattedRow = row
.filter((cell) => !cell.lineitem) // Exclude lineitem
.map((cell) => {
if (cell.value && cell.link) {
return `[${cell.value}](${cell.link})`;
}
else if (cell.status && cell.color) {
return cell.status;
}
return String(cell);
});
md += "| " + formattedRow.join(" | ") + " |\n";
}
return md;
}
catch (error) {
logger.error(`Error getting component data: ${error}`);
return `Error getting component data: ${error}`;
}
finally {
await client.close();
}
}
async function addComponentData(answer, imo) {
const pattern = /httpsdev\.syia\.ai\/chat\/ag-grid-table\?component=(\d+_\d+)/g;
const matches = Array.from(answer.matchAll(pattern));
let result = answer;
for (const match of matches) {
const component = match[1];
try {
const replacement = await getComponentData(`${component}_${imo}`);
result = result.replace(match[0], replacement);
}
catch (error) {
logger.error(`Error replacing component data: ${error}`);
}
}
return result;
}
export async function fetchQaDetails(imo, questionNo) {
// Use secondary database configuration for fetchQaDetails
const mongoUri = config.secondaryMongoUri || config.mongoUri;
const dbName = config.secondaryDbName || config.dbName;
const client = new MongoClient(mongoUri);
try {
await client.connect();
const db = client.db(dbName);
const vesselinfos = db.collection('vesselinfos');
const imoNumber = parseInt(imo);
const query = {
'imo': imoNumber,
'questionNo': parseInt(questionNo)
};
const projection = {
'_id': 0,
'imo': 1,
'vesselName': 1,
'refreshDate': 1,
'answer': 1
};
let res = await vesselinfos.findOne(query, { projection });
if (res === null) {
res = {
'imo': imoNumber,
'vesselName': null,
'refreshDate': null,
'answer': null
};
}
if (res && res.refreshDate instanceof Date) {
const datestr = res.refreshDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'short',
year: 'numeric'
});
res.refreshDate = datestr;
}
if (res && res.answer !== null) {
res.answer = await addComponentData(res.answer, String(imoNumber));
}
try {
const link = await getVesselQnaSnapshot(String(imoNumber), String(questionNo));
if (res) {
res.link = link;
}
}
catch (error) {
if (res) {
res.link = null;
}
}
return res;
}
catch (error) {
logger.error(`Error fetching QA details: ${error}`);
throw error;
}
finally {
await client.close();
}
}
export async function getDataLink(data) {
const url = "https://dev-api.siya.com/v1.0/vessel-info/qna-snapshot";
const headers = {
"Content-Type": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoiNjRkMzdhMDM1Mjk5YjFlMDQxOTFmOTJhIiwiZmlyc3ROYW1lIjoiU3lpYSIsImxhc3ROYW1lIjoiRGV2IiwiZW1haWwiOiJkZXZAc3lpYS5haSIsInJvbGUiOiJhZG1pbiIsInJvbGVJZCI6IjVmNGUyODFkZDE4MjM0MzY4NDE1ZjViZiIsImlhdCI6MTc0MDgwODg2OH0sImlhdCI6MTc0MDgwODg2OCwiZXhwIjoxNzcyMzQ0ODY4fQ.1grxEO0aO7wfkSNDzpLMHXFYuXjaA1bBguw2SJS9r2M"
};
const payload = {
"data": data
};
try {
const response = await axios.post(url, payload, { headers });
if (response.data.status === "OK") {
return response.data.resultData;
}
else {
return null;
}
}
catch (error) {
logger.error(`Error getting data link: ${error}`);
return null;
}
}
export async function insertDataLinkToMongodb(dataLink, linkHeader, sessionId, imo, vesselName) {
/**
* Insert data link into MongoDB collection
*/
// Insert the datalink to mongodb collection casefile_data
const MONGO_URI_dev_syia_api = 'mongodb://dev-syia:m3BFsUxaPTHhE78@13.202.154.63:27017/?authMechanism=DEFAULT&authSource=dev-syia-api';
const DB_NAME_dev_syia_api = 'dev-syia-api';
// Create connection to dev-syia-api database
const client = new MongoClient(MONGO_URI_dev_syia_api);
try {
await client.connect();
const db = client.db(DB_NAME_dev_syia_api);
// Insert the datalink to mongodb collection casefile_data
const collection = "casefile_data";
const casefileDataCollection = db.collection(collection);
// Check if sessionId exists in casefile_data collection
const sessionExists = await casefileDataCollection.findOne({ "sessionId": sessionId });
const linkData = { "link": dataLink, "linkHeader": linkHeader };
if (sessionExists) {
// Append the data_link to the existing session
await casefileDataCollection.updateOne({ "sessionId": sessionId }, {
"$push": { "links": linkData },
"$set": { "datetime": new Date() }
});
}
else {
const toInsert = {
"sessionId": sessionId,
"imo": imo,
"vesselName": vesselName,
"links": [linkData],
"datetime": new Date()
};
await casefileDataCollection.insertOne(toInsert);
}
}
catch (error) {
logger.error(`Error inserting data link to MongoDB: ${error}`);
throw error;
}
finally {
await client.close();
}
}
export function convertDefectDates(document) {
/**
* Convert Unix timestamps to human readable format for defect date fields.
*/
const dateFields = [
'reportDate',
'closingDate',
'inspectionTargetDate',
'targetDate',
'extendedDate',
'nextDueDate'
];
for (const field of dateFields) {
if (field in document) {
try {
if (typeof document[field] === 'number') {
document[field] = new Date(document[field] * 1000).toISOString().replace('T', ' ').substr(0, 19);
}
}
catch (error) {
logger.warn(`Failed to convert ${field}: ${error}`);
}
}
}
return document;
}
//# sourceMappingURL=helpers.js.map