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

608 lines 15.3 kB
import { GraphQLClient } from 'graphql-request'; export class AtlasClient { restaurantClient; accountClient; authManager; baseUrl; _isRetrying = false; onAuthFailure = null; constructor(authManager) { this.authManager = authManager; this.baseUrl = process.env.ATLAS_GRAPHQL_ENDPOINT || 'https://api.atlas.kitchen'; 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 { const headers = this.authManager.getHeaders(); return await client.request(query, variables, headers); } catch (error) { // Check for auth failure and retry once (guard against recursion from auth calls) if (!this._isRetrying && this.isAuthError(error) && this.onAuthFailure) { this._isRetrying = true; try { const recovered = await this.onAuthFailure(); if (recovered) { const headers = this.authManager.getHeaders(); return await client.request(query, variables, headers); } } finally { this._isRetrying = false; } } if (error.response?.errors) { const graphqlError = error.response.errors[0]; throw new Error(`GraphQL Error: ${graphqlError.message}`); } throw error; } } isAuthError(error) { const status = error.response?.status; if (status === 401 || status === 403) return true; const message = error.response?.errors?.[0]?.message || error.message || ''; return /unauthorized|unauthenticated|not logged in|token.*expired|invalid.*token/i.test(message); } async apiKeyLogin(apiKey) { const query = ` mutation ApiKeyLogin($apiKey: String!) { apiKeyLogin(input: { apiKey: $apiKey }) { accessToken refreshToken } } `; const response = await this.request(query, { apiKey }, 'accounts'); return response.apiKeyLogin; } async refreshToken(refreshToken) { const currentToken = this.authManager.getAccessToken(); this.authManager.setTokens({ accessToken: refreshToken, refreshToken: refreshToken }); try { const query = ` mutation GenerateAccessToken { accountGenerateAccessToken { accessToken refreshToken } } `; const response = await this.request(query, {}, 'accounts'); return response.accountGenerateAccessToken; } finally { if (currentToken) { this.authManager.setTokens({ accessToken: currentToken, refreshToken: refreshToken }); } } } async getMerchants() { const query = ` query GetMerchants { account { merchants { brandAndEntityNames id identifier name } } } `; const response = await this.request(query, undefined, 'accounts'); return response.account.merchants; } async getOrders(filters = {}) { const query = ` query GetOrders($filter: OrderFilter) { allOrdersOptimised(filter: $filter) { totalCount page perPage orders { identifier brand outlet servingDate orderDate timeslotRange fulfilmentType contactName topLevelItems totalIncludingTax } } } `; // Transform filters to match OrderFilter structure const filter = {}; if (filters.startDate || filters.endDate) { filter.servingDateBetween = {}; if (filters.startDate) filter.servingDateBetween.start_date = filters.startDate; if (filters.endDate) filter.servingDateBetween.end_date = filters.endDate; } if (filters.state) filter.state = filters.state; if (filters.perPage) filter.perPage = filters.perPage; if (filters.page) filter.page = filters.page; const response = await this.request(query, { filter }); return response.allOrdersOptimised; } async getOrder(orderId) { const query = ` query GetOrder($id: Int!) { order(id: $id) { id identifier state servingDate prepareBy createdAt fulfilmentType fulfilmentSubType fulfilmentState notes sourceLabel externalOrderShortCode contactName contactNumber contactEmail isGift recipientName recipientContactNumber giftMessage promoCode servedByName brand { label } outlet { id label labelForPickup } table { name } address { line1 line2 postalCode notes } timeslotRange promotion { id type label description valueType } orderDiscounts { id discountType value quantity reason promoCode } topLevelItems { id name quantity notes subtotal unitPriceFractional unitLabel discount subItems { id name quantity subtotal unitLabel unitPriceFractional subItems { id name quantity subtotal } } appliedDiscounts { id value percentValue reason } } user { id name email mobileNumber isGuest } paymentBreakdown { subtotal totalIncludingTax tax surcharge serviceCharge tip discount deliveryFee amountUnpaid rounding } orderPayments { id amount tip paymentType { id label } cardLast4 orderRefunds { id amount reason isSuccessful } } currentTrip { id externalLogisticsId externalLogisticsType externalLogisticsStatus externalLogisticsTrackingUrl externalLogisticsCode externalLogisticsCost merchantLogisticsCost isCancelled driverDetails distance } } } `; // Convert string ID to integer if needed const id = parseInt(orderId, 10); const response = await this.request(query, { id }); return response.order; } async getOrderByIdentifier(identifier) { const query = ` query GetOrderByIdentifier($identifier: String!) { getOrderByIdentifier(identifier: $identifier) { id identifier state servingDate prepareBy createdAt fulfilmentType fulfilmentSubType fulfilmentState notes sourceLabel externalOrderShortCode contactName contactNumber contactEmail isGift recipientName recipientContactNumber giftMessage promoCode servedByName brand { label } outlet { id label labelForPickup } table { name } address { line1 line2 postalCode notes } timeslotRange promotion { id type label description valueType } orderDiscounts { id discountType value quantity reason promoCode } topLevelItems { id name quantity notes subtotal unitPriceFractional unitLabel discount subItems { id name quantity subtotal unitLabel unitPriceFractional subItems { id name quantity subtotal } } appliedDiscounts { id value percentValue reason } } user { id name email mobileNumber isGuest } paymentBreakdown { subtotal totalIncludingTax tax surcharge serviceCharge tip discount deliveryFee amountUnpaid rounding } orderPayments { id amount tip paymentType { id label } cardLast4 orderRefunds { id amount reason isSuccessful } } currentTrip { id externalLogisticsId externalLogisticsType externalLogisticsStatus externalLogisticsTrackingUrl externalLogisticsCode externalLogisticsCost merchantLogisticsCost isCancelled driverDetails distance } } } `; const response = await this.request(query, { identifier }); return response.getOrderByIdentifier; } async getCart(cartId) { const query = ` query GetPosCartOptimised($cartId: Int!) { getPosCartOptimised(cartId: $cartId) { id isCheckedOut platform fulfilmentType promoCode notes tableName outletId servingDate pax paxKids paymentTypeLabel user { id name email mobileNumber isGuest } paymentBreakdown { subtotal totalIncludingTax tax surcharge serviceCharge discount deliveryFee rounding } topLevelItems { id name quantity notes subtotal unitLabel sentAt subItems { id name quantity subtotal unitLabel subItems { id name quantity subtotal } } appliedDiscounts { id value percentValue reason } } hasUnsentItems hasSentItems bills { id capturedAt total paymentType { id label } } capturedAmount uncapturedAmount } } `; // 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 getItems(filter) { const query = ` query GetItems($filter: ItemFilter) { allItems(filter: $filter) { totalCount page perPage items { id type archivedAt identifier label description unitPriceFractional currency isConfigurable horizontalImageUrl brandId tagList reportCategory } } } `; 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 getOutlets() { const query = ` query GetOutlets { outlets { id identifier label archived config } } `; const response = await this.request(query, {}); return response.outlets; } } //# sourceMappingURL=client.js.map