UNPKG

jinko-mcp-dev

Version:

MCP server for Jinko Travel BFF

157 lines (156 loc) 5.22 kB
/** * Utility functions for the hotel MCP server */ import fs from "fs"; import yaml from "js-yaml"; import { API_BASE_URL, FACILITIES_PATH, MAX_QUOTE_POLL_ATTEMPTS, QUOTE_POLL_INTERVAL_MS } from "./config.js"; import { session } from "./state.js"; import { getLogger, getMetrics } from "../telemetry/index.js"; /** * Load facilities data from JSON file * @returns Array of facility objects */ export function loadFacilitiesData() { try { if (fs.existsSync(FACILITIES_PATH)) { const data = fs.readFileSync(FACILITIES_PATH, "utf-8"); return JSON.parse(data); } else { console.warn("facilities.json not found at:", FACILITIES_PATH); } } catch (error) { console.error("Error loading facilities data:", error); } return []; // Return empty array on error or if file not found } /** * Make API request to the travel BFF API * @param endpoint API endpoint path * @param method HTTP method * @param body Request body (optional) * @returns Response data or null on error */ export async function makeApiRequest(endpoint, method = "GET", body) { const url = `${API_BASE_URL}${endpoint}`; const logger = getLogger(); const metrics = getMetrics(); const startTime = Date.now(); const headers = { "Content-Type": "application/json", Accept: "application/json", }; // Add context information to headers if available in session if (session.conversation_id) { headers["X-Conversation-ID"] = session.conversation_id; } if (session.market) { headers["X-Market"] = session.market; } if (session.language) { headers["X-Language"] = session.language; } if (session.currency) { headers["X-Currency"] = session.currency; } if (session.country_code) { headers["X-Country-Code"] = session.country_code; } await logger.logApiCall(endpoint, method); try { const options = { method, headers, }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(url, options); const duration = (Date.now() - startTime) / 1000; if (!response.ok) { // Record API metrics await metrics.recordApiCall(endpoint, method, duration * 1000, 'error'); // Convert to ms await logger.logApiResult(endpoint, method, duration, response.status, `API call failed: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`); } const result = (await response.json()); // Record successful API metrics await metrics.recordApiCall(endpoint, method, duration * 1000, 'success'); // Convert to ms await logger.logApiResult(endpoint, method, duration, response.status); return result; } catch (error) { const duration = (Date.now() - startTime) / 1000; // Record error API metrics await metrics.recordApiCall(endpoint, method, duration * 1000, 'error'); // Convert to ms await logger.logApiResult(endpoint, method, duration, 0, `API call error: ${error.message}`); await logger.logError(error, { endpoint, method }); return null; } } /** * Create a YAML response for MCP tools * @param data Object to convert to YAML * @returns MCP response object with YAML content */ export function createYamlResponse(data) { const yamlString = yaml.dump(data, { indent: 2, lineWidth: -1, // Don't wrap lines noRefs: true, // Don't use references }); return { content: [ { type: "text", text: yamlString, }, ], }; } /** * Create a JSON response for MCP tools * @param data Object to include in JSON * @returns MCP response object with JSON content */ export function createJsonResponse(data) { const jsonString = JSON.stringify(data, null); return { content: [ { type: "text", text: jsonString, }, ], }; } /** * Poll for quote status until it's ready or times out * @param quoteId ID of the quote to poll for * @returns Quote result or null if still processing */ export async function pollForQuoteStatus(quoteId) { let quoteStatus = "processing"; let quoteResult = null; let attempts = 0; while (quoteStatus === "processing" && attempts < MAX_QUOTE_POLL_ATTEMPTS) { attempts++; // Wait before polling again await new Promise(resolve => setTimeout(resolve, QUOTE_POLL_INTERVAL_MS)); // Poll quote status const pullResponse = await makeApiRequest(`/api/v1/booking/quote/pull/${quoteId}`, "GET"); if (!pullResponse) { continue; } quoteStatus = pullResponse.status; if (quoteStatus === "success") { quoteResult = pullResponse.quote; break; } else if (quoteStatus === "failed") { throw new Error(`Quote generation failed: ${pullResponse.error || "Unknown error"}`); } } return quoteResult; }