UNPKG

purchase-mcp-server

Version:

Purchase and budget management server handling requisitions, purchase orders, expenses, budgets, and vendor management with ERP access for data extraction

169 lines 7.54 kB
import { logger } from "../utils/logger.js"; import { Client as TypesenseClient } from "typesense"; import { processTypesenseResults } from "../utils/helper_functions.js"; const typesenseClient = new TypesenseClient({ nodes: [ { host: "j51ouydaces0i2m7p-1.a1.typesense.net", port: 443, protocol: "https" } ], apiKey: "wgTfJoajCRXWWNlLILAdZh1bU66RA4wv" }); export async function smartExpenseSearchHandler(arguments_) { const collection = "expense"; const session_id = arguments_.session_id || "testing"; const query_text = (arguments_.query || "").trim() || "*"; const filters = arguments_.filters || {}; const sort_by = arguments_.sort_by || "relevance"; const sort_order = arguments_.sort_order || "desc"; const max_results = arguments_.max_results || 10; try { // Check if vendor filter is provided if (filters.vendor) { try { // Query to find the actual vendor name const vendorQuery = { q: filters.vendor, query_by: "vendor", per_page: 1, sort_by: "_text_match:desc" // Sort by best text match }; logger.debug(`[Vendor Query] ${JSON.stringify(vendorQuery)}`); const vendorResults = await typesenseClient.collections(collection).documents().search(vendorQuery); if (vendorResults && vendorResults.hits && vendorResults.hits.length > 0) { const document = vendorResults.hits[0].document; const actualVendorName = document.vendor; logger.info(`Found actual vendor name: ${actualVendorName} for query: ${filters.vendor}`); // Replace the vendor filter with the actual vendor name filters.vendor = actualVendorName; } else { logger.info(`No vendor found for query: ${filters.vendor}`); // Return early with a message that no matching vendor was found return [{ type: "text", text: `No matching vendor found for '${filters.vendor}'. Please try a different vendor name.`, title: "No Vendor Found", format: "json" }]; } // Keep the vendor filter for the main query } catch (error) { logger.error('Error querying vendor:', error); // Return early with an error message return [{ type: "text", text: `Error searching for vendor '${filters.vendor}': ${error.message}`, title: "Vendor Search Error", format: "json" }]; } } // Compose filter_by string from filters const filterParts = []; if (filters) { for (const [key, value] of Object.entries(filters)) { if (value === null || value === undefined) { continue; } if (key.endsWith("_range")) { // Handle range filters - dates and amounts const fieldBase = key.replace("_range", ""); if (fieldBase.toLowerCase().includes("date")) { // Handle date ranges const startDate = value.start_date; const endDate = value.end_date; if (startDate) { const startTimestamp = Math.floor(new Date(startDate).getTime() / 1000); filterParts.push(`${fieldBase}:>=${startTimestamp}`); } if (endDate) { const endTimestamp = Math.floor(new Date(endDate).getTime() / 1000); filterParts.push(`${fieldBase}:<=${endTimestamp}`); } } else if (fieldBase.toLowerCase().includes("amount")) { // Handle amount ranges const minAmount = value.min_amount; const maxAmount = value.max_amount; if (minAmount !== undefined) { filterParts.push(`${fieldBase}:>=${minAmount}`); } if (maxAmount !== undefined) { filterParts.push(`${fieldBase}:<=${maxAmount}`); } } else { throw new Error(`Unsupported range filter field: ${fieldBase}`); } } else if (typeof value === "boolean") { filterParts.push(`${key}:=${value.toString().toLowerCase()}`); } else if (typeof value === "string") { filterParts.push(`${key}:=${JSON.stringify(value).replace(/^"|"$/g, "")}`); } else { filterParts.push(`${key}:=${value}`); } } } const filterBy = filterParts.length > 0 ? filterParts.join(" && ") : undefined; // Set up query fields based on schema const queryBy = "vesselName,accountDescription"; const excludeFields = "embedding,_id,docId,fleetId,vesselId,fleetManagerId,technicalSuperintendentId"; const query = { q: query_text, query_by: queryBy, exclude_fields: excludeFields, per_page: max_results }; if (filterBy) { query.filter_by = filterBy; } if (sort_by !== "relevance") { query.sort_by = `${sort_by}:${sort_order}`; } logger.debug(`[Typesense Query] ${JSON.stringify(query)}`); const results = await typesenseClient.collections(collection).documents().search(query); logger.info(`[Typesense Results] ${JSON.stringify(results)}`); if (!results || !results.hits || results.hits.length === 0) { return [{ type: "text", text: `No expense records found for query '${query_text}'.`, title: "No Results Found", format: "json" }]; } // Format results using the utility function const title = `Smart Expense Search Results for '${query_text}'`; const linkHeader = `Smart expense search result for query: '${query_text}'`; return await processTypesenseResults(results, "smart_expense_search", title, session_id, linkHeader); } catch (error) { logger.error('Error performing smart expense search:', error); return [{ type: "text", text: `Error performing search: ${error.message}`, title: "Error", format: "json" }]; } } // test the function const test = async () => { const arguments_ = { session_id: "testing", query: "BW KIZOKU", filters: { vendor: "Marine Mechanics" } }; const results = await smartExpenseSearchHandler(arguments_); console.log(results); }; test(); //# sourceMappingURL=testing.js.map