UNPKG

@atlas-kitchen/atlas-mcp

Version:

Model Context Protocol server for Atlas restaurant management system - enables Claude to interact with restaurant orders, menus, and reports

1,142 lines 29.2 kB
import { GraphQLClient } from 'graphql-request'; export class AtlasClient { restaurantClient; accountClient; authManager; baseUrl; constructor(authManager) { this.authManager = authManager; this.baseUrl = process.env.ATLAS_GRAPHQL_ENDPOINT || 'https://api.atlas.kitchen'; // Create separate clients for different graph contexts this.restaurantClient = new GraphQLClient(`${this.baseUrl}/v1/restaurants/graphql`); this.accountClient = new GraphQLClient(`${this.baseUrl}/v1/accounts/graphql`); } async request(query, variables, context = 'restaurants') { const client = context === 'accounts' ? this.accountClient : this.restaurantClient; try { // Set headers for this request const headers = this.authManager.getHeaders(); return await client.request(query, variables, headers); } catch (error) { // Handle GraphQL errors if (error.response?.errors) { const graphqlError = error.response.errors[0]; throw new Error(`GraphQL Error: ${graphqlError.message}`); } throw error; } } async login(email, password) { const query = ` mutation AccountLogin($email: String!, $password: String!) { accountLogin(input: { email: $email, password: $password }) { refreshToken accessToken account { id email } merchants { identifier } } } `; const response = await this.request(query, { email, password }, 'accounts'); return response.accountLogin; } async logout() { const query = ` mutation AccountLogout { accountLogout { success } } `; const response = await this.request(query, {}, 'accounts'); return response.accountLogout; } async refreshToken(refreshToken) { // Temporarily set the refresh token as the access token for this request const currentToken = this.authManager.getAccessToken(); this.authManager.setTokens({ accessToken: refreshToken, refreshToken: refreshToken }); try { const query = ` mutation GenerateAccessToken { accountGenerateAccessToken { accessToken refreshToken account { id email name username isVerified requireSetPassword merchants { id identifier name } merchantAccounts { id merchantId role } } merchants { id identifier name } } } `; const response = await this.request(query, {}, 'accounts'); return response.accountGenerateAccessToken; } finally { // Restore the original token if the refresh failed if (currentToken) { this.authManager.setTokens({ accessToken: currentToken, refreshToken: refreshToken }); } } } async getOrders(filters = {}) { const query = ` query GetOrders($filter: OrderFilter) { allOrders(filter: $filter) { totalCount page perPage metadata orders { state identifier servingDate createdAt addressLine1 addressLine2 postalCode brand { label } timeslotRange fulfilmentType fulfilmentSubType fulfilmentState isContactless isCutleryRequired notes paymentBreakdown { total totalIncludingTax tip } table { name } outlet { label labelForPickup } id sourceLabel servedByName externalOrderShortCode topLevelItems { name quantity } contactName contactEmail contactNumber isGift recipientName recipientContactNumber giftMessage currentTrip { externalLogisticsId externalLogisticsCode externalLogisticsType merchantLogisticsCost } orderPayments { paymentType { label } } } } } `; // Transform filters to match OrderFilter structure const filter = {}; if (filters.startDate || filters.endDate) { filter.dateRange = {}; if (filters.startDate) filter.dateRange.startDate = filters.startDate; if (filters.endDate) filter.dateRange.endDate = filters.endDate; } if (filters.state) filter.state = filters.state; if (filters.first) filter.perPage = filters.first; if (filters.after) filter.page = filters.after; // This might need adjustment based on actual cursor structure const response = await this.request(query, { filter }); return response.allOrders; } async getOrder(orderId) { const query = ` query GetOrder($id: Int!) { order(id: $id) { id identifier state servingDate prepareBy isContactless isCutleryRequired fulfilmentType fulfilmentSubType fulfilmentState notes buzzerIdentifier chargeId stripeAccountId isAdyenEnabled sourceLabel externalOrderShortCode contactName contactNumber contactEmail contactAccessCode isGift recipientName recipientContactNumber giftMessage promoCode stationId isPostTaxDiscount recipientOrganisationName invoices { id paymentStatus totalPaid totalRefunded amountOutstanding invoicePayments { id createdAt amount paymentType { label } reference referenceType notes invoicePaymentRefunds { id createdAt amount reason reference referenceType paymentType { label } } } } pointProgram { currencyNames isCustomCurrency label type } promotion { id type label description valueType } orderDiscounts { id discountType value quantity reason promoCode targetType sourceType sourceId } createdAt externalOrderedAt payAtStation serveByOverride servedByName table { name } address { id line1 line2 postalCode notes } brand { label } timeslotId timeslotType timeslotRange currentTrip { externalLogisticsId externalLogisticsCode externalLogisticsType externalLogisticsTrackingUrl isCancelled logisticsDriverInfo logisticsStatus } topLevelItems { id name quantity notes subtotal itemModifierGroupId paid amountPaid discount refunds refundableAmount unitPriceFractional unitLabel customIdentifier item { id isConfigurable idBreadcrumb horizontalImageUrl } isOpenItem adjustmentReason { id label } sortedSubItems { id idBreadcrumb label subItems { id name quantity subtotal unitLabel customIdentifier item { id isConfigurable idBreadcrumb horizontalImageUrl } adjustmentReason { id label } sortedSubItems { id idBreadcrumb label subItems { id name quantity subtotal unitLabel customIdentifier item { id isConfigurable idBreadcrumb horizontalImageUrl } adjustmentReason { id label } } } } } subItems { id modifierId itemModifierGroupId name quantity subtotal unitLabel perUnitQuantity customIdentifier adjustmentReason { id label } unitPriceFractional subItems { id modifierId itemModifierGroupId name quantity subtotal unitLabel perUnitQuantity customIdentifier adjustmentReason { id label } unitPriceFractional } } appliedDiscounts { id value percentValue quantity reason } } user { id userId name email mobileNumber isGuest addresses { id line1 line2 nearestOutlet { id label labelForPickup } } } outlet { id label labelForPickup useAutoBookLogistics stations { id name posEnabled printers { id name } paymentTerminals { id } } } paymentBreakdown { subtotal merchantTotalIncludingTax merchantTax totalIncludingTax tax taxInclusivePrices cashVouchersAmount surcharge surchargeLabel serviceCharge serviceChargeDiscount donationAmount donationLabel pointsAmount pointsValue tip discount platformDiscount adminDiscount deliveryFee platformDeliveryFee amountUnpaid rounding } minimumVehicleType minimumVehicleTypeItemLabel minimumVehicleTypeOrderValue paymentLinkId paymentLinkUrl paymentTypeId paymentTypeLabel paymentType { id label subType paymentTypeImageUrl paymentMethodId } cart { id bills { id capturedAt total paymentType { id label subType paymentTypeImageUrl paymentMethodId } } capturedAmount uncapturedAmount cartCashVouchers { id quantity voucherReference cashVoucher { id label value code } } } orderPayments { id amount tip isCaptured captureId paymentType { id label subType paymentTypeImageUrl paymentMethodId } cardLast4 orderRefunds { id type amount reason isSuccessful responseReason } } } } `; // Convert string ID to integer if needed const id = parseInt(orderId, 10); const response = await this.request(query, { id }); return response.order; } async getCart(cartId) { const query = ` query GetPosCartOptimised($cartId: Int!) { getPosCartOptimised(cartId: $cartId) { id archived isCheckedOut promoCode promoCodeError platform payAtStation posUserValidated promotion { id type label description value valueType } cartDiscounts { id discountType value quantity reason promoCode targetType sourceType sourceId } cartCashVouchers { id cashVoucherId voucherReference quantity cashVoucher { id code value } } cashVouchers { id code value } donationId isPostTaxDiscount serviceChargeable paymentBreakdown { deliveryFee surcharge surchargeLabel discount donationAmount cashVouchersAmount tax subtotal serviceCharge serviceChargeDiscount totalIncludingTax taxInclusivePrices rounding cashRounding pointsValue pointsAmount usePoints } notes buzzerIdentifier userId user { id userId name email mobileNumber isGuest } pax paxKids paymentTypeId paymentTypeLabel lastBilledAt topLevelItems { id name quantity notes subtotal serviceChargeable itemModifierGroupId sentAt unitLabel customIdentifier seatId cartItemGroup { id label createdAt groupType } adjustmentReason { id label } appliedDiscounts { id value percentValue quantity reason sourceType sourceId } itemId item { id idBreadcrumb horizontalImageUrl } isOpenItem sortedSubItems { idBreadcrumb label subItems { id itemId name notes quantity subtotal unitLabel customIdentifier adjustmentReason { id label } sortedSubItems { idBreadcrumb label subItems { id itemId name notes quantity subtotal unitLabel customIdentifier adjustmentReason { id label } } } } } subItems { id itemId modifierId itemModifierGroupId name notes quantity subtotal unitLabel customIdentifier adjustmentReason { id label } subItems { id itemId modifierId itemModifierGroupId name notes quantity subtotal unitLabel customIdentifier adjustmentReason { id label } } } } qrHash tableId tableName fulfilmentType outletId servingDate timeslotType timeslot { id rangeLabel } address { id line1 line2 postalCode notes } hasUnsentItems hasSentItems hasHeldItems bills { id billItemsCount capturedAt discount donation paymentTypeId paymentType { id label } tax total type billItems { id cartItemId cartItem { id name quantity notes } quantity total } } capturedAmount uncapturedAmount splitAmount unsplitAmount uncapturedCartItems { id name quantity subtotal } } } `; // Convert string ID to integer if needed const id = parseInt(cartId, 10); const response = await this.request(query, { cartId: id }); return response.getPosCartOptimised; } async getOpenPosCarts() { const query = ` query GetOpenPosCarts { allOpenPosCarts { id paymentTypeId paymentTypeLabel platform payAtStation buzzerIdentifier fulfilmentType tableId tableName hasUnsentItems lastBilledAt hasSentItems hasHeldItems notes pax paxKids createdAt subtotal user { id name } } } `; const response = await this.request(query); return response.allOpenPosCarts; } async getMenus(outletId, servingDate, timeslotType) { const query = ` query GetMenus($outletId: Int, $servingDate: ISO8601Date, $timeslotType: String) { menus(outletId: $outletId, servingDate: $servingDate, timeslotType: $timeslotType) { id state label identifier startDate endDate createdAt updatedAt brandId tagList imageUrl brand { label } outletBrands { outletId brandId grabFoodMerchantId foodpandaMerchantId } brandOutletMenus { outletId brandId } } } `; const response = await this.request(query, { outletId, servingDate, timeslotType }); return response.menus; } async getOptimizedMenus(outletId, servingDate) { const query = ` query GetOptimisedMenus($outletId: Int!, $servingDate: ISO8601Date!) { menusOptimised(outletId: $outletId, servingDate: $servingDate) { id label startDate endDate sections { id label description displayOrder items { id label description displayOrder unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb modifierGroups { id label selectionRequiredMin selectionRequiredMax displayOrder isFixed maxQuantityPerModifier idBreadcrumb modifiers { id label description displayOrder itemId unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb defaultQuantity unitLabel perUnitQuantity modifierGroups { id label selectionRequiredMin selectionRequiredMax displayOrder isFixed maxQuantityPerModifier idBreadcrumb modifiers { id label description displayOrder itemId unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb defaultQuantity unitLabel perUnitQuantity } } } } } subSections { id label description displayOrder items { id label description displayOrder unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb modifierGroups { id label selectionRequiredMin selectionRequiredMax displayOrder isFixed maxQuantityPerModifier idBreadcrumb modifiers { id label description displayOrder itemId unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb defaultQuantity unitLabel perUnitQuantity modifierGroups { id label selectionRequiredMin selectionRequiredMax displayOrder isFixed maxQuantityPerModifier idBreadcrumb modifiers { id label description displayOrder itemId unitPriceFractional currency isConfigurable horizontalImageUrl idBreadcrumb defaultQuantity unitLabel perUnitQuantity } } } } } } } } } `; const response = await this.request(query, { outletId, servingDate }); return response.menusOptimised; } async getItems(filter) { const query = ` query GetItems($filter: ItemFilter) { allItems(filter: $filter) { totalCount page perPage items { id type archivedAt identifier label internalLabel description unitPriceFractional unitCostFractional unitLabel perUnitQuantity currency serviceChargeable isConfigurable horizontalImageUrl brandId kitchenTagList tagList reportCategory promotionalLabelText promotionalLabelFontColor promotionalLabelBgColor availabilityConditionGroups { id startDate endDate hidden disabledReason availableByDefault availabilityConditions { id outletIds daysOfWeek fulfilmentTypes startTime endTime } } } } } `; const response = await this.request(query, { filter }); return response.allItems; } async getSalesReport(filters, dateRange) { const query = ` query GetSalesSummaryReport($filters: ReportFilter, $dateRange: DateRange, $dateRangeType: DateRangeField) { getSalesSummaryReport(filters: $filters, dateRange: $dateRange, dateRangeType: $dateRangeType) { metadata salesSummary netSalesByDay netSalesByHour paymentSummary underpaymentSummary outletSummary diningOptionSummary tableSummary orderSourceSummary promotionSummary brandSummary salesCategorySummary shiftSummary tipSummary } } `; const response = await this.request(query, { filters, dateRange, dateRangeType: "ORDER_SERVING_DATE" }); return response.getSalesSummaryReport; } async getProductInsights(params) { const query = ` query GetProductInsights( $brandIds: [Int!]! $outletIds: [Int!]! $itemTypes: [String!]! $fulfilmentTypes: [String!]! $servingDateBetween: DateRange! $sources: [String!] $itemTagIds: [Int!] $searchQuery: String $itemsToVisualize: [String!] ) { getProductInsights( brandIds: $brandIds outletIds: $outletIds itemTypes: $itemTypes fulfilmentTypes: $fulfilmentTypes servingDateBetween: $servingDateBetween sources: $sources itemTagIds: $itemTagIds searchQuery: $searchQuery itemsToVisualize: $itemsToVisualize ) { top20ProductsByCount top20ProductsBySales bottom20ProductsByCount bottom20ProductsBySales allItemsByPopularity allProductsPurchased rawData } } `; const response = await this.request(query, params); return response.getProductInsights; } } //# sourceMappingURL=client.js.map