@brainerce/mcp-server 3.12.2 → 3.12.4

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/index.mjs CHANGED
@@ -211,7 +211,7 @@ async function startCheckout() {
211
211
  2. Customer optionally applies coupon on cart page \u2192 \`applyCoupon(cartId, code)\`
212
212
  3. Detect payment providers \u2192 \`getPaymentProviders()\`
213
213
  4. Start checkout session (\`startGuestCheckout()\` or \`createCheckout()\`)
214
- 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\`
214
+ 5. Set shipping address (includes required email) \u2192 \`setShippingAddress()\` \u2014 also pass \`notes\` from the **"Order notes" textarea that every checkout page should include by default** (optional field, max 2000 chars; lands on the order for the merchant)
215
215
  6. Select shipping method \u2192 \`selectShippingMethod()\`
216
216
  6b. (Optional) Set checkout custom fields \u2192 \`setCheckoutCustomFields()\` \u2014 surcharges auto-calculated
217
217
  7. Create payment intent \u2192 \`createPaymentIntent()\` \u2192 returns \`{ clientSecret, provider }\`
@@ -233,6 +233,8 @@ function CheckoutPage() {
233
233
  const [checkout, setCheckout] = useState<Checkout | null>(null);
234
234
  const [error, setError] = useState<string | null>(null);
235
235
  const [loading, setLoading] = useState(true);
236
+ // Optional "Order notes" textarea \u2014 render it on the checkout page by default
237
+ const [orderNotes, setOrderNotes] = useState('');
236
238
 
237
239
  useEffect(() => { startCheckout(); }, []);
238
240
 
@@ -267,6 +269,7 @@ function CheckoutPage() {
267
269
  postalCode: cart.shippingAddress.postalCode,
268
270
  country: cart.shippingAddress.country,
269
271
  phone: cart.shippingAddress.phone,
272
+ notes: orderNotes || undefined, // from the "Order notes" textarea \u2014 include one by default
270
273
  });
271
274
  setCheckout(checkoutData);
272
275
 
@@ -2117,6 +2120,7 @@ A useful order card includes ALL of the following when the data is present. Each
2117
2120
  | **Tracking** | \`order.trackingNumber\`, \`order.trackingUrl\`, \`order.carrier\`, \`order.shippedAt\`, \`order.deliveredAt\` | Link out to \`trackingUrl\` when set. |
2118
2121
  | **Payment** | \`order.paymentMethod\`, \`order.financialStatus\` | Badge \`financialStatus\` (paid / pending / refunded / partially_refunded). |
2119
2122
  | Downloads | \`order.hasDownloads\` \u2192 \`client.getOrderDownloads(id)\` | Separate call; returns \`OrderDownloadLink[]\`. |
2123
+ | **Order note** | \`order.notes\` | The shopper's own checkout note, echoed back read-only. Render when present ("Your order note"). |
2120
2124
  | Financial summary | \`order.subtotal\`, \`order.appliedDiscounts\`, \`order.couponCode\` + \`couponDiscount\`, \`order.shippingAmount\`, \`order.taxAmount\`, \`order.totalAmount\` | Breakdown rows + final total. In VAT-inclusive mode \`taxAmount\` is 0 \u2014 read \`order.taxBreakdown.totalTax\` and label "Tax (incl.)". |
2121
2125
 
2122
2126
  #### Rendering \`order.items[i].customizations\` by type
@@ -2142,7 +2146,6 @@ The storefront scaffolded by \`create-brainerce-store\` already implements this.
2142
2146
  These fields are NOT returned to buyers by \`/customers/me/orders\`. If you see them in older examples, ignore them \u2014 they are for merchant/admin views only.
2143
2147
 
2144
2148
  - \`order.accountId\`, \`order.storeId\`, \`order.customerId\` (already known on the request)
2145
- - \`order.notes\` (internal merchant notes)
2146
2149
  - \`order.customFieldValues\` (order-level checkout fields \u2014 merchant concept; buyers see \`items[i].customizations\`)
2147
2150
  - \`order.appliedSurcharges\`, \`order.surchargeAmount\`, \`order.appliedRuleIds\`, \`order.downloadMeta\`, \`order.pickupLocationData\``;
2148
2151
  }
