@brainerce/mcp-server 3.12.1 → 3.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/http.js +93 -2
- package/dist/bin/stdio.js +93 -2
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +93 -2
- package/dist/index.mjs +93 -2
- package/package.json +1 -1
package/dist/bin/http.js
CHANGED
|
@@ -1782,7 +1782,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1782
1782
|
|
|
1783
1783
|
### Restaurant features (advanced)
|
|
1784
1784
|
|
|
1785
|
-
|
|
1785
|
+
Scheduled availability windows, nested combos (depth \u2264 3 via \`nestedByModifierId\`), and downsell modifiers (negative \`priceDelta\`) are all built on the same data model. See INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products" for those flows.`;
|
|
1786
1786
|
}
|
|
1787
1787
|
function getProductReviewsSection() {
|
|
1788
1788
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3310,6 +3310,47 @@ so customers can track their shipment automatically.
|
|
|
3310
3310
|
|
|
3311
3311
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3312
3312
|
}
|
|
3313
|
+
function getLoyaltySection() {
|
|
3314
|
+
return `# Loyalty & Rewards
|
|
3315
|
+
|
|
3316
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3317
|
+
|
|
3318
|
+
> **Storefront mode only.** These methods work only with \`new BrainerceClient({ storeId })\` + a logged-in \`customerToken\`. There is **no vibe-coded route** \u2014 in \`salesChannelId: 'vc_*'\` mode they throw. Skip loyalty if you connect vibe-coded.
|
|
3319
|
+
|
|
3320
|
+
Points are earned automatically by the platform when an order is paid \u2014 there is no earn call from the storefront. Your job is to **show** balance/rewards and to **redeem**.
|
|
3321
|
+
|
|
3322
|
+
## Status
|
|
3323
|
+
|
|
3324
|
+
\`\`\`typescript
|
|
3325
|
+
// requires client.setCustomerToken(auth.token)
|
|
3326
|
+
const status = await client.getLoyaltyStatus();
|
|
3327
|
+
// { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
|
|
3328
|
+
\`\`\`
|
|
3329
|
+
|
|
3330
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3331
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3332
|
+
|
|
3333
|
+
## Enroll (only if auto-enroll is off)
|
|
3334
|
+
|
|
3335
|
+
\`\`\`typescript
|
|
3336
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3337
|
+
\`\`\`
|
|
3338
|
+
|
|
3339
|
+
## Rewards & redemption
|
|
3340
|
+
|
|
3341
|
+
\`\`\`typescript
|
|
3342
|
+
const rewards = await client.getAvailableRewards();
|
|
3343
|
+
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3344
|
+
|
|
3345
|
+
const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
|
|
3346
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3347
|
+
\`\`\`
|
|
3348
|
+
|
|
3349
|
+
- \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3350
|
+
- Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
|
|
3351
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3352
|
+
`;
|
|
3353
|
+
}
|
|
3313
3354
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3314
3355
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3315
3356
|
const cur = currency || "USD";
|
|
@@ -3336,6 +3377,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3336
3377
|
return getInventorySection();
|
|
3337
3378
|
case "discounts":
|
|
3338
3379
|
return getDiscountsSection();
|
|
3380
|
+
case "loyalty":
|
|
3381
|
+
return getLoyaltySection();
|
|
3339
3382
|
case "recommendations":
|
|
3340
3383
|
return getRecommendationsSection();
|
|
3341
3384
|
case "reviews":
|
|
@@ -3502,6 +3545,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3502
3545
|
"order-confirmation",
|
|
3503
3546
|
"inventory",
|
|
3504
3547
|
"discounts",
|
|
3548
|
+
"loyalty",
|
|
3505
3549
|
"recommendations",
|
|
3506
3550
|
"reviews",
|
|
3507
3551
|
"product-customization-fields",
|
|
@@ -4839,6 +4883,43 @@ interface TaxEstimateResponse {
|
|
|
4839
4883
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4840
4884
|
// full shipping address. The country comes from your edge runtime
|
|
4841
4885
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4886
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4887
|
+
interface LoyaltyStatus {
|
|
4888
|
+
enrolled: boolean;
|
|
4889
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4890
|
+
lifetimeEarned: number;
|
|
4891
|
+
program: {
|
|
4892
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4893
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4894
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4895
|
+
} | null; // null when the store has no loyalty program
|
|
4896
|
+
}
|
|
4897
|
+
|
|
4898
|
+
interface LoyaltyReward {
|
|
4899
|
+
id: string;
|
|
4900
|
+
name: string;
|
|
4901
|
+
description: string | null;
|
|
4902
|
+
pointsCost: number;
|
|
4903
|
+
discountValue: number; // fixed amount off, store currency
|
|
4904
|
+
minOrderAmount: number | null;
|
|
4905
|
+
maxUsesPerUser: number;
|
|
4906
|
+
isActive: boolean;
|
|
4907
|
+
createdAt: string;
|
|
4908
|
+
updatedAt: string;
|
|
4909
|
+
}
|
|
4910
|
+
|
|
4911
|
+
interface RedeemRewardResult {
|
|
4912
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4913
|
+
discountValue: number;
|
|
4914
|
+
pointsSpent: number;
|
|
4915
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4918
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4919
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4920
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4921
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4922
|
+
`;
|
|
4842
4923
|
var TYPES_BY_DOMAIN = {
|
|
4843
4924
|
products: PRODUCTS_TYPES,
|
|
4844
4925
|
cart: CART_TYPES,
|
|
@@ -4851,7 +4932,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4851
4932
|
reviews: REVIEWS_TYPES,
|
|
4852
4933
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4853
4934
|
content: CONTENT_TYPES,
|
|
4854
|
-
regions: REGIONS_TYPES
|
|
4935
|
+
regions: REGIONS_TYPES,
|
|
4936
|
+
loyalty: LOYALTY_TYPES
|
|
4855
4937
|
};
|
|
4856
4938
|
function getTypesByDomain(domain) {
|
|
4857
4939
|
if (domain === "all") {
|
|
@@ -4885,6 +4967,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4885
4967
|
"modifier-groups",
|
|
4886
4968
|
"content",
|
|
4887
4969
|
"regions",
|
|
4970
|
+
"loyalty",
|
|
4888
4971
|
"all"
|
|
4889
4972
|
]).describe(
|
|
4890
4973
|
'The domain of types to retrieve. Use "helpers" for helper function signatures and common types like StoreInfo, PaginatedResponse. Use "content" for FAQ / Footer / Header / Announcement / RichText / Page. Use "regions" for Region / TaxClass admin types.'
|
|
@@ -6331,6 +6414,9 @@ function formatCapabilities(caps) {
|
|
|
6331
6414
|
lines.push(
|
|
6332
6415
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6333
6416
|
);
|
|
6417
|
+
lines.push(
|
|
6418
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6419
|
+
);
|
|
6334
6420
|
lines.push("");
|
|
6335
6421
|
lines.push("## Connection Settings");
|
|
6336
6422
|
lines.push(
|
|
@@ -6376,6 +6462,11 @@ function formatCapabilities(caps) {
|
|
|
6376
6462
|
if (caps.features.hasCoupons) {
|
|
6377
6463
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6378
6464
|
}
|
|
6465
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6466
|
+
suggestions.push(
|
|
6467
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
|
|
6468
|
+
);
|
|
6469
|
+
}
|
|
6379
6470
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6380
6471
|
suggestions.push(
|
|
6381
6472
|
`Checkout custom fields are configured. Build a step between shipping/pickup and payment using getCheckoutCustomFields() + setCheckoutCustomFields(). Render checkout.appliedSurcharges in the order summary so customers see what they're paying for. Use get-code-example("checkout-custom-fields").`
|
package/dist/bin/stdio.js
CHANGED
|
@@ -1779,7 +1779,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1779
1779
|
|
|
1780
1780
|
### Restaurant features (advanced)
|
|
1781
1781
|
|
|
1782
|
-
|
|
1782
|
+
Scheduled availability windows, nested combos (depth \u2264 3 via \`nestedByModifierId\`), and downsell modifiers (negative \`priceDelta\`) are all built on the same data model. See INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products" for those flows.`;
|
|
1783
1783
|
}
|
|
1784
1784
|
function getProductReviewsSection() {
|
|
1785
1785
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3307,6 +3307,47 @@ so customers can track their shipment automatically.
|
|
|
3307
3307
|
|
|
3308
3308
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3309
3309
|
}
|
|
3310
|
+
function getLoyaltySection() {
|
|
3311
|
+
return `# Loyalty & Rewards
|
|
3312
|
+
|
|
3313
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3314
|
+
|
|
3315
|
+
> **Storefront mode only.** These methods work only with \`new BrainerceClient({ storeId })\` + a logged-in \`customerToken\`. There is **no vibe-coded route** \u2014 in \`salesChannelId: 'vc_*'\` mode they throw. Skip loyalty if you connect vibe-coded.
|
|
3316
|
+
|
|
3317
|
+
Points are earned automatically by the platform when an order is paid \u2014 there is no earn call from the storefront. Your job is to **show** balance/rewards and to **redeem**.
|
|
3318
|
+
|
|
3319
|
+
## Status
|
|
3320
|
+
|
|
3321
|
+
\`\`\`typescript
|
|
3322
|
+
// requires client.setCustomerToken(auth.token)
|
|
3323
|
+
const status = await client.getLoyaltyStatus();
|
|
3324
|
+
// { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
|
|
3325
|
+
\`\`\`
|
|
3326
|
+
|
|
3327
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3328
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3329
|
+
|
|
3330
|
+
## Enroll (only if auto-enroll is off)
|
|
3331
|
+
|
|
3332
|
+
\`\`\`typescript
|
|
3333
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3334
|
+
\`\`\`
|
|
3335
|
+
|
|
3336
|
+
## Rewards & redemption
|
|
3337
|
+
|
|
3338
|
+
\`\`\`typescript
|
|
3339
|
+
const rewards = await client.getAvailableRewards();
|
|
3340
|
+
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3341
|
+
|
|
3342
|
+
const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
|
|
3343
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3344
|
+
\`\`\`
|
|
3345
|
+
|
|
3346
|
+
- \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3347
|
+
- Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
|
|
3348
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3349
|
+
`;
|
|
3350
|
+
}
|
|
3310
3351
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3311
3352
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3312
3353
|
const cur = currency || "USD";
|
|
@@ -3333,6 +3374,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3333
3374
|
return getInventorySection();
|
|
3334
3375
|
case "discounts":
|
|
3335
3376
|
return getDiscountsSection();
|
|
3377
|
+
case "loyalty":
|
|
3378
|
+
return getLoyaltySection();
|
|
3336
3379
|
case "recommendations":
|
|
3337
3380
|
return getRecommendationsSection();
|
|
3338
3381
|
case "reviews":
|
|
@@ -3499,6 +3542,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3499
3542
|
"order-confirmation",
|
|
3500
3543
|
"inventory",
|
|
3501
3544
|
"discounts",
|
|
3545
|
+
"loyalty",
|
|
3502
3546
|
"recommendations",
|
|
3503
3547
|
"reviews",
|
|
3504
3548
|
"product-customization-fields",
|
|
@@ -4836,6 +4880,43 @@ interface TaxEstimateResponse {
|
|
|
4836
4880
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4837
4881
|
// full shipping address. The country comes from your edge runtime
|
|
4838
4882
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4883
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4884
|
+
interface LoyaltyStatus {
|
|
4885
|
+
enrolled: boolean;
|
|
4886
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4887
|
+
lifetimeEarned: number;
|
|
4888
|
+
program: {
|
|
4889
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4890
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4891
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4892
|
+
} | null; // null when the store has no loyalty program
|
|
4893
|
+
}
|
|
4894
|
+
|
|
4895
|
+
interface LoyaltyReward {
|
|
4896
|
+
id: string;
|
|
4897
|
+
name: string;
|
|
4898
|
+
description: string | null;
|
|
4899
|
+
pointsCost: number;
|
|
4900
|
+
discountValue: number; // fixed amount off, store currency
|
|
4901
|
+
minOrderAmount: number | null;
|
|
4902
|
+
maxUsesPerUser: number;
|
|
4903
|
+
isActive: boolean;
|
|
4904
|
+
createdAt: string;
|
|
4905
|
+
updatedAt: string;
|
|
4906
|
+
}
|
|
4907
|
+
|
|
4908
|
+
interface RedeemRewardResult {
|
|
4909
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4910
|
+
discountValue: number;
|
|
4911
|
+
pointsSpent: number;
|
|
4912
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4915
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4916
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4917
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4918
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4919
|
+
`;
|
|
4839
4920
|
var TYPES_BY_DOMAIN = {
|
|
4840
4921
|
products: PRODUCTS_TYPES,
|
|
4841
4922
|
cart: CART_TYPES,
|
|
@@ -4848,7 +4929,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4848
4929
|
reviews: REVIEWS_TYPES,
|
|
4849
4930
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4850
4931
|
content: CONTENT_TYPES,
|
|
4851
|
-
regions: REGIONS_TYPES
|
|
4932
|
+
regions: REGIONS_TYPES,
|
|
4933
|
+
loyalty: LOYALTY_TYPES
|
|
4852
4934
|
};
|
|
4853
4935
|
function getTypesByDomain(domain) {
|
|
4854
4936
|
if (domain === "all") {
|
|
@@ -4882,6 +4964,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4882
4964
|
"modifier-groups",
|
|
4883
4965
|
"content",
|
|
4884
4966
|
"regions",
|
|
4967
|
+
"loyalty",
|
|
4885
4968
|
"all"
|
|
4886
4969
|
]).describe(
|
|
4887
4970
|
'The domain of types to retrieve. Use "helpers" for helper function signatures and common types like StoreInfo, PaginatedResponse. Use "content" for FAQ / Footer / Header / Announcement / RichText / Page. Use "regions" for Region / TaxClass admin types.'
|
|
@@ -6328,6 +6411,9 @@ function formatCapabilities(caps) {
|
|
|
6328
6411
|
lines.push(
|
|
6329
6412
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6330
6413
|
);
|
|
6414
|
+
lines.push(
|
|
6415
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6416
|
+
);
|
|
6331
6417
|
lines.push("");
|
|
6332
6418
|
lines.push("## Connection Settings");
|
|
6333
6419
|
lines.push(
|
|
@@ -6373,6 +6459,11 @@ function formatCapabilities(caps) {
|
|
|
6373
6459
|
if (caps.features.hasCoupons) {
|
|
6374
6460
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6375
6461
|
}
|
|
6462
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6463
|
+
suggestions.push(
|
|
6464
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
|
|
6465
|
+
);
|
|
6466
|
+
}
|
|
6376
6467
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6377
6468
|
suggestions.push(
|
|
6378
6469
|
`Checkout custom fields are configured. Build a step between shipping/pickup and payment using getCheckoutCustomFields() + setCheckoutCustomFields(). Render checkout.appliedSurcharges in the order summary so customers see what they're paying for. Use get-code-example("checkout-custom-fields").`
|
package/dist/index.d.mts
CHANGED
|
@@ -75,6 +75,8 @@ interface StoreCapabilities {
|
|
|
75
75
|
hasCheckoutCustomFields: boolean;
|
|
76
76
|
/** True when at least one PUBLISHED Content row exists for the store. */
|
|
77
77
|
hasContent?: boolean;
|
|
78
|
+
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
|
+
hasLoyaltyProgram?: boolean;
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ interface StoreCapabilities {
|
|
|
75
75
|
hasCheckoutCustomFields: boolean;
|
|
76
76
|
/** True when at least one PUBLISHED Content row exists for the store. */
|
|
77
77
|
hasContent?: boolean;
|
|
78
|
+
/** True when the store has an ACTIVE loyalty program. Storefront-mode only. */
|
|
79
|
+
hasLoyaltyProgram?: boolean;
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
declare function fetchStoreCapabilities(connectionId: string, baseUrl?: string): Promise<StoreCapabilities>;
|
package/dist/index.js
CHANGED
|
@@ -1805,7 +1805,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1805
1805
|
|
|
1806
1806
|
### Restaurant features (advanced)
|
|
1807
1807
|
|
|
1808
|
-
|
|
1808
|
+
Scheduled availability windows, nested combos (depth \u2264 3 via \`nestedByModifierId\`), and downsell modifiers (negative \`priceDelta\`) are all built on the same data model. See INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products" for those flows.`;
|
|
1809
1809
|
}
|
|
1810
1810
|
function getProductReviewsSection() {
|
|
1811
1811
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3333,6 +3333,47 @@ so customers can track their shipment automatically.
|
|
|
3333
3333
|
|
|
3334
3334
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3335
3335
|
}
|
|
3336
|
+
function getLoyaltySection() {
|
|
3337
|
+
return `# Loyalty & Rewards
|
|
3338
|
+
|
|
3339
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3340
|
+
|
|
3341
|
+
> **Storefront mode only.** These methods work only with \`new BrainerceClient({ storeId })\` + a logged-in \`customerToken\`. There is **no vibe-coded route** \u2014 in \`salesChannelId: 'vc_*'\` mode they throw. Skip loyalty if you connect vibe-coded.
|
|
3342
|
+
|
|
3343
|
+
Points are earned automatically by the platform when an order is paid \u2014 there is no earn call from the storefront. Your job is to **show** balance/rewards and to **redeem**.
|
|
3344
|
+
|
|
3345
|
+
## Status
|
|
3346
|
+
|
|
3347
|
+
\`\`\`typescript
|
|
3348
|
+
// requires client.setCustomerToken(auth.token)
|
|
3349
|
+
const status = await client.getLoyaltyStatus();
|
|
3350
|
+
// { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
|
|
3351
|
+
\`\`\`
|
|
3352
|
+
|
|
3353
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3354
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3355
|
+
|
|
3356
|
+
## Enroll (only if auto-enroll is off)
|
|
3357
|
+
|
|
3358
|
+
\`\`\`typescript
|
|
3359
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3360
|
+
\`\`\`
|
|
3361
|
+
|
|
3362
|
+
## Rewards & redemption
|
|
3363
|
+
|
|
3364
|
+
\`\`\`typescript
|
|
3365
|
+
const rewards = await client.getAvailableRewards();
|
|
3366
|
+
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3367
|
+
|
|
3368
|
+
const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
|
|
3369
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3370
|
+
\`\`\`
|
|
3371
|
+
|
|
3372
|
+
- \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3373
|
+
- Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
|
|
3374
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3375
|
+
`;
|
|
3376
|
+
}
|
|
3336
3377
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3337
3378
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3338
3379
|
const cur = currency || "USD";
|
|
@@ -3359,6 +3400,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3359
3400
|
return getInventorySection();
|
|
3360
3401
|
case "discounts":
|
|
3361
3402
|
return getDiscountsSection();
|
|
3403
|
+
case "loyalty":
|
|
3404
|
+
return getLoyaltySection();
|
|
3362
3405
|
case "recommendations":
|
|
3363
3406
|
return getRecommendationsSection();
|
|
3364
3407
|
case "reviews":
|
|
@@ -3525,6 +3568,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3525
3568
|
"order-confirmation",
|
|
3526
3569
|
"inventory",
|
|
3527
3570
|
"discounts",
|
|
3571
|
+
"loyalty",
|
|
3528
3572
|
"recommendations",
|
|
3529
3573
|
"reviews",
|
|
3530
3574
|
"product-customization-fields",
|
|
@@ -4862,6 +4906,43 @@ interface TaxEstimateResponse {
|
|
|
4862
4906
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4863
4907
|
// full shipping address. The country comes from your edge runtime
|
|
4864
4908
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4909
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4910
|
+
interface LoyaltyStatus {
|
|
4911
|
+
enrolled: boolean;
|
|
4912
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4913
|
+
lifetimeEarned: number;
|
|
4914
|
+
program: {
|
|
4915
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4916
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4917
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4918
|
+
} | null; // null when the store has no loyalty program
|
|
4919
|
+
}
|
|
4920
|
+
|
|
4921
|
+
interface LoyaltyReward {
|
|
4922
|
+
id: string;
|
|
4923
|
+
name: string;
|
|
4924
|
+
description: string | null;
|
|
4925
|
+
pointsCost: number;
|
|
4926
|
+
discountValue: number; // fixed amount off, store currency
|
|
4927
|
+
minOrderAmount: number | null;
|
|
4928
|
+
maxUsesPerUser: number;
|
|
4929
|
+
isActive: boolean;
|
|
4930
|
+
createdAt: string;
|
|
4931
|
+
updatedAt: string;
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4934
|
+
interface RedeemRewardResult {
|
|
4935
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4936
|
+
discountValue: number;
|
|
4937
|
+
pointsSpent: number;
|
|
4938
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4941
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4942
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4943
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4944
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4945
|
+
`;
|
|
4865
4946
|
var TYPES_BY_DOMAIN = {
|
|
4866
4947
|
products: PRODUCTS_TYPES,
|
|
4867
4948
|
cart: CART_TYPES,
|
|
@@ -4874,7 +4955,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4874
4955
|
reviews: REVIEWS_TYPES,
|
|
4875
4956
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4876
4957
|
content: CONTENT_TYPES,
|
|
4877
|
-
regions: REGIONS_TYPES
|
|
4958
|
+
regions: REGIONS_TYPES,
|
|
4959
|
+
loyalty: LOYALTY_TYPES
|
|
4878
4960
|
};
|
|
4879
4961
|
function getTypesByDomain(domain) {
|
|
4880
4962
|
if (domain === "all") {
|
|
@@ -4908,6 +4990,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4908
4990
|
"modifier-groups",
|
|
4909
4991
|
"content",
|
|
4910
4992
|
"regions",
|
|
4993
|
+
"loyalty",
|
|
4911
4994
|
"all"
|
|
4912
4995
|
]).describe(
|
|
4913
4996
|
'The domain of types to retrieve. Use "helpers" for helper function signatures and common types like StoreInfo, PaginatedResponse. Use "content" for FAQ / Footer / Header / Announcement / RichText / Page. Use "regions" for Region / TaxClass admin types.'
|
|
@@ -6354,6 +6437,9 @@ function formatCapabilities(caps) {
|
|
|
6354
6437
|
lines.push(
|
|
6355
6438
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6356
6439
|
);
|
|
6440
|
+
lines.push(
|
|
6441
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6442
|
+
);
|
|
6357
6443
|
lines.push("");
|
|
6358
6444
|
lines.push("## Connection Settings");
|
|
6359
6445
|
lines.push(
|
|
@@ -6399,6 +6485,11 @@ function formatCapabilities(caps) {
|
|
|
6399
6485
|
if (caps.features.hasCoupons) {
|
|
6400
6486
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6401
6487
|
}
|
|
6488
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6489
|
+
suggestions.push(
|
|
6490
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6402
6493
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6403
6494
|
suggestions.push(
|
|
6404
6495
|
`Checkout custom fields are configured. Build a step between shipping/pickup and payment using getCheckoutCustomFields() + setCheckoutCustomFields(). Render checkout.appliedSurcharges in the order summary so customers see what they're paying for. Use get-code-example("checkout-custom-fields").`
|
package/dist/index.mjs
CHANGED
|
@@ -1773,7 +1773,7 @@ The merchant can hide a group entirely for one variant by setting \`maxOverride:
|
|
|
1773
1773
|
|
|
1774
1774
|
### Restaurant features (advanced)
|
|
1775
1775
|
|
|
1776
|
-
|
|
1776
|
+
Scheduled availability windows, nested combos (depth \u2264 3 via \`nestedByModifierId\`), and downsell modifiers (negative \`priceDelta\`) are all built on the same data model. See INTEGRATION-OPTIONAL.md "Restaurant / build-your-own products" for those flows.`;
|
|
1777
1777
|
}
|
|
1778
1778
|
function getProductReviewsSection() {
|
|
1779
1779
|
return `## Product Reviews (\u2605 ratings + JSON-LD for SEO)
|
|
@@ -3301,6 +3301,47 @@ so customers can track their shipment automatically.
|
|
|
3301
3301
|
|
|
3302
3302
|
**SDK mode:** \`createShippingLabel()\` requires \`apiKey\` (admin mode only).`;
|
|
3303
3303
|
}
|
|
3304
|
+
function getLoyaltySection() {
|
|
3305
|
+
return `# Loyalty & Rewards
|
|
3306
|
+
|
|
3307
|
+
Logged-in customers earn points on paid orders and redeem them for a one-time discount coupon at checkout.
|
|
3308
|
+
|
|
3309
|
+
> **Storefront mode only.** These methods work only with \`new BrainerceClient({ storeId })\` + a logged-in \`customerToken\`. There is **no vibe-coded route** \u2014 in \`salesChannelId: 'vc_*'\` mode they throw. Skip loyalty if you connect vibe-coded.
|
|
3310
|
+
|
|
3311
|
+
Points are earned automatically by the platform when an order is paid \u2014 there is no earn call from the storefront. Your job is to **show** balance/rewards and to **redeem**.
|
|
3312
|
+
|
|
3313
|
+
## Status
|
|
3314
|
+
|
|
3315
|
+
\`\`\`typescript
|
|
3316
|
+
// requires client.setCustomerToken(auth.token)
|
|
3317
|
+
const status = await client.getLoyaltyStatus();
|
|
3318
|
+
// { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
|
|
3319
|
+
\`\`\`
|
|
3320
|
+
|
|
3321
|
+
- \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
|
|
3322
|
+
- \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
|
|
3323
|
+
|
|
3324
|
+
## Enroll (only if auto-enroll is off)
|
|
3325
|
+
|
|
3326
|
+
\`\`\`typescript
|
|
3327
|
+
await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
|
|
3328
|
+
\`\`\`
|
|
3329
|
+
|
|
3330
|
+
## Rewards & redemption
|
|
3331
|
+
|
|
3332
|
+
\`\`\`typescript
|
|
3333
|
+
const rewards = await client.getAvailableRewards();
|
|
3334
|
+
// [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
|
|
3335
|
+
|
|
3336
|
+
const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
|
|
3337
|
+
await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
|
|
3338
|
+
\`\`\`
|
|
3339
|
+
|
|
3340
|
+
- \`redeemLoyaltyReward\` mints a one-time fixed-amount coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
|
|
3341
|
+
- Throws if the customer has insufficient points. If minting fails, points are refunded automatically.
|
|
3342
|
+
- Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
|
|
3343
|
+
`;
|
|
3344
|
+
}
|
|
3304
3345
|
function getSectionByTopic(topic, connectionId, currency) {
|
|
3305
3346
|
const cid = connectionId || "vc_YOUR_CONNECTION_ID";
|
|
3306
3347
|
const cur = currency || "USD";
|
|
@@ -3327,6 +3368,8 @@ function getSectionByTopic(topic, connectionId, currency) {
|
|
|
3327
3368
|
return getInventorySection();
|
|
3328
3369
|
case "discounts":
|
|
3329
3370
|
return getDiscountsSection();
|
|
3371
|
+
case "loyalty":
|
|
3372
|
+
return getLoyaltySection();
|
|
3330
3373
|
case "recommendations":
|
|
3331
3374
|
return getRecommendationsSection();
|
|
3332
3375
|
case "reviews":
|
|
@@ -3493,6 +3536,7 @@ var GET_SDK_DOCS_SCHEMA = {
|
|
|
3493
3536
|
"order-confirmation",
|
|
3494
3537
|
"inventory",
|
|
3495
3538
|
"discounts",
|
|
3539
|
+
"loyalty",
|
|
3496
3540
|
"recommendations",
|
|
3497
3541
|
"reviews",
|
|
3498
3542
|
"product-customization-fields",
|
|
@@ -4830,6 +4874,43 @@ interface TaxEstimateResponse {
|
|
|
4830
4874
|
// Non-binding by design: the authoritative tax runs at checkout against the
|
|
4831
4875
|
// full shipping address. The country comes from your edge runtime
|
|
4832
4876
|
// (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
|
|
4877
|
+
var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
|
|
4878
|
+
interface LoyaltyStatus {
|
|
4879
|
+
enrolled: boolean;
|
|
4880
|
+
pointsBalance: number; // redeemable points (pending earns excluded)
|
|
4881
|
+
lifetimeEarned: number;
|
|
4882
|
+
program: {
|
|
4883
|
+
pointsName: string; // e.g. "points", "coins"
|
|
4884
|
+
currencyRatio: number; // points needed to redeem 1 unit of store currency
|
|
4885
|
+
status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
|
|
4886
|
+
} | null; // null when the store has no loyalty program
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4889
|
+
interface LoyaltyReward {
|
|
4890
|
+
id: string;
|
|
4891
|
+
name: string;
|
|
4892
|
+
description: string | null;
|
|
4893
|
+
pointsCost: number;
|
|
4894
|
+
discountValue: number; // fixed amount off, store currency
|
|
4895
|
+
minOrderAmount: number | null;
|
|
4896
|
+
maxUsesPerUser: number;
|
|
4897
|
+
isActive: boolean;
|
|
4898
|
+
createdAt: string;
|
|
4899
|
+
updatedAt: string;
|
|
4900
|
+
}
|
|
4901
|
+
|
|
4902
|
+
interface RedeemRewardResult {
|
|
4903
|
+
couponCode: string; // one-time coupon to apply at checkout
|
|
4904
|
+
discountValue: number;
|
|
4905
|
+
pointsSpent: number;
|
|
4906
|
+
pointsBalance: number; // remaining balance after redemption
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
// client.getLoyaltyStatus(): Promise<LoyaltyStatus>
|
|
4910
|
+
// client.enrollInLoyalty(): Promise<LoyaltyStatus>
|
|
4911
|
+
// client.getAvailableRewards(): Promise<LoyaltyReward[]>
|
|
4912
|
+
// client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
|
|
4913
|
+
`;
|
|
4833
4914
|
var TYPES_BY_DOMAIN = {
|
|
4834
4915
|
products: PRODUCTS_TYPES,
|
|
4835
4916
|
cart: CART_TYPES,
|
|
@@ -4842,7 +4923,8 @@ var TYPES_BY_DOMAIN = {
|
|
|
4842
4923
|
reviews: REVIEWS_TYPES,
|
|
4843
4924
|
"modifier-groups": MODIFIER_GROUPS_TYPES,
|
|
4844
4925
|
content: CONTENT_TYPES,
|
|
4845
|
-
regions: REGIONS_TYPES
|
|
4926
|
+
regions: REGIONS_TYPES,
|
|
4927
|
+
loyalty: LOYALTY_TYPES
|
|
4846
4928
|
};
|
|
4847
4929
|
function getTypesByDomain(domain) {
|
|
4848
4930
|
if (domain === "all") {
|
|
@@ -4876,6 +4958,7 @@ var GET_TYPE_DEFINITIONS_SCHEMA = {
|
|
|
4876
4958
|
"modifier-groups",
|
|
4877
4959
|
"content",
|
|
4878
4960
|
"regions",
|
|
4961
|
+
"loyalty",
|
|
4879
4962
|
"all"
|
|
4880
4963
|
]).describe(
|
|
4881
4964
|
'The domain of types to retrieve. Use "helpers" for helper function signatures and common types like StoreInfo, PaginatedResponse. Use "content" for FAQ / Footer / Header / Announcement / RichText / Page. Use "regions" for Region / TaxClass admin types.'
|
|
@@ -6322,6 +6405,9 @@ function formatCapabilities(caps) {
|
|
|
6322
6405
|
lines.push(
|
|
6323
6406
|
caps.features.hasCheckoutCustomFields ? "\u2705 Checkout custom fields configured (surcharges may apply)" : "\u274C No checkout custom fields"
|
|
6324
6407
|
);
|
|
6408
|
+
lines.push(
|
|
6409
|
+
caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
|
|
6410
|
+
);
|
|
6325
6411
|
lines.push("");
|
|
6326
6412
|
lines.push("## Connection Settings");
|
|
6327
6413
|
lines.push(
|
|
@@ -6367,6 +6453,11 @@ function formatCapabilities(caps) {
|
|
|
6367
6453
|
if (caps.features.hasCoupons) {
|
|
6368
6454
|
suggestions.push('Add a coupon input to the cart. Use get-code-example("coupon-input").');
|
|
6369
6455
|
}
|
|
6456
|
+
if (caps.features.hasLoyaltyProgram) {
|
|
6457
|
+
suggestions.push(
|
|
6458
|
+
"A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus(), getAvailableRewards(), and redeemLoyaltyReward(id). Note: loyalty is NOT available in vibe-coded mode."
|
|
6459
|
+
);
|
|
6460
|
+
}
|
|
6370
6461
|
if (caps.features.hasCheckoutCustomFields) {
|
|
6371
6462
|
suggestions.push(
|
|
6372
6463
|
`Checkout custom fields are configured. Build a step between shipping/pickup and payment using getCheckoutCustomFields() + setCheckoutCustomFields(). Render checkout.appliedSurcharges in the order summary so customers see what they're paying for. Use get-code-example("checkout-custom-fields").`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainerce/mcp-server",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.2",
|
|
4
4
|
"packageManager": "pnpm@9.0.0",
|
|
5
5
|
"description": "Framework-agnostic domain knowledge API for Brainerce. Provides SDK docs, types, business flows, critical rules, and store capabilities to AI tools like Lovable, Cursor, Bolt, v0, and Claude Code. Does NOT provide framework-specific boilerplate — clients build in whatever framework fits.",
|
|
6
6
|
"bin": {
|