@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
JavaScript
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