@@ -2190,7 +2193,22 @@ function OrderConfirmation() {
2190
2193
 
2191
2194
  **Common mistake:** Don't just check URL params and show success immediately!
2192
2195
  You MUST call \`waitForOrder(checkoutId)\` to verify the order was created.
2193
- Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.`;
2196
+ Also call \`handlePaymentSuccess(checkoutId)\` to clear purchased items from the cart.
2197
+
2198
+ ### Optional: render full order details on the confirmation page
2199
+
2200
+ Want a richer thank-you page (items, shipping address, totals, the shopper's
2201
+ order note)? After \`waitForOrder\` succeeds, fetch the full buyer-safe order \u2014
2202
+ works for guests too (possession of the checkout id is the credential):
2203
+
2204
+ \`\`\`typescript
2205
+ const order = await client.getOrderByCheckout(checkoutId);
2206
+ // order.items, order.shippingAddress, order.notes, order.subtotal,
2207
+ // order.shippingAmount, order.taxAmount / order.taxBreakdown, order.totalAmount
2208
+ \`\`\`
2209
+
2210
+ Render whatever subset fits your design \u2014 same field shapes as the buyer
2211
+ order-history view (see "Buyer order view" section).`;
2194
2212
  }
2195
2213
  function getI18nSection() {
2196
2214
  return `## Internationalization (i18n)
@@ -2520,6 +2538,8 @@ dashboard or the admin SDK).
2520
2538
 
2521
2539
  \`\`\`typescript
2522
2540
  // Publish to a specific vibe-coded site (admin mode):
2541
+ await admin.publishProductToSalesChannel('prod_id', 'vc_conn_id');
2542
+ await admin.publishCouponToSalesChannel('coupon_id', 'vc_conn_id');
2523
2543
  await admin.publishCategoryToVibeCodedSite('cat_id', 'conn_id');
2524
2544
  await admin.publishTagToVibeCodedSite('tag_id', 'conn_id');
2525
2545
  await admin.publishBrandToVibeCodedSite('brand_id', 'conn_id');
@@ -3315,11 +3335,18 @@ Points are earned automatically by the platform when an order is paid \u2014 the
3315
3335
  \`\`\`typescript
3316
3336
  // requires client.setCustomerToken(auth.token)
3317
3337
  const status = await client.getLoyaltyStatus();
3318
- // { enrolled, pointsBalance, lifetimeEarned, program: { pointsName, currencyRatio, status } | null }
3338
+ // {
3339
+ // enrolled, pointsBalance, lifetimeEarned,
3340
+ // program: { pointsName, currencyRatio, status } | null,
3341
+ // tier: { id, name, level, pointsMultiplier } | null,
3342
+ // nextTier: { id, name, level, pointsMultiplier, qualificationType, qualificationThreshold } | null,
3343
+ // progressToNextTier, pointsToNextTier,
3344
+ // }
3319
3345
  \`\`\`
3320
3346
 
3321
3347
  - \`program === null\` \u2192 the store has no loyalty program; hide the loyalty UI.
3322
3348
  - \`program.status !== 'ACTIVE'\` \u2192 program paused/draft; hide the loyalty UI.
3349
+ - \`tier\`/\`nextTier\` are \`null\` when the store hasn't configured tiers, or the customer hasn't qualified for one yet.
3323
3350
 
3324
3351
  ## Enroll (only if auto-enroll is off)
3325
3352
 
@@ -3327,19 +3354,67 @@ const status = await client.getLoyaltyStatus();
3327
3354
  await client.enrollInLoyalty(); // same status shape; no-op if already enrolled
3328
3355
  \`\`\`
3329
3356
 
3357
+ May grant a one-time "joined the program" bonus if configured \u2014 distinct from any account-signup bonus (granted automatically on registration, not by this call).
3358
+
3330
3359
  ## Rewards & redemption
3331
3360
 
3332
3361
  \`\`\`typescript
3333
3362
  const rewards = await client.getAvailableRewards();
3334
- // [{ id, name, description, pointsCost, discountValue, minOrderAmount, maxUsesPerUser, isActive }]
3363
+ // [{ id, name, description, pointsCost, type, discountValue, minOrderAmount, maxUsesPerUser, minTierLevel, isActive }]
3364
+ // Already filtered to rewards the customer's current tier qualifies for.
3335
3365
 
3336
- const { couponCode, discountValue, pointsBalance } = await client.redeemLoyaltyReward(rewardId);
3366
+ const { couponCode, discountType, discountValue, pointsBalance } =
3367
+ await client.redeemLoyaltyReward(rewardId);
3337
3368
  await client.applyCoupon(cartId, couponCode); // applies via the normal coupon engine
3338
3369
  \`\`\`
3339
3370
 
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.
3371
+ - \`reward.type\` is \`'FIXED_DISCOUNT'\` (currency amount) or \`'PERCENT_DISCOUNT'\` (0-100 percent) \u2014 the redeem result's \`discountType\` matches.
3372
+ - \`redeemLoyaltyReward\` mints a one-time coupon (\`usageLimit: 1\`). Apply it with the normal \`applyCoupon\` flow.
3373
+ - Throws if the customer has insufficient points, or their tier is below \`reward.minTierLevel\`. If minting fails, points are refunded automatically.
3342
3374
  - Disable a reward's redeem button when \`pointsBalance < reward.pointsCost\`.
3375
+
3376
+ ## Social share (optional)
3377
+
3378
+ \`\`\`typescript
3379
+ await client.reportSocialShare('instagram'); // platform optional, self-reported, once per customer
3380
+ \`\`\`
3381
+
3382
+ ## Referrals (when the store enables them)
3383
+
3384
+ \`getLoyaltyStatus()\` also returns \`referralCode\` (the member's share code, null when referrals are disabled or the customer isn't enrolled) and \`referralWelcomeCoupon\` (the still-unused welcome coupon this customer got for signing up via a referral link).
3385
+
3386
+ \`\`\`typescript
3387
+ // 1. Referrer: build a share link around their code.
3388
+ const { referralCode } = await client.getLoyaltyStatus();
3389
+ const shareUrl = \`https://your-store.com/?ref=\${referralCode}\`;
3390
+
3391
+ // 2. Landing page: PUBLIC lookup \u2014 no customerToken needed.
3392
+ const info = await client.getReferralInfo(refFromQuery);
3393
+ // { valid, referrerFirstName, reward: { name, type, value, minOrderAmount } | null }
3394
+ if (info.valid) {
3395
+ // "Jane sent you a gift: 10% off your first order"
3396
+ }
3397
+
3398
+ // 3. Registration: pass the code \u2014 invalid codes never fail registration
3399
+ // (validated asynchronously server-side with fraud checks).
3400
+ await client.registerCustomer({ email, password, referralCode: refFromQuery });
3401
+
3402
+ // 4. Show the referee their welcome coupon after login:
3403
+ const { referralWelcomeCoupon } = await client.getLoyaltyStatus();
3404
+ if (referralWelcomeCoupon) {
3405
+ await client.applyCoupon(cartId, referralWelcomeCoupon.code);
3406
+ }
3407
+ \`\`\`
3408
+
3409
+ The referrer earns a points bonus after the referee's first qualifying order (held through the program's pending window, like order points). Everything server-side \u2014 no storefront earn calls.
3410
+
3411
+ ## Birthday gifts (when the store enables them)
3412
+
3413
+ No dedicated calls \u2014 collect \`birthMonth\`/\`birthDay\` (1-12 / 1-31, **no year**, must be sent together) via \`updateMyProfile()\` and the platform emails a one-time gift coupon ahead of the customer's birthday.
3414
+
3415
+ \`\`\`typescript
3416
+ await client.updateMyProfile({ birthMonth: 4, birthDay: 17 });
3417
+ \`\`\`
3343
3418
  `;
3344
3419
  }
