@brainerce/mcp-server 2.1.0 → 2.1.1

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 CHANGED
@@ -777,6 +777,12 @@ const { bundles } = await client.getCartBundles(cartId);
777
777
  // bundles[].bundleProduct \u2014 the product to offer
778
778
  // bundles[].originalPrice / bundles[].discountedPrice \u2014 prices
779
779
  // bundles[].discountType ('PERCENTAGE' | 'FIXED_AMOUNT') + discountValue
780
+
781
+ // Add a bundle to cart (applies the discount automatically):
782
+ await client.addBundleToCart(cartId, bundleOfferId);
783
+ // Remove a bundle from cart:
784
+ await client.removeBundleFromCart(cartId, bundleOfferId);
785
+ // Detect already-added bundles: check cart.items for metadata?.isBundleItem === true
780
786
  \`\`\`
781
787
 
782
788
  **Free shipping progress bar** (configured threshold in channel settings):
@@ -3001,7 +3007,11 @@ export default async function ProductDetailPage({ params }: Props) {
3001
3007
 
3002
3008
  import { useEffect, useState } from 'react';
3003
3009
  import Link from 'next/link';
3004
- import type { CartRecommendationsResponse, CartUpgradesResponse, CartBundlesResponse } from 'brainerce';
3010
+ import type {
3011
+ CartRecommendationsResponse,
3012
+ CartUpgradesResponse,
3013
+ CartBundlesResponse,
3014
+ } from 'brainerce';
3005
3015
  import { getClient } from '@/lib/brainerce';
3006
3016
  import { useCart } from '@/providers/store-provider';
3007
3017
  import { useStoreInfo } from '@/providers/store-provider';
@@ -3041,7 +3051,11 @@ export default function CartPage() {
3041
3051
 
3042
3052
  // Load upgrade suggestions when cart changes
3043
3053
  useEffect(() => {
3044
- if (!cart?.id || cart.items.length === 0 || storeInfo?.upsell?.cartUpgradeBannerEnabled === false) {
3054
+ if (
3055
+ !cart?.id ||
3056
+ cart.items.length === 0 ||
3057
+ storeInfo?.upsell?.cartUpgradeBannerEnabled === false
3058
+ ) {
3045
3059
  setUpgrades(null);
3046
3060
  return;
3047
3061
  }
@@ -9568,8 +9582,18 @@ function ProductThumb({
9568
9582
  <Image src={imageUrl} alt={name} fill sizes="80px" className="object-cover" />
9569
9583
  ) : (
9570
9584
  <div className="flex h-full w-full items-center justify-center">
9571
- <svg className="text-muted-foreground h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9572
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9585
+ <svg
9586
+ className="text-muted-foreground h-8 w-8"
9587
+ fill="none"
9588
+ viewBox="0 0 24 24"
9589
+ stroke="currentColor"
9590
+ >
9591
+ <path
9592
+ strokeLinecap="round"
9593
+ strokeLinejoin="round"
9594
+ strokeWidth={1.5}
9595
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9596
+ />
9573
9597
  </svg>
9574
9598
  </div>
9575
9599
  )}
@@ -9646,7 +9670,9 @@ export function FrequentlyBoughtTogether({
9646
9670
 
9647
9671
  return (
9648
9672
  <div className={cn('border-border rounded-lg border p-6', className)}>
9649
- <h2 className="text-foreground mb-4 text-xl font-semibold">{t('frequentlyBoughtTogether')}</h2>
9673
+ <h2 className="text-foreground mb-4 text-xl font-semibold">
9674
+ {t('frequentlyBoughtTogether')}
9675
+ </h2>
9650
9676
 
9651
9677
  <div className="flex flex-wrap items-center gap-3">
9652
9678
  {/* Current product (always included, no checkbox) */}
@@ -9733,12 +9759,7 @@ export function FreeShippingBar({ className }: FreeShippingBarProps) {
9733
9759
  <div className={cn('rounded-lg border border-green-200 bg-green-50 p-3', className)}>
9734
9760
  <div className="flex items-center gap-2 text-sm font-medium text-green-700">
9735
9761
  <svg className="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9736
- <path
9737
- strokeLinecap="round"
9738
- strokeLinejoin="round"
9739
- strokeWidth={2}
9740
- d="M5 13l4 4L19 7"
9741
- />
9762
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
9742
9763
  </svg>
9743
9764
  {t('freeShippingQualified')}
9744
9765
  </div>
@@ -9800,20 +9821,28 @@ export function CartUpgradeBanner({
9800
9821
  if (sessionStorage.getItem(storageKey)) {
9801
9822
  setDismissed(true);
9802
9823
  }
9803
- } catch {}
9824
+ } catch {
9825
+ /* ignore */
9826
+ }
9804
9827
  }, [storageKey]);
9805
9828
 
9806
9829
  if (dismissed) return null;
9807
9830
 
9808
9831
  const target = suggestion.targetProduct;
9809
9832
  const firstImage = target.images?.[0];
9810
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9833
+ const imageUrl = firstImage
9834
+ ? typeof firstImage === 'string'
9835
+ ? firstImage
9836
+ : firstImage.url
9837
+ : null;
9811
9838
  const formattedDelta = formatPrice(parseFloat(suggestion.priceDelta), { currency }) as string;
9812
9839
 
9813
9840
  function handleDismiss() {
9814
9841
  try {
9815
9842
  sessionStorage.setItem(storageKey, '1');
9816
- } catch {}
9843
+ } catch {
9844
+ /* ignore */
9845
+ }
9817
9846
  setDismissed(true);
9818
9847
  }
9819
9848
 
@@ -9846,7 +9875,13 @@ export function CartUpgradeBanner({
9846
9875
  className="text-muted-foreground hover:text-foreground absolute end-2 top-2 text-xs"
9847
9876
  aria-label={t('dismissUpgrade')}
9848
9877
  >
9849
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
9878
+ <svg
9879
+ className="h-4 w-4"
9880
+ fill="none"
9881
+ viewBox="0 0 24 24"
9882
+ stroke="currentColor"
9883
+ strokeWidth={2}
9884
+ >
9850
9885
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
9851
9886
  </svg>
9852
9887
  </button>
@@ -9858,7 +9893,12 @@ export function CartUpgradeBanner({
9858
9893
  ) : (
9859
9894
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9860
9895
  <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9861
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9896
+ <path
9897
+ strokeLinecap="round"
9898
+ strokeLinejoin="round"
9899
+ strokeWidth={1.5}
9900
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9901
+ />
9862
9902
  </svg>
9863
9903
  </div>
9864
9904
  )}
@@ -9912,13 +9952,17 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9912
9952
 
9913
9953
  const product = offer.bundleProduct;
9914
9954
  const firstImage = product.images?.[0];
9915
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9955
+ const imageUrl = firstImage
9956
+ ? typeof firstImage === 'string'
9957
+ ? firstImage
9958
+ : firstImage.url
9959
+ : null;
9916
9960
  const originalPrice = parseFloat(offer.originalPrice);
9917
9961
  const discountedPrice = parseFloat(offer.discountedPrice);
9918
9962
  const discountLabel =
9919
9963
  offer.discountType === 'PERCENTAGE'
9920
9964
  ? \`\${offer.discountValue}%\`
9921
- : formatPrice(parseFloat(offer.discountValue), { currency }) as string;
9965
+ : (formatPrice(parseFloat(offer.discountValue), { currency }) as string);
9922
9966
 
9923
9967
  async function handleAdd() {
9924
9968
  if (adding) return;
@@ -9952,7 +9996,12 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9952
9996
  ) : (
9953
9997
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9954
9998
  <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9955
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9999
+ <path
10000
+ strokeLinecap="round"
10001
+ strokeLinejoin="round"
10002
+ strokeWidth={1.5}
10003
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
10004
+ />
9956
10005
  </svg>
9957
10006
  </div>
9958
10007
  )}
@@ -10017,7 +10066,11 @@ export function OrderBumpCard({ bump, isAdded, onToggle, loading, className }: O
10017
10066
 
10018
10067
  const product = bump.bumpProduct;
10019
10068
  const firstImage = product.images?.[0];
10020
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
10069
+ const imageUrl = firstImage
10070
+ ? typeof firstImage === 'string'
10071
+ ? firstImage
10072
+ : firstImage.url
10073
+ : null;
10021
10074
  const originalPrice = parseFloat(bump.originalPrice);
10022
10075
  const hasDiscount = bump.discountedPrice != null;
10023
10076
  const discountedPrice = hasDiscount ? parseFloat(bump.discountedPrice!) : null;
package/dist/bin/stdio.js CHANGED
@@ -775,6 +775,12 @@ const { bundles } = await client.getCartBundles(cartId);
775
775
  // bundles[].bundleProduct \u2014 the product to offer
776
776
  // bundles[].originalPrice / bundles[].discountedPrice \u2014 prices
777
777
  // bundles[].discountType ('PERCENTAGE' | 'FIXED_AMOUNT') + discountValue
778
+
779
+ // Add a bundle to cart (applies the discount automatically):
780
+ await client.addBundleToCart(cartId, bundleOfferId);
781
+ // Remove a bundle from cart:
782
+ await client.removeBundleFromCart(cartId, bundleOfferId);
783
+ // Detect already-added bundles: check cart.items for metadata?.isBundleItem === true
778
784
  \`\`\`
779
785
 
780
786
  **Free shipping progress bar** (configured threshold in channel settings):
@@ -2999,7 +3005,11 @@ export default async function ProductDetailPage({ params }: Props) {
2999
3005
 
3000
3006
  import { useEffect, useState } from 'react';
3001
3007
  import Link from 'next/link';
3002
- import type { CartRecommendationsResponse, CartUpgradesResponse, CartBundlesResponse } from 'brainerce';
3008
+ import type {
3009
+ CartRecommendationsResponse,
3010
+ CartUpgradesResponse,
3011
+ CartBundlesResponse,
3012
+ } from 'brainerce';
3003
3013
  import { getClient } from '@/lib/brainerce';
3004
3014
  import { useCart } from '@/providers/store-provider';
3005
3015
  import { useStoreInfo } from '@/providers/store-provider';
@@ -3039,7 +3049,11 @@ export default function CartPage() {
3039
3049
 
3040
3050
  // Load upgrade suggestions when cart changes
3041
3051
  useEffect(() => {
3042
- if (!cart?.id || cart.items.length === 0 || storeInfo?.upsell?.cartUpgradeBannerEnabled === false) {
3052
+ if (
3053
+ !cart?.id ||
3054
+ cart.items.length === 0 ||
3055
+ storeInfo?.upsell?.cartUpgradeBannerEnabled === false
3056
+ ) {
3043
3057
  setUpgrades(null);
3044
3058
  return;
3045
3059
  }
@@ -9566,8 +9580,18 @@ function ProductThumb({
9566
9580
  <Image src={imageUrl} alt={name} fill sizes="80px" className="object-cover" />
9567
9581
  ) : (
9568
9582
  <div className="flex h-full w-full items-center justify-center">
9569
- <svg className="text-muted-foreground h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9570
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9583
+ <svg
9584
+ className="text-muted-foreground h-8 w-8"
9585
+ fill="none"
9586
+ viewBox="0 0 24 24"
9587
+ stroke="currentColor"
9588
+ >
9589
+ <path
9590
+ strokeLinecap="round"
9591
+ strokeLinejoin="round"
9592
+ strokeWidth={1.5}
9593
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9594
+ />
9571
9595
  </svg>
9572
9596
  </div>
9573
9597
  )}
@@ -9644,7 +9668,9 @@ export function FrequentlyBoughtTogether({
9644
9668
 
9645
9669
  return (
9646
9670
  <div className={cn('border-border rounded-lg border p-6', className)}>
9647
- <h2 className="text-foreground mb-4 text-xl font-semibold">{t('frequentlyBoughtTogether')}</h2>
9671
+ <h2 className="text-foreground mb-4 text-xl font-semibold">
9672
+ {t('frequentlyBoughtTogether')}
9673
+ </h2>
9648
9674
 
9649
9675
  <div className="flex flex-wrap items-center gap-3">
9650
9676
  {/* Current product (always included, no checkbox) */}
@@ -9731,12 +9757,7 @@ export function FreeShippingBar({ className }: FreeShippingBarProps) {
9731
9757
  <div className={cn('rounded-lg border border-green-200 bg-green-50 p-3', className)}>
9732
9758
  <div className="flex items-center gap-2 text-sm font-medium text-green-700">
9733
9759
  <svg className="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9734
- <path
9735
- strokeLinecap="round"
9736
- strokeLinejoin="round"
9737
- strokeWidth={2}
9738
- d="M5 13l4 4L19 7"
9739
- />
9760
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
9740
9761
  </svg>
9741
9762
  {t('freeShippingQualified')}
9742
9763
  </div>
@@ -9798,20 +9819,28 @@ export function CartUpgradeBanner({
9798
9819
  if (sessionStorage.getItem(storageKey)) {
9799
9820
  setDismissed(true);
9800
9821
  }
9801
- } catch {}
9822
+ } catch {
9823
+ /* ignore */
9824
+ }
9802
9825
  }, [storageKey]);
9803
9826
 
9804
9827
  if (dismissed) return null;
9805
9828
 
9806
9829
  const target = suggestion.targetProduct;
9807
9830
  const firstImage = target.images?.[0];
9808
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9831
+ const imageUrl = firstImage
9832
+ ? typeof firstImage === 'string'
9833
+ ? firstImage
9834
+ : firstImage.url
9835
+ : null;
9809
9836
  const formattedDelta = formatPrice(parseFloat(suggestion.priceDelta), { currency }) as string;
9810
9837
 
9811
9838
  function handleDismiss() {
9812
9839
  try {
9813
9840
  sessionStorage.setItem(storageKey, '1');
9814
- } catch {}
9841
+ } catch {
9842
+ /* ignore */
9843
+ }
9815
9844
  setDismissed(true);
9816
9845
  }
9817
9846
 
@@ -9844,7 +9873,13 @@ export function CartUpgradeBanner({
9844
9873
  className="text-muted-foreground hover:text-foreground absolute end-2 top-2 text-xs"
9845
9874
  aria-label={t('dismissUpgrade')}
9846
9875
  >
9847
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
9876
+ <svg
9877
+ className="h-4 w-4"
9878
+ fill="none"
9879
+ viewBox="0 0 24 24"
9880
+ stroke="currentColor"
9881
+ strokeWidth={2}
9882
+ >
9848
9883
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
9849
9884
  </svg>
9850
9885
  </button>
@@ -9856,7 +9891,12 @@ export function CartUpgradeBanner({
9856
9891
  ) : (
9857
9892
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9858
9893
  <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9859
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9894
+ <path
9895
+ strokeLinecap="round"
9896
+ strokeLinejoin="round"
9897
+ strokeWidth={1.5}
9898
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9899
+ />
9860
9900
  </svg>
9861
9901
  </div>
9862
9902
  )}
@@ -9910,13 +9950,17 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9910
9950
 
9911
9951
  const product = offer.bundleProduct;
9912
9952
  const firstImage = product.images?.[0];
9913
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9953
+ const imageUrl = firstImage
9954
+ ? typeof firstImage === 'string'
9955
+ ? firstImage
9956
+ : firstImage.url
9957
+ : null;
9914
9958
  const originalPrice = parseFloat(offer.originalPrice);
9915
9959
  const discountedPrice = parseFloat(offer.discountedPrice);
9916
9960
  const discountLabel =
9917
9961
  offer.discountType === 'PERCENTAGE'
9918
9962
  ? \`\${offer.discountValue}%\`
9919
- : formatPrice(parseFloat(offer.discountValue), { currency }) as string;
9963
+ : (formatPrice(parseFloat(offer.discountValue), { currency }) as string);
9920
9964
 
9921
9965
  async function handleAdd() {
9922
9966
  if (adding) return;
@@ -9950,7 +9994,12 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9950
9994
  ) : (
9951
9995
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9952
9996
  <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9953
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9997
+ <path
9998
+ strokeLinecap="round"
9999
+ strokeLinejoin="round"
10000
+ strokeWidth={1.5}
10001
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
10002
+ />
9954
10003
  </svg>
9955
10004
  </div>
9956
10005
  )}
@@ -10015,7 +10064,11 @@ export function OrderBumpCard({ bump, isAdded, onToggle, loading, className }: O
10015
10064
 
10016
10065
  const product = bump.bumpProduct;
10017
10066
  const firstImage = product.images?.[0];
10018
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
10067
+ const imageUrl = firstImage
10068
+ ? typeof firstImage === 'string'
10069
+ ? firstImage
10070
+ : firstImage.url
10071
+ : null;
10019
10072
  const originalPrice = parseFloat(bump.originalPrice);
10020
10073
  const hasDiscount = bump.discountedPrice != null;
10021
10074
  const discountedPrice = hasDiscount ? parseFloat(bump.discountedPrice!) : null;
package/dist/index.js CHANGED
@@ -792,6 +792,12 @@ const { bundles } = await client.getCartBundles(cartId);
792
792
  // bundles[].bundleProduct \u2014 the product to offer
793
793
  // bundles[].originalPrice / bundles[].discountedPrice \u2014 prices
794
794
  // bundles[].discountType ('PERCENTAGE' | 'FIXED_AMOUNT') + discountValue
795
+
796
+ // Add a bundle to cart (applies the discount automatically):
797
+ await client.addBundleToCart(cartId, bundleOfferId);
798
+ // Remove a bundle from cart:
799
+ await client.removeBundleFromCart(cartId, bundleOfferId);
800
+ // Detect already-added bundles: check cart.items for metadata?.isBundleItem === true
795
801
  \`\`\`
796
802
 
797
803
  **Free shipping progress bar** (configured threshold in channel settings):
@@ -3016,7 +3022,11 @@ export default async function ProductDetailPage({ params }: Props) {
3016
3022
 
3017
3023
  import { useEffect, useState } from 'react';
3018
3024
  import Link from 'next/link';
3019
- import type { CartRecommendationsResponse, CartUpgradesResponse, CartBundlesResponse } from 'brainerce';
3025
+ import type {
3026
+ CartRecommendationsResponse,
3027
+ CartUpgradesResponse,
3028
+ CartBundlesResponse,
3029
+ } from 'brainerce';
3020
3030
  import { getClient } from '@/lib/brainerce';
3021
3031
  import { useCart } from '@/providers/store-provider';
3022
3032
  import { useStoreInfo } from '@/providers/store-provider';
@@ -3056,7 +3066,11 @@ export default function CartPage() {
3056
3066
 
3057
3067
  // Load upgrade suggestions when cart changes
3058
3068
  useEffect(() => {
3059
- if (!cart?.id || cart.items.length === 0 || storeInfo?.upsell?.cartUpgradeBannerEnabled === false) {
3069
+ if (
3070
+ !cart?.id ||
3071
+ cart.items.length === 0 ||
3072
+ storeInfo?.upsell?.cartUpgradeBannerEnabled === false
3073
+ ) {
3060
3074
  setUpgrades(null);
3061
3075
  return;
3062
3076
  }
@@ -9583,8 +9597,18 @@ function ProductThumb({
9583
9597
  <Image src={imageUrl} alt={name} fill sizes="80px" className="object-cover" />
9584
9598
  ) : (
9585
9599
  <div className="flex h-full w-full items-center justify-center">
9586
- <svg className="text-muted-foreground h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9587
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9600
+ <svg
9601
+ className="text-muted-foreground h-8 w-8"
9602
+ fill="none"
9603
+ viewBox="0 0 24 24"
9604
+ stroke="currentColor"
9605
+ >
9606
+ <path
9607
+ strokeLinecap="round"
9608
+ strokeLinejoin="round"
9609
+ strokeWidth={1.5}
9610
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9611
+ />
9588
9612
  </svg>
9589
9613
  </div>
9590
9614
  )}
@@ -9661,7 +9685,9 @@ export function FrequentlyBoughtTogether({
9661
9685
 
9662
9686
  return (
9663
9687
  <div className={cn('border-border rounded-lg border p-6', className)}>
9664
- <h2 className="text-foreground mb-4 text-xl font-semibold">{t('frequentlyBoughtTogether')}</h2>
9688
+ <h2 className="text-foreground mb-4 text-xl font-semibold">
9689
+ {t('frequentlyBoughtTogether')}
9690
+ </h2>
9665
9691
 
9666
9692
  <div className="flex flex-wrap items-center gap-3">
9667
9693
  {/* Current product (always included, no checkbox) */}
@@ -9748,12 +9774,7 @@ export function FreeShippingBar({ className }: FreeShippingBarProps) {
9748
9774
  <div className={cn('rounded-lg border border-green-200 bg-green-50 p-3', className)}>
9749
9775
  <div className="flex items-center gap-2 text-sm font-medium text-green-700">
9750
9776
  <svg className="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9751
- <path
9752
- strokeLinecap="round"
9753
- strokeLinejoin="round"
9754
- strokeWidth={2}
9755
- d="M5 13l4 4L19 7"
9756
- />
9777
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
9757
9778
  </svg>
9758
9779
  {t('freeShippingQualified')}
9759
9780
  </div>
@@ -9815,20 +9836,28 @@ export function CartUpgradeBanner({
9815
9836
  if (sessionStorage.getItem(storageKey)) {
9816
9837
  setDismissed(true);
9817
9838
  }
9818
- } catch {}
9839
+ } catch {
9840
+ /* ignore */
9841
+ }
9819
9842
  }, [storageKey]);
9820
9843
 
9821
9844
  if (dismissed) return null;
9822
9845
 
9823
9846
  const target = suggestion.targetProduct;
9824
9847
  const firstImage = target.images?.[0];
9825
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9848
+ const imageUrl = firstImage
9849
+ ? typeof firstImage === 'string'
9850
+ ? firstImage
9851
+ : firstImage.url
9852
+ : null;
9826
9853
  const formattedDelta = formatPrice(parseFloat(suggestion.priceDelta), { currency }) as string;
9827
9854
 
9828
9855
  function handleDismiss() {
9829
9856
  try {
9830
9857
  sessionStorage.setItem(storageKey, '1');
9831
- } catch {}
9858
+ } catch {
9859
+ /* ignore */
9860
+ }
9832
9861
  setDismissed(true);
9833
9862
  }
9834
9863
 
@@ -9861,7 +9890,13 @@ export function CartUpgradeBanner({
9861
9890
  className="text-muted-foreground hover:text-foreground absolute end-2 top-2 text-xs"
9862
9891
  aria-label={t('dismissUpgrade')}
9863
9892
  >
9864
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
9893
+ <svg
9894
+ className="h-4 w-4"
9895
+ fill="none"
9896
+ viewBox="0 0 24 24"
9897
+ stroke="currentColor"
9898
+ strokeWidth={2}
9899
+ >
9865
9900
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
9866
9901
  </svg>
9867
9902
  </button>
@@ -9873,7 +9908,12 @@ export function CartUpgradeBanner({
9873
9908
  ) : (
9874
9909
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9875
9910
  <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9876
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9911
+ <path
9912
+ strokeLinecap="round"
9913
+ strokeLinejoin="round"
9914
+ strokeWidth={1.5}
9915
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9916
+ />
9877
9917
  </svg>
9878
9918
  </div>
9879
9919
  )}
@@ -9927,13 +9967,17 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9927
9967
 
9928
9968
  const product = offer.bundleProduct;
9929
9969
  const firstImage = product.images?.[0];
9930
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9970
+ const imageUrl = firstImage
9971
+ ? typeof firstImage === 'string'
9972
+ ? firstImage
9973
+ : firstImage.url
9974
+ : null;
9931
9975
  const originalPrice = parseFloat(offer.originalPrice);
9932
9976
  const discountedPrice = parseFloat(offer.discountedPrice);
9933
9977
  const discountLabel =
9934
9978
  offer.discountType === 'PERCENTAGE'
9935
9979
  ? \`\${offer.discountValue}%\`
9936
- : formatPrice(parseFloat(offer.discountValue), { currency }) as string;
9980
+ : (formatPrice(parseFloat(offer.discountValue), { currency }) as string);
9937
9981
 
9938
9982
  async function handleAdd() {
9939
9983
  if (adding) return;
@@ -9967,7 +10011,12 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9967
10011
  ) : (
9968
10012
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9969
10013
  <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9970
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
10014
+ <path
10015
+ strokeLinecap="round"
10016
+ strokeLinejoin="round"
10017
+ strokeWidth={1.5}
10018
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
10019
+ />
9971
10020
  </svg>
9972
10021
  </div>
9973
10022
  )}
@@ -10032,7 +10081,11 @@ export function OrderBumpCard({ bump, isAdded, onToggle, loading, className }: O
10032
10081
 
10033
10082
  const product = bump.bumpProduct;
10034
10083
  const firstImage = product.images?.[0];
10035
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
10084
+ const imageUrl = firstImage
10085
+ ? typeof firstImage === 'string'
10086
+ ? firstImage
10087
+ : firstImage.url
10088
+ : null;
10036
10089
  const originalPrice = parseFloat(bump.originalPrice);
10037
10090
  const hasDiscount = bump.discountedPrice != null;
10038
10091
  const discountedPrice = hasDiscount ? parseFloat(bump.discountedPrice!) : null;
package/dist/index.mjs CHANGED
@@ -747,6 +747,12 @@ const { bundles } = await client.getCartBundles(cartId);
747
747
  // bundles[].bundleProduct \u2014 the product to offer
748
748
  // bundles[].originalPrice / bundles[].discountedPrice \u2014 prices
749
749
  // bundles[].discountType ('PERCENTAGE' | 'FIXED_AMOUNT') + discountValue
750
+
751
+ // Add a bundle to cart (applies the discount automatically):
752
+ await client.addBundleToCart(cartId, bundleOfferId);
753
+ // Remove a bundle from cart:
754
+ await client.removeBundleFromCart(cartId, bundleOfferId);
755
+ // Detect already-added bundles: check cart.items for metadata?.isBundleItem === true
750
756
  \`\`\`
751
757
 
752
758
  **Free shipping progress bar** (configured threshold in channel settings):
@@ -2971,7 +2977,11 @@ export default async function ProductDetailPage({ params }: Props) {
2971
2977
 
2972
2978
  import { useEffect, useState } from 'react';
2973
2979
  import Link from 'next/link';
2974
- import type { CartRecommendationsResponse, CartUpgradesResponse, CartBundlesResponse } from 'brainerce';
2980
+ import type {
2981
+ CartRecommendationsResponse,
2982
+ CartUpgradesResponse,
2983
+ CartBundlesResponse,
2984
+ } from 'brainerce';
2975
2985
  import { getClient } from '@/lib/brainerce';
2976
2986
  import { useCart } from '@/providers/store-provider';
2977
2987
  import { useStoreInfo } from '@/providers/store-provider';
@@ -3011,7 +3021,11 @@ export default function CartPage() {
3011
3021
 
3012
3022
  // Load upgrade suggestions when cart changes
3013
3023
  useEffect(() => {
3014
- if (!cart?.id || cart.items.length === 0 || storeInfo?.upsell?.cartUpgradeBannerEnabled === false) {
3024
+ if (
3025
+ !cart?.id ||
3026
+ cart.items.length === 0 ||
3027
+ storeInfo?.upsell?.cartUpgradeBannerEnabled === false
3028
+ ) {
3015
3029
  setUpgrades(null);
3016
3030
  return;
3017
3031
  }
@@ -9538,8 +9552,18 @@ function ProductThumb({
9538
9552
  <Image src={imageUrl} alt={name} fill sizes="80px" className="object-cover" />
9539
9553
  ) : (
9540
9554
  <div className="flex h-full w-full items-center justify-center">
9541
- <svg className="text-muted-foreground h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9542
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9555
+ <svg
9556
+ className="text-muted-foreground h-8 w-8"
9557
+ fill="none"
9558
+ viewBox="0 0 24 24"
9559
+ stroke="currentColor"
9560
+ >
9561
+ <path
9562
+ strokeLinecap="round"
9563
+ strokeLinejoin="round"
9564
+ strokeWidth={1.5}
9565
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9566
+ />
9543
9567
  </svg>
9544
9568
  </div>
9545
9569
  )}
@@ -9616,7 +9640,9 @@ export function FrequentlyBoughtTogether({
9616
9640
 
9617
9641
  return (
9618
9642
  <div className={cn('border-border rounded-lg border p-6', className)}>
9619
- <h2 className="text-foreground mb-4 text-xl font-semibold">{t('frequentlyBoughtTogether')}</h2>
9643
+ <h2 className="text-foreground mb-4 text-xl font-semibold">
9644
+ {t('frequentlyBoughtTogether')}
9645
+ </h2>
9620
9646
 
9621
9647
  <div className="flex flex-wrap items-center gap-3">
9622
9648
  {/* Current product (always included, no checkbox) */}
@@ -9703,12 +9729,7 @@ export function FreeShippingBar({ className }: FreeShippingBarProps) {
9703
9729
  <div className={cn('rounded-lg border border-green-200 bg-green-50 p-3', className)}>
9704
9730
  <div className="flex items-center gap-2 text-sm font-medium text-green-700">
9705
9731
  <svg className="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9706
- <path
9707
- strokeLinecap="round"
9708
- strokeLinejoin="round"
9709
- strokeWidth={2}
9710
- d="M5 13l4 4L19 7"
9711
- />
9732
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
9712
9733
  </svg>
9713
9734
  {t('freeShippingQualified')}
9714
9735
  </div>
@@ -9770,20 +9791,28 @@ export function CartUpgradeBanner({
9770
9791
  if (sessionStorage.getItem(storageKey)) {
9771
9792
  setDismissed(true);
9772
9793
  }
9773
- } catch {}
9794
+ } catch {
9795
+ /* ignore */
9796
+ }
9774
9797
  }, [storageKey]);
9775
9798
 
9776
9799
  if (dismissed) return null;
9777
9800
 
9778
9801
  const target = suggestion.targetProduct;
9779
9802
  const firstImage = target.images?.[0];
9780
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9803
+ const imageUrl = firstImage
9804
+ ? typeof firstImage === 'string'
9805
+ ? firstImage
9806
+ : firstImage.url
9807
+ : null;
9781
9808
  const formattedDelta = formatPrice(parseFloat(suggestion.priceDelta), { currency }) as string;
9782
9809
 
9783
9810
  function handleDismiss() {
9784
9811
  try {
9785
9812
  sessionStorage.setItem(storageKey, '1');
9786
- } catch {}
9813
+ } catch {
9814
+ /* ignore */
9815
+ }
9787
9816
  setDismissed(true);
9788
9817
  }
9789
9818
 
@@ -9816,7 +9845,13 @@ export function CartUpgradeBanner({
9816
9845
  className="text-muted-foreground hover:text-foreground absolute end-2 top-2 text-xs"
9817
9846
  aria-label={t('dismissUpgrade')}
9818
9847
  >
9819
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
9848
+ <svg
9849
+ className="h-4 w-4"
9850
+ fill="none"
9851
+ viewBox="0 0 24 24"
9852
+ stroke="currentColor"
9853
+ strokeWidth={2}
9854
+ >
9820
9855
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
9821
9856
  </svg>
9822
9857
  </button>
@@ -9828,7 +9863,12 @@ export function CartUpgradeBanner({
9828
9863
  ) : (
9829
9864
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9830
9865
  <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9831
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9866
+ <path
9867
+ strokeLinecap="round"
9868
+ strokeLinejoin="round"
9869
+ strokeWidth={1.5}
9870
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9871
+ />
9832
9872
  </svg>
9833
9873
  </div>
9834
9874
  )}
@@ -9882,13 +9922,17 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9882
9922
 
9883
9923
  const product = offer.bundleProduct;
9884
9924
  const firstImage = product.images?.[0];
9885
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
9925
+ const imageUrl = firstImage
9926
+ ? typeof firstImage === 'string'
9927
+ ? firstImage
9928
+ : firstImage.url
9929
+ : null;
9886
9930
  const originalPrice = parseFloat(offer.originalPrice);
9887
9931
  const discountedPrice = parseFloat(offer.discountedPrice);
9888
9932
  const discountLabel =
9889
9933
  offer.discountType === 'PERCENTAGE'
9890
9934
  ? \`\${offer.discountValue}%\`
9891
- : formatPrice(parseFloat(offer.discountValue), { currency }) as string;
9935
+ : (formatPrice(parseFloat(offer.discountValue), { currency }) as string);
9892
9936
 
9893
9937
  async function handleAdd() {
9894
9938
  if (adding) return;
@@ -9922,7 +9966,12 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9922
9966
  ) : (
9923
9967
  <div className="text-muted-foreground flex h-full w-full items-center justify-center">
9924
9968
  <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9925
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
9969
+ <path
9970
+ strokeLinecap="round"
9971
+ strokeLinejoin="round"
9972
+ strokeWidth={1.5}
9973
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
9974
+ />
9926
9975
  </svg>
9927
9976
  </div>
9928
9977
  )}
@@ -9987,7 +10036,11 @@ export function OrderBumpCard({ bump, isAdded, onToggle, loading, className }: O
9987
10036
 
9988
10037
  const product = bump.bumpProduct;
9989
10038
  const firstImage = product.images?.[0];
9990
- const imageUrl = firstImage ? (typeof firstImage === 'string' ? firstImage : firstImage.url) : null;
10039
+ const imageUrl = firstImage
10040
+ ? typeof firstImage === 'string'
10041
+ ? firstImage
10042
+ : firstImage.url
10043
+ : null;
9991
10044
  const originalPrice = parseFloat(bump.originalPrice);
9992
10045
  const hasDiscount = bump.discountedPrice != null;
9993
10046
  const discountedPrice = hasDiscount ? parseFloat(bump.discountedPrice!) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainerce/mcp-server",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "MCP server for AI-powered Brainerce store building. Provides SDK docs, types, and code examples to AI tools like Lovable, Cursor, and Claude Code.",
5
5
  "bin": {
6
6
  "brainerce-mcp": "dist/bin/stdio.js"