UNPKG

@shopana/novaposhta-mcp-server

Version:

MCP Server for Nova Poshta API integration with AI assistants

1,486 lines (1,469 loc) 131 kB
#!/usr/bin/env node // src/config.ts function loadConfig(overrides = {}) { const envApiKey = process.env.NOVA_POSHTA_API_KEY || process.env.NP_API_KEY; const apiKey = overrides.apiKey ?? envApiKey; const system = overrides.system ?? process.env.NOVA_POSHTA_SYSTEM; return { apiKey, baseUrl: overrides.baseUrl ?? process.env.NOVA_POSHTA_BASE_URL ?? "https://api.novaposhta.ua/v2.0/json/", logLevel: overrides.logLevel ?? process.env.LOG_LEVEL ?? "info", timeout: overrides.timeout ?? Number.parseInt(process.env.TIMEOUT ?? "30000", 10), system }; } // src/server.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { CallToolRequestSchema, ListToolsRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; // node_modules/@shopana/novaposhta-api-client/dist/core/client.js function toHttpTransport(ctx) { return { async request(request) { const { apiKey, system, ...rest } = request; const finalRequest = { ...rest, ...apiKey ? { apiKey } : {}, ...system || ctx.system ? { system: system || ctx.system } : {} }; const response = await ctx.transport({ url: ctx.baseUrl, body: finalRequest }); return response.data; } }; } function createClient(ctx) { const self = {}; self.use = function use(service) { if (typeof service.attach === "function") { service.attach(ctx); } const ns = service.namespace; if (!ns || typeof ns !== "string") { throw new Error('Service must define a string "namespace" property'); } if (ns in self) { throw new Error(`Namespace already installed on client: ${ns}`); } self[ns] = service; return self; }; return self; } // node_modules/@shopana/novaposhta-api-client/dist/types/enums.js var NovaPoshtaModel; (function(NovaPoshtaModel2) { NovaPoshtaModel2["TrackingDocument"] = "TrackingDocumentGeneral"; NovaPoshtaModel2["InternetDocument"] = "InternetDocumentGeneral"; NovaPoshtaModel2["Common"] = "CommonGeneral"; NovaPoshtaModel2["Address"] = "AddressGeneral"; NovaPoshtaModel2["Counterparty"] = "CounterpartyGeneral"; NovaPoshtaModel2["ContactPerson"] = "ContactPersonGeneral"; NovaPoshtaModel2["ScanSheet"] = "ScanSheetGeneral"; NovaPoshtaModel2["AdditionalService"] = "AdditionalServiceGeneral"; })(NovaPoshtaModel || (NovaPoshtaModel = {})); var NovaPoshtaMethod; (function(NovaPoshtaMethod2) { NovaPoshtaMethod2["GetStatusDocuments"] = "getStatusDocuments"; NovaPoshtaMethod2["Save"] = "save"; NovaPoshtaMethod2["Update"] = "update"; NovaPoshtaMethod2["Delete"] = "delete"; NovaPoshtaMethod2["GetDocumentPrice"] = "getDocumentPrice"; NovaPoshtaMethod2["GetDocumentDeliveryDate"] = "getDocumentDeliveryDate"; NovaPoshtaMethod2["GetDocumentList"] = "getDocumentList"; NovaPoshtaMethod2["GenerateReport"] = "generateReport"; NovaPoshtaMethod2["GetCargoTypes"] = "getCargoTypes"; NovaPoshtaMethod2["GetBackwardDeliveryCargoTypes"] = "getBackwardDeliveryCargoTypes"; NovaPoshtaMethod2["GetPalletsList"] = "getPalletsList"; NovaPoshtaMethod2["GetTypesOfPayersForRedelivery"] = "getTypesOfPayersForRedelivery"; NovaPoshtaMethod2["GetPackList"] = "getPackList"; NovaPoshtaMethod2["GetTiresWheelsList"] = "getTiresWheelsList"; NovaPoshtaMethod2["GetCargoDescriptionList"] = "getCargoDescriptionList"; NovaPoshtaMethod2["GetMessageCodeText"] = "getMessageCodeText"; NovaPoshtaMethod2["GetServiceTypes"] = "getServiceTypes"; NovaPoshtaMethod2["GetOwnershipFormsList"] = "getOwnershipFormsList"; NovaPoshtaMethod2["GetTimeIntervals"] = "getTimeIntervals"; NovaPoshtaMethod2["GetPickupTimeIntervals"] = "getPickupTimeIntervals"; NovaPoshtaMethod2["GetTypesOfPayers"] = "getTypesOfPayers"; NovaPoshtaMethod2["GetPaymentForms"] = "getPaymentForms"; NovaPoshtaMethod2["GetTypesOfCounterparties"] = "getTypesOfCounterparties"; NovaPoshtaMethod2["GetSettlements"] = "getSettlements"; NovaPoshtaMethod2["GetCities"] = "getCities"; NovaPoshtaMethod2["GetAreas"] = "getAreas"; NovaPoshtaMethod2["GetWarehouses"] = "getWarehouses"; NovaPoshtaMethod2["GetWarehouseTypes"] = "getWarehouseTypes"; NovaPoshtaMethod2["GetStreet"] = "getStreet"; NovaPoshtaMethod2["SearchSettlements"] = "searchSettlements"; NovaPoshtaMethod2["SearchSettlementStreets"] = "searchSettlementStreets"; NovaPoshtaMethod2["GetSettlementAreas"] = "getSettlementAreas"; NovaPoshtaMethod2["GetSettlementCountryRegion"] = "getSettlementCountryRegion"; NovaPoshtaMethod2["GetCounterparties"] = "getCounterparties"; NovaPoshtaMethod2["GetCounterpartyAddresses"] = "getCounterpartyAddresses"; NovaPoshtaMethod2["GetCounterpartyContactPersons"] = "getCounterpartyContactPersons"; NovaPoshtaMethod2["GetCounterpartyOptions"] = "getCounterpartyOptions"; })(NovaPoshtaMethod || (NovaPoshtaMethod = {})); var PaymentMethod; (function(PaymentMethod2) { PaymentMethod2["Cash"] = "Cash"; PaymentMethod2["NonCash"] = "NonCash"; })(PaymentMethod || (PaymentMethod = {})); var CargoType; (function(CargoType2) { CargoType2["Parcel"] = "Parcel"; CargoType2["Cargo"] = "Cargo"; CargoType2["Documents"] = "Documents"; CargoType2["TiresWheels"] = "TiresWheels"; CargoType2["Pallet"] = "Pallet"; })(CargoType || (CargoType = {})); var ServiceType; (function(ServiceType2) { ServiceType2["DoorsDoors"] = "DoorsDoors"; ServiceType2["DoorsWarehouse"] = "DoorsWarehouse"; ServiceType2["WarehouseWarehouse"] = "WarehouseWarehouse"; ServiceType2["WarehouseDoors"] = "WarehouseDoors"; })(ServiceType || (ServiceType = {})); var PayerType; (function(PayerType2) { PayerType2["Sender"] = "Sender"; PayerType2["Recipient"] = "Recipient"; PayerType2["ThirdPerson"] = "ThirdPerson"; })(PayerType || (PayerType = {})); var DeliveryStatus; (function(DeliveryStatus2) { DeliveryStatus2[DeliveryStatus2["CreatedBySender"] = 1] = "CreatedBySender"; DeliveryStatus2[DeliveryStatus2["Deleted"] = 2] = "Deleted"; DeliveryStatus2[DeliveryStatus2["NotFound"] = 3] = "NotFound"; DeliveryStatus2[DeliveryStatus2["InSenderCityInterregional"] = 4] = "InSenderCityInterregional"; DeliveryStatus2[DeliveryStatus2["InSenderCityLocal"] = 41] = "InSenderCityLocal"; DeliveryStatus2[DeliveryStatus2["InTransitToRecipientCity"] = 5] = "InTransitToRecipientCity"; DeliveryStatus2[DeliveryStatus2["InRecipientCityAwaitingDelivery"] = 6] = "InRecipientCityAwaitingDelivery"; DeliveryStatus2[DeliveryStatus2["ArrivedAtWarehouse"] = 7] = "ArrivedAtWarehouse"; DeliveryStatus2[DeliveryStatus2["ArrivedAtPostomat"] = 8] = "ArrivedAtPostomat"; DeliveryStatus2[DeliveryStatus2["Received"] = 9] = "Received"; DeliveryStatus2[DeliveryStatus2["ReceivedAwaitingMoneyTransfer"] = 10] = "ReceivedAwaitingMoneyTransfer"; DeliveryStatus2[DeliveryStatus2["ReceivedAndMoneyTransferred"] = 11] = "ReceivedAndMoneyTransferred"; DeliveryStatus2[DeliveryStatus2["BeingPacked"] = 12] = "BeingPacked"; DeliveryStatus2[DeliveryStatus2["OnTheWayToRecipient"] = 101] = "OnTheWayToRecipient"; DeliveryStatus2[DeliveryStatus2["RefusedByRecipientReturnCreated"] = 102] = "RefusedByRecipientReturnCreated"; DeliveryStatus2[DeliveryStatus2["RefusedByRecipient"] = 103] = "RefusedByRecipient"; DeliveryStatus2[DeliveryStatus2["AddressChanged"] = 104] = "AddressChanged"; DeliveryStatus2[DeliveryStatus2["StorageTerminated"] = 105] = "StorageTerminated"; DeliveryStatus2[DeliveryStatus2["ReturnWaybillCreated"] = 106] = "ReturnWaybillCreated"; DeliveryStatus2[DeliveryStatus2["FailedDeliveryAttempt"] = 111] = "FailedDeliveryAttempt"; DeliveryStatus2[DeliveryStatus2["DeliveryRescheduledByRecipient"] = 112] = "DeliveryRescheduledByRecipient"; })(DeliveryStatus || (DeliveryStatus = {})); var WarehouseType; (function(WarehouseType2) { WarehouseType2["Branch"] = "Branch"; WarehouseType2["Postomat"] = "Postomat"; WarehouseType2["PickupPoint"] = "PickupPoint"; })(WarehouseType || (WarehouseType = {})); var SettlementType; (function(SettlementType2) { SettlementType2["City"] = "\u043C."; SettlementType2["Town"] = "\u0441\u043C\u0442."; SettlementType2["Village"] = "\u0441."; SettlementType2["UrbanVillage"] = "\u0441\u0449."; })(SettlementType || (SettlementType = {})); var OwnershipForm; (function(OwnershipForm2) { OwnershipForm2["PrivatePerson"] = "PrivatePerson"; OwnershipForm2["Organization"] = "Organization"; })(OwnershipForm || (OwnershipForm = {})); var CounterpartyType; (function(CounterpartyType2) { CounterpartyType2["PrivatePerson"] = "PrivatePerson"; CounterpartyType2["Organization"] = "Organization"; })(CounterpartyType || (CounterpartyType = {})); var ContactPersonType; (function(ContactPersonType2) { ContactPersonType2["Sender"] = "Sender"; ContactPersonType2["Recipient"] = "Recipient"; })(ContactPersonType || (ContactPersonType = {})); var TimeIntervalType; (function(TimeIntervalType2) { TimeIntervalType2["CityDeliveryTimeInterval1"] = "CityDeliveryTimeInterval1"; TimeIntervalType2["CityDeliveryTimeInterval2"] = "CityDeliveryTimeInterval2"; TimeIntervalType2["CityDeliveryTimeInterval3"] = "CityDeliveryTimeInterval3"; TimeIntervalType2["CityDeliveryTimeInterval4"] = "CityDeliveryTimeInterval4"; })(TimeIntervalType || (TimeIntervalType = {})); var PickupTimeInterval; (function(PickupTimeInterval2) { PickupTimeInterval2["CityPickingTimeInterval1"] = "CityPickingTimeInterval1"; PickupTimeInterval2["CityPickingTimeInterval2"] = "CityPickingTimeInterval2"; PickupTimeInterval2["CityPickingTimeInterval3"] = "CityPickingTimeInterval3"; PickupTimeInterval2["CityPickingTimeInterval4"] = "CityPickingTimeInterval4"; })(PickupTimeInterval || (PickupTimeInterval = {})); var BackwardDeliveryType; (function(BackwardDeliveryType2) { BackwardDeliveryType2["Money"] = "Money"; BackwardDeliveryType2["Documents"] = "Documents"; BackwardDeliveryType2["Cargo"] = "Cargo"; })(BackwardDeliveryType || (BackwardDeliveryType = {})); var BackwardDeliverySubtype; (function(BackwardDeliverySubtype2) { BackwardDeliverySubtype2["MoneyTransfer"] = "MoneyTransfer"; BackwardDeliverySubtype2["DocumentsReturn"] = "DocumentsReturn"; BackwardDeliverySubtype2["CargoReturn"] = "CargoReturn"; })(BackwardDeliverySubtype || (BackwardDeliverySubtype = {})); var AdditionalService; (function(AdditionalService2) { AdditionalService2["SaturdayDelivery"] = "SaturdayDelivery"; AdditionalService2["DeliveryByHand"] = "DeliveryByHand"; AdditionalService2["AfterpaymentOnGoodsCost"] = "AfterpaymentOnGoodsCost"; AdditionalService2["LocalExpress"] = "LocalExpress"; AdditionalService2["PreferredDeliveryDate"] = "PreferredDeliveryDate"; AdditionalService2["TimeInterval"] = "TimeInterval"; AdditionalService2["PackingNumber"] = "PackingNumber"; AdditionalService2["InfoRegClientBarcodes"] = "InfoRegClientBarcodes"; AdditionalService2["AccompanyingDocuments"] = "AccompanyingDocuments"; AdditionalService2["AdditionalInformation"] = "AdditionalInformation"; AdditionalService2["NumberOfFloorsLifting"] = "NumberOfFloorsLifting"; AdditionalService2["NumberOfFloorsDescent"] = "NumberOfFloorsDescent"; AdditionalService2["ForwardingCount"] = "ForwardingCount"; AdditionalService2["RedBoxBarcode"] = "RedBoxBarcode"; AdditionalService2["SpecialCargo"] = "SpecialCargo"; })(AdditionalService || (AdditionalService = {})); var DocumentType; (function(DocumentType2) { DocumentType2["InternetDocument"] = "InternetDocument"; DocumentType2["ReturnDocument"] = "ReturnDocument"; DocumentType2["RedirectionDocument"] = "RedirectionDocument"; })(DocumentType || (DocumentType = {})); var Currency; (function(Currency2) { Currency2["UAH"] = "UAH"; Currency2["USD"] = "USD"; Currency2["EUR"] = "EUR"; Currency2["RUB"] = "RUB"; })(Currency || (Currency = {})); var Language; (function(Language2) { Language2["Ukrainian"] = "ua"; Language2["Russian"] = "ru"; })(Language || (Language = {})); var DeliveryDay; (function(DeliveryDay2) { DeliveryDay2["Monday"] = "Delivery1"; DeliveryDay2["Tuesday"] = "Delivery2"; DeliveryDay2["Wednesday"] = "Delivery3"; DeliveryDay2["Thursday"] = "Delivery4"; DeliveryDay2["Friday"] = "Delivery5"; DeliveryDay2["Saturday"] = "Delivery6"; DeliveryDay2["Sunday"] = "Delivery7"; })(DeliveryDay || (DeliveryDay = {})); var PackingType; (function(PackingType2) { PackingType2["Box"] = "Box"; PackingType2["Tube"] = "Tube"; PackingType2["Envelope"] = "Envelope"; PackingType2["Bag"] = "Bag"; })(PackingType || (PackingType = {})); var TireWheelType; (function(TireWheelType2) { TireWheelType2["Tires"] = "Tires"; TireWheelType2["Wheels"] = "Wheels"; })(TireWheelType || (TireWheelType = {})); var StreetType; (function(StreetType2) { StreetType2["Street"] = "\u0432\u0443\u043B."; StreetType2["Avenue"] = "\u043F\u0440\u043E\u0441\u043F."; StreetType2["Boulevard"] = "\u0431\u0443\u043B."; StreetType2["Lane"] = "\u043F\u0440\u043E\u0432."; StreetType2["Square"] = "\u043F\u043B."; StreetType2["Embankment"] = "\u043D\u0430\u0431."; })(StreetType || (StreetType = {})); var NOVA_POSHTA_MODELS = Object.values(NovaPoshtaModel); var NOVA_POSHTA_METHODS = Object.values(NovaPoshtaMethod); var PAYMENT_METHODS = Object.values(PaymentMethod); var CARGO_TYPES = Object.values(CargoType); var SERVICE_TYPES = Object.values(ServiceType); var PAYER_TYPES = Object.values(PayerType); var DELIVERY_STATUSES = Object.values(DeliveryStatus); // node_modules/@shopana/novaposhta-api-client/dist/services/waybillService.js var WaybillService = class { constructor() { this.namespace = "waybill"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Create a standard waybill */ async create(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Create a waybill with additional options and services */ async createWithOptions(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Create a postomat waybill (with restrictions) */ async createForPostomat(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Update an existing waybill */ async update(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.Update, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Delete waybills */ async delete(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.Delete, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Calculate delivery date */ async getDeliveryDate(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.GetDocumentDeliveryDate, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Calculate delivery price */ async getPrice(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.GetDocumentPrice, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Batch create multiple waybills */ async createBatch(requests) { const results = []; for (const request of requests) { try { const result = await this.create(request); results.push(result); } catch (error) { results.push({ success: false, data: [], errors: [error instanceof Error ? error.message : "Unknown error"], warnings: [], info: [], messageCodes: [], errorCodes: [], warningCodes: [], infoCodes: [] }); } } return results; } /** * Batch delete multiple waybills */ async deleteBatch(documentRefs) { return this.delete({ DocumentRefs: documentRefs }); } /** * Get estimated delivery cost and date in one request */ async getEstimate(request) { const [price, deliveryDate] = await Promise.all([ this.getPrice(request), this.getDeliveryDate({ ServiceType: request.ServiceType, CitySender: request.CitySender, CityRecipient: request.CityRecipient }) ]); return { price, deliveryDate }; } /** * Validate waybill data without creating */ async validateWaybill(_request) { try { return true; } catch { return false; } } /** * Check if postomat delivery is available for the request */ canDeliverToPostomat(request) { if (!request.CargoType || !["Parcel", "Documents"].includes(request.CargoType)) { return false; } if (!request.ServiceType || !["DoorsWarehouse", "WarehouseWarehouse"].includes(request.ServiceType)) { return false; } if (request.Cost && request.Cost > 1e4) { return false; } return true; } // ============================================================================= // LEGACY COMPATIBILITY METHODS // ============================================================================= /** * Create express waybill (legacy method for compatibility) * @deprecated Use create() method instead */ async createExpressWaybill(request) { return this.create(request); } /** * Create waybill with options (legacy method for compatibility) * @deprecated Use createWithOptions() method instead */ async createWaybillWithOptions(request) { return this.createWithOptions(request); } /** * Create postomat express waybill (legacy method for compatibility) * @deprecated Use createForPostomat() method instead */ async createPoshtomatExpressWaybill(request) { return this.createForPostomat(request); } /** * Update express waybill (legacy method for compatibility) * @deprecated Use update() method instead */ async updateExpressWaybill(request) { return this.update(request); } /** * Delete waybill (legacy method for compatibility) * @deprecated Use delete() method instead */ async deleteWaybill(request) { return this.delete(request); } /** * Get delivery date (legacy method for compatibility) * @deprecated Use getDeliveryDate() method instead */ async getDocumentDeliveryDate(request) { return this.getDeliveryDate(request); } /** * Get delivery price (legacy method for compatibility) * @deprecated Use getPrice() method instead */ async getDeliveryPrice(request) { return this.getPrice(request); } /** * Get document price (legacy method for compatibility) * @deprecated Use getPrice() method instead */ async getDocumentPrice(request) { return this.getPrice(request); } }; // node_modules/@shopana/novaposhta-api-client/dist/services/trackingService.js var TrackingService = class { constructor() { this.namespace = "tracking"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Track multiple documents */ async trackDocuments(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.TrackingDocument, calledMethod: NovaPoshtaMethod.GetStatusDocuments, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Track a single document by number */ async trackDocument(documentNumber, phone) { const response = await this.trackDocuments({ Documents: [{ DocumentNumber: documentNumber, Phone: phone }] }); if (response.success && response.data.length > 0) { return response.data[0] || null; } return null; } /** * Get document movement history */ async getDocumentMovement(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.TrackingDocument, calledMethod: NovaPoshtaMethod.GetStatusDocuments, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get list of documents for a date range */ async getDocumentList(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.InternetDocument, calledMethod: NovaPoshtaMethod.GetDocumentList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Track multiple documents and return organized results */ async trackMultiple(documentNumbers) { const documents = documentNumbers.map((num) => ({ DocumentNumber: num })); const response = await this.trackDocuments({ Documents: documents }); const successful = []; const failed = []; if (response.success && response.data) { response.data.forEach((item, index) => { if (item && item.StatusCode !== DeliveryStatus.NotFound) { successful.push(item); } else { failed.push(documentNumbers[index] || "unknown"); } }); } else { failed.push(...documentNumbers); } const statistics = this.calculateStatistics(successful); return { successful, failed, statistics }; } /** * Filter tracking results by criteria */ filterTrackingResults(results, filter) { return results.filter((result) => { if (filter.status && filter.status.length > 0) { if (!filter.status.includes(result.StatusCode)) { return false; } } if (filter.dateFrom || filter.dateTo) { const resultDate = new Date(result.DateCreated); if (filter.dateFrom && resultDate < new Date(filter.dateFrom)) { return false; } if (filter.dateTo && resultDate > new Date(filter.dateTo)) { return false; } } if (filter.citySender && !result.CitySender.includes(filter.citySender)) { return false; } if (filter.cityRecipient && !result.CityRecipient.includes(filter.cityRecipient)) { return false; } return true; }); } /** * Get tracking statistics for a set of results */ calculateStatistics(results) { const stats = { totalTracked: results.length, delivered: 0, inTransit: 0, atWarehouse: 0, failed: 0, unknown: 0 }; results.forEach((result) => { switch (result.StatusCode) { case DeliveryStatus.Received: case DeliveryStatus.ReceivedAwaitingMoneyTransfer: case DeliveryStatus.ReceivedAndMoneyTransferred: stats.delivered++; break; case DeliveryStatus.InTransitToRecipientCity: case DeliveryStatus.OnTheWayToRecipient: case DeliveryStatus.BeingPacked: stats.inTransit++; break; case DeliveryStatus.ArrivedAtWarehouse: case DeliveryStatus.ArrivedAtPostomat: case DeliveryStatus.InRecipientCityAwaitingDelivery: stats.atWarehouse++; break; case DeliveryStatus.RefusedByRecipient: case DeliveryStatus.RefusedByRecipientReturnCreated: case DeliveryStatus.FailedDeliveryAttempt: stats.failed++; break; default: stats.unknown++; break; } }); return stats; } /** * Check if document is delivered */ isDelivered(status) { return [ DeliveryStatus.Received, DeliveryStatus.ReceivedAwaitingMoneyTransfer, DeliveryStatus.ReceivedAndMoneyTransferred ].includes(status.StatusCode); } /** * Check if document is in transit */ isInTransit(status) { return [ DeliveryStatus.InTransitToRecipientCity, DeliveryStatus.OnTheWayToRecipient, DeliveryStatus.BeingPacked ].includes(status.StatusCode); } /** * Check if document is at warehouse/postomat */ isAtWarehouse(status) { return [ DeliveryStatus.ArrivedAtWarehouse, DeliveryStatus.ArrivedAtPostomat, DeliveryStatus.InRecipientCityAwaitingDelivery ].includes(status.StatusCode); } /** * Get human-readable status description */ getStatusDescription(status, language = "ua") { const descriptions = { [DeliveryStatus.CreatedBySender]: { ua: "\u0421\u0442\u0432\u043E\u0440\u0435\u043D\u043E \u0432\u0456\u0434\u043F\u0440\u0430\u0432\u043D\u0438\u043A\u043E\u043C", ru: "\u0421\u043E\u0437\u0434\u0430\u043D\u043E \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u0435\u043B\u0435\u043C", en: "Created by sender" }, [DeliveryStatus.Deleted]: { ua: "\u0412\u0438\u0434\u0430\u043B\u0435\u043D\u043E", ru: "\u0423\u0434\u0430\u043B\u0435\u043D\u043E", en: "Deleted" }, [DeliveryStatus.NotFound]: { ua: "\u041D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E", ru: "\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E", en: "Not found" }, [DeliveryStatus.InTransitToRecipientCity]: { ua: "\u041F\u0440\u044F\u043C\u0443\u0454 \u0434\u043E \u043C\u0456\u0441\u0442\u0430 \u043E\u0434\u0435\u0440\u0436\u0443\u0432\u0430\u0447\u0430", ru: "\u041D\u0430\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0432 \u0433\u043E\u0440\u043E\u0434 \u043F\u043E\u043B\u0443\u0447\u0430\u0442\u0435\u043B\u044F", en: "En route to recipient city" }, [DeliveryStatus.Received]: { ua: "\u041E\u0442\u0440\u0438\u043C\u0430\u043D\u043E", ru: "\u041F\u043E\u043B\u0443\u0447\u0435\u043D\u043E", en: "Delivered" } // Add more status descriptions as needed }; return descriptions[status]?.[language] || `Unknown status: ${status}`; } /** * Monitor documents with periodic updates */ async monitorDocuments(documentNumbers, callback, intervalMs = 3e5) { const monitor = async () => { try { const response = await this.trackMultiple(documentNumbers); callback(response.successful); } catch (error) { console.error("Error monitoring documents:", error); } }; await monitor(); const intervalId = setInterval(monitor, intervalMs); return () => { clearInterval(intervalId); }; } // ============================================================================= // LEGACY COMPATIBILITY METHODS // ============================================================================= /** * Track waybill (legacy method for compatibility) * @deprecated Use trackDocument() or trackDocuments() method instead */ async trackWaybill(request) { return this.trackDocuments({ Documents: request.documents.map((doc) => ({ DocumentNumber: doc.documentNumber, Phone: doc.phone })) }); } /** * Get status documents (legacy method for compatibility) * @deprecated Use trackDocuments() method instead */ async getStatusDocuments(request) { return this.trackDocuments(request); } }; // node_modules/@shopana/novaposhta-api-client/dist/services/referenceService.js var ReferenceService = class { constructor() { this.namespace = "reference"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Get cargo types * @description Retrieves list of available cargo types * @cacheable 24 hours */ async getCargoTypes(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetCargoTypes, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get pallets list * @description Retrieves list of available pallets * @cacheable 24 hours */ async getPalletsList(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetPalletsList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get pack list * @description Retrieves list of available packaging types * @cacheable 24 hours */ async getPackList(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetPackList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get tires and wheels list * @description Retrieves list of available tires and wheels types * @cacheable 24 hours */ async getTiresWheelsList(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetTiresWheelsList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get cargo description list * @description Retrieves list of cargo descriptions with optional search * @cacheable 24 hours */ async getCargoDescriptionList(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetCargoDescriptionList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get message code text * @description Retrieves list of error codes and their descriptions * @cacheable 24 hours */ async getMessageCodeText(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetMessageCodeText, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get service types * @description Retrieves list of delivery service types * @cacheable 24 hours */ async getServiceTypes(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetServiceTypes, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get ownership forms list * @description Retrieves list of ownership forms * @cacheable 24 hours */ async getOwnershipFormsList(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetOwnershipFormsList, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get time intervals * @description Retrieves available time intervals for delivery * @cacheable 1 hour */ async getTimeIntervals(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetTimeIntervals, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get pickup time intervals * @description Retrieves available time intervals for pickup * @cacheable 1 hour */ async getPickupTimeIntervals(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetPickupTimeIntervals, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get backward delivery cargo types * @description Retrieves list of backward delivery cargo types * @cacheable 24 hours */ async getBackwardDeliveryCargoTypes(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetBackwardDeliveryCargoTypes, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get types of payers for redelivery * @description Retrieves list of payer types for redelivery * @cacheable 24 hours */ async getTypesOfPayersForRedelivery(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetTypesOfPayersForRedelivery, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get types of payers * @description Retrieves list of payer roles (Sender/Recipient/ThirdPerson) * @cacheable 24 hours */ async getTypesOfPayers(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetTypesOfPayers, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get payment forms * @description Retrieves list of payment forms (Cash/NonCash) * @cacheable 24 hours */ async getPaymentForms(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetPaymentForms, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get types of counterparties * @description Retrieves list of counterparty types (PrivatePerson/Organization) * @cacheable 24 hours */ async getTypesOfCounterparties(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Common, calledMethod: NovaPoshtaMethod.GetTypesOfCounterparties, methodProperties: request }; return await this.transport.request(apiRequest); } }; // node_modules/@shopana/novaposhta-api-client/dist/services/addressService.js var AddressService = class { constructor() { this.namespace = "address"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Get settlements (areas) * @description Retrieves list of settlement areas * @cacheable 12 hours */ async getSettlements(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.GetSettlementAreas, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get settlement country regions * @description Retrieves list of settlement regions for a specific area * @cacheable 12 hours */ async getSettlementCountryRegion(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.GetSettlementCountryRegion, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get cities * @description Retrieves list of cities with Nova Poshta offices * @cacheable 12 hours */ async getCities(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.GetCities, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get streets * @description Retrieves list of streets in a specific city * @cacheable 12 hours */ async getStreet(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.GetStreet, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Search settlements online * @description Performs online search for settlements * @cacheable 1 hour */ async searchSettlements(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.SearchSettlements, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Search settlement streets online * @description Performs online search for streets in a settlement * @cacheable 1 hour */ async searchSettlementStreets(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.SearchSettlementStreets, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get warehouses (branches and postomats) * @description Retrieves list of Nova Poshta warehouses filtered by city, settlement, or other criteria * @cacheable 1 hour * @note API returns HTTP 303 Redirect with link to cached file for some cities */ async getWarehouses(request = {}) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.GetWarehouses, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Save address * @description Creates a new counterparty address */ async save(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Update address * @description Updates existing counterparty address */ async update(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.Update, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Delete address * @description Deletes counterparty address by reference */ async delete(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Address, calledMethod: NovaPoshtaMethod.Delete, methodProperties: request }; return await this.transport.request(apiRequest); } }; // node_modules/@shopana/novaposhta-api-client/dist/services/counterpartyService.js var CounterpartyService = class { constructor() { this.namespace = "counterparty"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Get counterparties * @description Retrieves list of counterparties by property (Sender/Recipient/ThirdPerson) * @cacheable 1 hour */ async getCounterparties(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.GetCounterparties, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get counterparty addresses * @description Retrieves addresses for a specific counterparty * @cacheable 1 hour */ async getCounterpartyAddresses(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.GetCounterpartyAddresses, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get counterparty contact persons * @description Retrieves contact persons for a counterparty * @cacheable 1 hour */ async getCounterpartyContactPersons(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.GetCounterpartyContactPersons, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Save counterparty * @description Creates a new counterparty (sender or recipient) */ async save(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Update counterparty * @description Updates an existing counterparty */ async update(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.Update, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Delete counterparty * @description Deletes a counterparty by reference (only recipients) */ async delete(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.Delete, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Get counterparty options * @description Retrieves available options (payments, delays) for counterparty * @cacheable 1 hour */ async getCounterpartyOptions(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.Counterparty, calledMethod: NovaPoshtaMethod.GetCounterpartyOptions, methodProperties: request }; return await this.transport.request(apiRequest); } }; // node_modules/@shopana/novaposhta-api-client/dist/services/contactPersonService.js var ContactPersonService = class { constructor() { this.namespace = "contactPerson"; } attach(ctx) { this.transport = toHttpTransport(ctx); this.apiKey = ctx.apiKey; } /** * Save contact person * @description Creates a new contact person for a counterparty */ async save(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.ContactPerson, calledMethod: NovaPoshtaMethod.Save, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Update contact person * @description Updates an existing contact person record */ async update(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.ContactPerson, calledMethod: NovaPoshtaMethod.Update, methodProperties: request }; return await this.transport.request(apiRequest); } /** * Delete contact person * @description Deletes a contact person by reference */ async delete(request) { const apiRequest = { ...this.apiKey ? { apiKey: this.apiKey } : {}, modelName: NovaPoshtaModel.ContactPerson, calledMethod: NovaPoshtaMethod.Delete, methodProperties: request }; return await this.transport.request(apiRequest); } }; // ../novaposhta-transport-fetch/dist/index.js import fetchPonyfill from "cross-fetch"; function createFetchHttpTransport(init) { const doFetch = init?.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : fetchPonyfill); const headers = { "Content-Type": "application/json", Accept: "application/json", ...init?.baseHeaders ?? {} }; async function transport({ url, body, signal }) { const res = await doFetch(url, { method: "POST", headers, body: JSON.stringify(body), signal }); const data = await res.json(); return { status: res.status, data }; } return transport; } // src/server.ts import { readFileSync } from "fs"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; import { createServer } from "http"; import { randomUUID } from "crypto"; // src/utils/logger.ts var LEVEL_PRIORITY = { debug: 10, info: 20, warn: 30, error: 40 }; var Logger = class { constructor(level = "info") { this.level = level; } debug(message, extra) { this.log("debug", message, extra); } info(message, extra) { this.log("info", message, extra); } warn(message, extra) { this.log("warn", message, extra); } error(message, extra) { this.log("error", message, extra); } log(level, message, extra) { if (LEVEL_PRIORITY[level] < LEVEL_PRIORITY[this.level]) { return; } const payload = extra !== void 0 ? { message, extra } : { message }; console.error(JSON.stringify({ level, timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...payload })); } }; // src/utils/error-handler.ts function formatError(error) { if (isNovaPoshtaError(error)) { const context = error.context ? ` Context: ${JSON.stringify(error.context)}` : ""; return `Nova Poshta API error (${error.code}): ${error.message}.${context}`; } if (error instanceof Error) { return error.message; } return typeof error === "string" ? error : "Unknown error"; } function formatResponseErrors(errors, fallback) { if (Array.isArray(errors) && errors.length > 0) { return errors.join(", "); } return fallback; } function toErrorResult(error, prefix = "Failed to execute tool") { return { content: [ { type: "text", text: `${prefix}: ${formatError(error)}` } ], isError: true }; } function isNovaPoshtaError(error) { return Boolean( error && typeof error === "object" && "code" in error && "message" in error ); } // src/utils/validation.ts function isNonEmptyString(value) { return typeof value === "string" && value.trim().length > 0; } function isTrackingNumber(value) { return typeof value === "string" && /^\d{14}$/.test(value.trim()); } function isPhoneNumber(value) { return typeof value === "string" && /^380\d{9}$/.test(value.trim()); } function isDateFormat(value) { if (typeof value !== "string") return false; const trimmed = value.trim(); if (!/^\d{2}\.\d{2}\.\d{4}$/.test(trimmed)) return false; const [day, month, year] = trimmed.split(".").map(Number); if (month < 1 || month > 12) return false; if (day < 1 || day > 31) return false; if (year < 1900 || year > 2100) return false; return true; } function sanitizePhone(phone) { let sanitized = phone.replace(/\D/g, ""); if (sanitized.startsWith("80")) { sanitized = "3" + sanitized; } if (sanitized.startsWith("0")) { sanitized = "38" + sanitized; } return sanitized; } function assertString(value, field) { if (!isNonEmptyString(value)) { throw new Error(`Field "${field}" must be a non-empty string`); } return value.trim(); } function assertNumber(value, field) { if (typeof value === "string" && value.trim() !== "") { const parsed = Number(value); if (!Number.isNaN(parsed)) { return parsed; } } if (typeof value !== "number" || Number.isNaN(value)) { throw new Error(`Field "${field}" must be a valid number`); } return value; } function assertOptionalString(value, field) { if (value === void 0 || value === null) { return void 0; } return assertString(value, field); } function assertOptionalNumber(value, field) { if (value === void 0 || value === null || value === "") { return void 0; } return assertNumber(value, field); } // src/utils/tool-response.ts function createTextResult(message, structuredContent) { return { content: [ { type: "text", text: message } ], ...structuredContent ? { structuredContent } : {} }; } function formatAsJson(input) { return JSON.stringify(input, null, 2); } // src/tools/address.ts var addressTools = [ { name: "address_get_settlements", description: "Get settlement areas (\u043E\u0431\u043B\u0430\u0441\u0442\u0435\u0439) via Address/getSettlementAreas (doc 1.3). Returns list of administrative regions/areas in Ukraine. Docs recommend caching this public directory for 12 hours.", inputSchema: { type: "object", properties: { Ref: { type: "string", description: "Optional area reference to get specific area." } }, required: [] } }, { name: "address_get_settlement_country_region", description: "Get settlement country regions (\u0440\u0435\u0433\u0456\u043E\u043D\u0456\u0432) for a specific area via Address/getSettlementCountryRegion (doc 1.3). Returns list of regions within an administrative area. Requires a