3345
3420
  function getSectionByTopic(topic, connectionId, currency) {
@@ -3864,6 +3939,7 @@ interface Checkout {
3864
3939
  appliedSurcharges?: Array<{ key: string; name: string; value: unknown; amount: string }> | null;
3865
3940
  customFieldValues?: Record<string, unknown> | null;
3866
3941
  couponCode?: string | null;
3942
+ notes?: string | null; // Order-level shopper note \u2014 copied onto the order at completion
3867
3943
  lineItems: CheckoutLineItem[]; // Use THIS for order summary, NOT cart.items!
3868
3944
  itemCount: number;
3869
3945
  availableShippingRates?: ShippingRate[];
@@ -3932,6 +4008,7 @@ interface SetShippingAddressDto {
3932
4008
  postalCode: string;
3933
4009
  country: string;
3934
4010
  phone?: string;
4011
+ notes?: string; // Order notes textarea \u2014 include one on checkout by default! Max 2000 chars, lands on the order
3935
4012
  }
3936
4013
 
3937
4014
  interface CreateCheckoutDto {
@@ -4875,6 +4952,18 @@ interface TaxEstimateResponse {
4875
4952
  // full shipping address. The country comes from your edge runtime
4876
4953
  // (CF-IPCountry / request.geo.country / etc.), NOT the request IP.`;
4877
4954
  var LOYALTY_TYPES = `// ---- Loyalty & Rewards (storefront mode only) ----
4955
+ interface LoyaltyTierSummary {
4956
+ id: string;
4957
+ name: string;
4958
+ level: number; // ordering; higher = better tier
4959
+ pointsMultiplier: number;
4960
+ }
4961
+
4962
+ interface LoyaltyNextTierSummary extends LoyaltyTierSummary {
4963
+ qualificationType: 'SPEND' | 'POINTS';
4964
+ qualificationThreshold: number;
4965
+ }
4966
+
4878
4967
  interface LoyaltyStatus {
4879
4968
  enrolled: boolean;
4880
4969
  pointsBalance: number; // redeemable points (pending earns excluded)
@@ -4884,6 +4973,28 @@ interface LoyaltyStatus {
4884
4973
  currencyRatio: number; // points needed to redeem 1 unit of store currency
4885
4974
  status: 'DRAFT' | 'ACTIVE' | 'PAUSED';
4886
4975
  } | null; // null when the store has no loyalty program
4976
+ tier: LoyaltyTierSummary | null; // null if untiered / no tiers configured
4977
+ nextTier: LoyaltyNextTierSummary | null; // null if at the top tier already
4978
+ progressToNextTier: number; // 0..1
4979
+ pointsToNextTier: number | null;
4980
+ referralCode?: string | null; // member's share code; null when referrals disabled / not enrolled
4981
+ referralWelcomeCoupon?: { // unused welcome coupon from signing up via a referral link
4982
+ code: string;
4983
+ type: string; // 'PERCENTAGE' | 'FIXED_AMOUNT'
4984
+ value: number;
4985
+ } | null;
4986
+ }
4987
+
4988
+ // Public referral-link lookup \u2014 NO customerToken needed (safe pre-signup)
4989
+ interface ReferralInfo {
4990
+ valid: boolean; // false for unknown/disabled codes
4991
+ referrerFirstName: string | null; // for "Jane sent you a gift" copy \u2014 no other PII
4992
+ reward: {
4993
+ name: string;
4994
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
4995
+ value: number;
4996
+ minOrderAmount: number | null;
4997
+ } | null;
4887
4998
  }
4888
4999
 
4889
5000
  interface LoyaltyReward {
@@ -4891,16 +5002,19 @@ interface LoyaltyReward {
4891
5002
  name: string;
4892
5003
  description: string | null;
4893
5004
  pointsCost: number;
4894
- discountValue: number; // fixed amount off, store currency
5005
+ type: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
5006
+ discountValue: number; // currency amount (FIXED_DISCOUNT) or 0-100 percent (PERCENT_DISCOUNT)
4895
5007
  minOrderAmount: number | null;
4896
5008
  maxUsesPerUser: number;
4897
5009
  isActive: boolean;
5010
+ minTierLevel: number | null; // minimum tier level required, or null for no restriction
4898
5011
  createdAt: string;
4899
5012
  updatedAt: string;
4900
5013
  }
4901
5014
 
4902
5015
  interface RedeemRewardResult {
4903
5016
  couponCode: string; // one-time coupon to apply at checkout
5017
+ discountType: 'FIXED_DISCOUNT' | 'PERCENT_DISCOUNT';
4904
5018
  discountValue: number;
4905
5019
  pointsSpent: number;
4906
5020
  pointsBalance: number; // remaining balance after redemption
@@ -4910,6 +5024,10 @@ interface RedeemRewardResult {
4910
5024
  // client.enrollInLoyalty(): Promise<LoyaltyStatus>
4911
5025
  // client.getAvailableRewards(): Promise<LoyaltyReward[]>
4912
5026
  // client.redeemLoyaltyReward(rewardId: string): Promise<RedeemRewardResult>
5027
+ // client.reportSocialShare(platform?: string): Promise<{ awarded: boolean; points: number }>
5028
+ // client.getReferralInfo(code: string): Promise<ReferralInfo> // PUBLIC \u2014 no customerToken
5029
+ // Referral signup: client.registerCustomer({ ..., referralCode })
5030
+ // Birthday gift data: client.updateMyProfile({ birthMonth, birthDay }) // 1-12 / 1-31, no year, both together
4913
5031
  `;
4914
5032
  var TYPES_BY_DOMAIN = {
4915
5033
  products: PRODUCTS_TYPES,
@@ -6408,6 +6526,14 @@ function formatCapabilities(caps) {
6408
6526
  lines.push(
6409
6527
  caps.features.hasLoyaltyProgram ? "\u2705 Loyalty program active (points & rewards \u2014 storefront-mode only)" : "\u274C No loyalty program"
6410
6528
  );
6529
+ if (caps.features.hasLoyaltyProgram) {
6530
+ lines.push(
6531
+ caps.features.hasReferralProgram ? "\u2705 Referral program enabled (share codes + welcome reward \u2014 storefront-mode only)" : "\u274C Referral program not enabled"
6532
+ );
6533
+ lines.push(
6534
+ caps.features.hasBirthdayRewards ? "\u2705 Birthday gifts enabled (set birthMonth/birthDay via updateMyProfile)" : "\u274C Birthday gifts not enabled"
6535
+ );
6536
+ }
6411
6537
  lines.push("");
6412
6538
  lines.push("## Connection Settings");
6413
6539
  lines.push(
@@ -6455,7 +6581,7 @@ function formatCapabilities(caps) {
6455
6581
  }
6456
6582
  if (caps.features.hasLoyaltyProgram) {
6457
6583
  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."
6584
+ "A loyalty program is active. In a storefront-mode client (new BrainerceClient({ storeId })), show the logged-in customer their points balance, tier progress, and redeemable rewards, and let them redeem for a coupon at checkout. Use getLoyaltyStatus() (includes tier/nextTier/progressToNextTier), getAvailableRewards(), redeemLoyaltyReward(id), and optionally reportSocialShare(). Note: loyalty is NOT available in vibe-coded mode."
6459
6585
  );
6460
6586
  }
6461
6587
  if (caps.features.hasCheckoutCustomFields) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainerce/mcp-server",
3
- "version": "3.12.2",
3
+ "version": "3.12.4",
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": {