@yawlabs/lemonsqueezy-mcp 0.7.0 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +139 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30179,15 +30179,29 @@ function checkDestructiveRateLimit(now = Date.now()) {
30179
30179
  function isStoreAllowlistActive() {
30180
30180
  return loadOptions().allowedStoreIds !== null;
30181
30181
  }
30182
- function checkStoreScopedToolInput(toolAcceptsStoreId, input) {
30183
- if (!toolAcceptsStoreId) return;
30184
- const raw = input.storeId;
30185
- if (raw !== void 0 && raw !== null && raw !== "") {
30186
- checkStoreAllowed(String(raw));
30187
- return;
30188
- }
30189
- if (isStoreAllowlistActive()) {
30190
- throw new GuardrailError("storeId is required when LEMONSQUEEZY_ALLOWED_STORE_IDS is set");
30182
+ function isPresent(value) {
30183
+ if (value === void 0 || value === null) return false;
30184
+ if (typeof value === "string" && value === "") return false;
30185
+ if (Array.isArray(value) && value.length === 0) return false;
30186
+ return true;
30187
+ }
30188
+ function checkStoreScopedToolInput(tool, input) {
30189
+ const toolAcceptsStoreId = "storeId" in tool.inputSchema.shape;
30190
+ if (toolAcceptsStoreId) {
30191
+ const raw = input.storeId;
30192
+ if (raw !== void 0 && raw !== null && raw !== "") {
30193
+ checkStoreAllowed(String(raw));
30194
+ } else if (isStoreAllowlistActive()) {
30195
+ throw new GuardrailError("storeId is required when LEMONSQUEEZY_ALLOWED_STORE_IDS is set");
30196
+ }
30197
+ }
30198
+ if (tool.requiredFilters && tool.requiredFilters.length > 0 && isStoreAllowlistActive()) {
30199
+ const anyPresent = tool.requiredFilters.some((key) => isPresent(input[key]));
30200
+ if (!anyPresent) {
30201
+ throw new GuardrailError(
30202
+ `At least one of [${tool.requiredFilters.join(", ")}] is required when LEMONSQUEEZY_ALLOWED_STORE_IDS is set`
30203
+ );
30204
+ }
30191
30205
  }
30192
30206
  }
30193
30207
  function isDestructiveCall(tool, input) {
@@ -30252,10 +30266,46 @@ function logEvent(entry) {
30252
30266
  }
30253
30267
  }
30254
30268
 
30269
+ // src/redact.ts
30270
+ var SECRET_KEY_RE = /^(secret|password|token|api[_-]?key|bearer|authorization|signing[_-]?secret)$/i;
30271
+ var REDACTED = "[REDACTED]";
30272
+ var CIRCULAR = "[CIRCULAR]";
30273
+ var MAX_DEPTH = 32;
30274
+ function isPlainObject3(value) {
30275
+ if (value === null || typeof value !== "object") return false;
30276
+ const proto = Object.getPrototypeOf(value);
30277
+ return proto === Object.prototype || proto === null;
30278
+ }
30279
+ function redactInner(value, visited, depth) {
30280
+ if (depth > MAX_DEPTH) return CIRCULAR;
30281
+ if (value === null || typeof value !== "object") return value;
30282
+ if (Array.isArray(value)) {
30283
+ if (visited.has(value)) return CIRCULAR;
30284
+ visited.add(value);
30285
+ return value.map((item) => redactInner(item, visited, depth + 1));
30286
+ }
30287
+ if (!isPlainObject3(value)) return value;
30288
+ if (visited.has(value)) return CIRCULAR;
30289
+ visited.add(value);
30290
+ const out = {};
30291
+ for (const [key, val] of Object.entries(value)) {
30292
+ if (SECRET_KEY_RE.test(key)) {
30293
+ out[key] = REDACTED;
30294
+ } else {
30295
+ out[key] = redactInner(val, visited, depth + 1);
30296
+ }
30297
+ }
30298
+ return out;
30299
+ }
30300
+ function redactSecrets(input) {
30301
+ return redactInner(input, /* @__PURE__ */ new WeakSet(), 0);
30302
+ }
30303
+
30255
30304
  // src/retry.ts
30256
30305
  var REQUEST_TIMEOUT_MS = 3e4;
30257
30306
  var DEFAULT_RETRY_WAIT_MS = 1e3;
30258
30307
  var MAX_RETRY_WAIT_MS = 3e4;
30308
+ var OVERALL_DEADLINE_MS = 9e4;
30259
30309
  var DEFAULT_MAX_ATTEMPTS = 4;
30260
30310
  var BASE_BACKOFF_MS = 250;
30261
30311
  var JITTER_FRACTION = 0.25;
@@ -30275,6 +30325,19 @@ function isAbortTimeoutError(err) {
30275
30325
  const e = err;
30276
30326
  return e.name === "TimeoutError" || e.name === "AbortError" || e.code === "ABORT_ERR";
30277
30327
  }
30328
+ function isRetryTimeoutError(err) {
30329
+ if (!err || typeof err !== "object") return false;
30330
+ const e = err;
30331
+ return e.name === "TimeoutError" && typeof e.elapsedMs === "number" && typeof e.attempts === "number";
30332
+ }
30333
+ function makeRetryTimeoutError(elapsedMs, attempts, cause) {
30334
+ const err = new Error("Request timed out");
30335
+ err.name = "TimeoutError";
30336
+ err.elapsedMs = elapsedMs;
30337
+ err.attempts = attempts;
30338
+ if (cause !== void 0) err.cause = cause;
30339
+ return err;
30340
+ }
30278
30341
  function backoffDelay(attempt, rand = Math.random) {
30279
30342
  const base = BASE_BACKOFF_MS * 2 ** (attempt - 1);
30280
30343
  const jitter = rand() * base * JITTER_FRACTION;
@@ -30286,31 +30349,54 @@ async function fetchWithRetry(url2, init, opts) {
30286
30349
  const sleep = opts.sleep ?? defaultSleep;
30287
30350
  const fetchImpl = opts.fetchImpl ?? fetch;
30288
30351
  const rand = opts.rand ?? Math.random;
30352
+ const deadlineMs = opts.deadlineMs ?? OVERALL_DEADLINE_MS;
30353
+ const now = opts.now ?? Date.now;
30354
+ const startedAt = now();
30355
+ const deadlineAt = startedAt + deadlineMs;
30356
+ let attempts = 0;
30289
30357
  let lastError;
30358
+ let lastResponse;
30290
30359
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
30360
+ if (attempt > 1 && now() >= deadlineAt) {
30361
+ if (lastResponse) return lastResponse;
30362
+ throw makeRetryTimeoutError(now() - startedAt, attempts, lastError);
30363
+ }
30364
+ attempts = attempt;
30291
30365
  try {
30292
30366
  const res = await fetchImpl(url2, { ...init, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
30367
+ lastResponse = res;
30293
30368
  if (res.status === 429) {
30294
30369
  if (attempt === maxAttempts) return res;
30295
30370
  const waitMs = parseRetryAfterMs(res.headers.get("retry-after"));
30296
30371
  if (waitMs > MAX_RETRY_WAIT_MS) return res;
30372
+ if (now() + waitMs >= deadlineAt) return res;
30297
30373
  await sleep(waitMs);
30298
30374
  continue;
30299
30375
  }
30300
30376
  if (res.status >= 500 && res.status < 600 && opts.idempotent && attempt < maxAttempts) {
30301
- await sleep(backoffDelay(attempt, rand));
30377
+ const waitMs = backoffDelay(attempt, rand);
30378
+ if (now() + waitMs >= deadlineAt) return res;
30379
+ await sleep(waitMs);
30302
30380
  continue;
30303
30381
  }
30304
30382
  return res;
30305
30383
  } catch (err) {
30306
30384
  lastError = err;
30307
30385
  if (isAbortTimeoutError(err)) {
30308
- if (!opts.idempotent || attempt === maxAttempts) throw err;
30309
- await sleep(backoffDelay(attempt, rand));
30386
+ if (!opts.idempotent || attempt === maxAttempts) {
30387
+ throw makeRetryTimeoutError(now() - startedAt, attempts, err);
30388
+ }
30389
+ const waitMs = backoffDelay(attempt, rand);
30390
+ if (now() + waitMs >= deadlineAt) {
30391
+ throw makeRetryTimeoutError(now() - startedAt, attempts, err);
30392
+ }
30393
+ await sleep(waitMs);
30310
30394
  continue;
30311
30395
  }
30312
30396
  if (err instanceof TypeError && opts.idempotent && attempt < maxAttempts) {
30313
- await sleep(backoffDelay(attempt, rand));
30397
+ const waitMs = backoffDelay(attempt, rand);
30398
+ if (now() + waitMs >= deadlineAt) throw err;
30399
+ await sleep(waitMs);
30314
30400
  continue;
30315
30401
  }
30316
30402
  throw err;
@@ -30433,6 +30519,14 @@ function buildQuery(params) {
30433
30519
  function decorateError(error48, requestId) {
30434
30520
  return requestId ? `${error48} (request_id: ${requestId})` : error48;
30435
30521
  }
30522
+ function formatTimeoutMessage(err, fallbackElapsedMs) {
30523
+ if (isRetryTimeoutError(err)) {
30524
+ const seconds2 = Math.max(1, Math.round(err.elapsedMs / 1e3));
30525
+ return `Request timed out after ${seconds2}s (${err.attempts} attempts)`;
30526
+ }
30527
+ const seconds = Math.max(1, Math.round(fallbackElapsedMs / 1e3));
30528
+ return `Request timed out after ${seconds}s (1 attempts)`;
30529
+ }
30436
30530
  async function apiRequest(method, path, body) {
30437
30531
  const start = Date.now();
30438
30532
  const apiKey = await loadApiKey();
@@ -30453,7 +30547,7 @@ async function apiRequest(method, path, body) {
30453
30547
  } catch (err) {
30454
30548
  const latency_ms2 = Date.now() - start;
30455
30549
  if (isAbortTimeoutError(err)) {
30456
- const error48 = `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`;
30550
+ const error48 = formatTimeoutMessage(err, latency_ms2);
30457
30551
  logEvent({ event: "http_call", method, path, status: "timeout", latency_ms: latency_ms2, error: error48 });
30458
30552
  return { ok: false, status: 0, error: error48 };
30459
30553
  }
@@ -30540,7 +30634,7 @@ async function licenseRequest(path, body) {
30540
30634
  } catch (err) {
30541
30635
  const latency_ms2 = Date.now() - start;
30542
30636
  if (isAbortTimeoutError(err)) {
30543
- const error48 = `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`;
30637
+ const error48 = formatTimeoutMessage(err, latency_ms2);
30544
30638
  logEvent({ event: "http_call", method: "POST", path, status: "timeout", latency_ms: latency_ms2, error: error48 });
30545
30639
  return { ok: false, status: 0, error: error48 };
30546
30640
  }
@@ -30663,7 +30757,7 @@ var affiliateTools = [
30663
30757
  },
30664
30758
  {
30665
30759
  name: "ls_list_affiliates",
30666
- description: "List all affiliates for the authenticated user's stores, optionally filtered by user email. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
30760
+ description: "List all affiliates for the authenticated user's stores, optionally filtered by user email. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool can still return affiliates tied to non-allowed stores -- the endpoint has no parent ID filter to scope by. Pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
30667
30761
  annotations: {
30668
30762
  title: "List affiliates",
30669
30763
  readOnlyHint: true,
@@ -30733,7 +30827,7 @@ var checkoutTools = [
30733
30827
  variantId: lsIdSchema.describe("The variant ID for the product being purchased"),
30734
30828
  customPrice: external_exports3.number().int().min(0).optional().describe("Custom price in cents (overrides the variant price)"),
30735
30829
  enabledVariants: external_exports3.array(lsIdSchema).optional().describe("Array of variant IDs to show on the checkout (for products with multiple variants)"),
30736
- email: external_exports3.string().max(1e4).optional().describe("Prefill customer email"),
30830
+ email: external_exports3.string().email().max(320).optional().describe("Prefill customer email"),
30737
30831
  name: external_exports3.string().max(1e4).optional().describe("Prefill customer name"),
30738
30832
  billingAddressCountry: external_exports3.string().max(1e4).optional().describe("Prefill billing country (ISO 3166-1 alpha-2)"),
30739
30833
  billingAddressZip: external_exports3.string().max(1e4).optional().describe("Prefill billing ZIP/postal code"),
@@ -30940,7 +31034,7 @@ var discountRedemptionTools = [
30940
31034
  },
30941
31035
  {
30942
31036
  name: "ls_list_discount_redemptions",
30943
- description: "List all discount redemptions, optionally filtered by discount or order. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31037
+ description: "List all discount redemptions, optionally filtered by discount or order. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: discountId, orderId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
30944
31038
  annotations: {
30945
31039
  title: "List discount redemptions",
30946
31040
  readOnlyHint: true,
@@ -30955,6 +31049,7 @@ var discountRedemptionTools = [
30955
31049
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
30956
31050
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
30957
31051
  }),
31052
+ requiredFilters: ["discountId", "orderId"],
30958
31053
  handler: listHandler("/discount-redemptions", { discountId: "discount_id", orderId: "order_id" })
30959
31054
  }
30960
31055
  ];
@@ -31092,7 +31187,7 @@ var fileTools = [
31092
31187
  },
31093
31188
  {
31094
31189
  name: "ls_list_files",
31095
- description: "List all files, optionally filtered by variant. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31190
+ description: "List all files, optionally filtered by variant. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: variantId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31096
31191
  annotations: {
31097
31192
  title: "List files",
31098
31193
  readOnlyHint: true,
@@ -31106,6 +31201,7 @@ var fileTools = [
31106
31201
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31107
31202
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31108
31203
  }),
31204
+ requiredFilters: ["variantId"],
31109
31205
  handler: listHandler("/files", { variantId: "variant_id" })
31110
31206
  }
31111
31207
  ];
@@ -31130,7 +31226,7 @@ var licenseKeyInstanceTools = [
31130
31226
  },
31131
31227
  {
31132
31228
  name: "ls_list_license_key_instances",
31133
- description: "List all license key instances (activations), optionally filtered by license key. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31229
+ description: "List all license key instances (activations), optionally filtered by license key. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: licenseKeyId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31134
31230
  annotations: {
31135
31231
  title: "List license key instances",
31136
31232
  readOnlyHint: true,
@@ -31144,6 +31240,7 @@ var licenseKeyInstanceTools = [
31144
31240
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31145
31241
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31146
31242
  }),
31243
+ requiredFilters: ["licenseKeyId"],
31147
31244
  handler: listHandler("/license-key-instances", { licenseKeyId: "license_key_id" })
31148
31245
  }
31149
31246
  ];
@@ -31206,11 +31303,14 @@ var licenseKeyTools = [
31206
31303
  idempotentHint: true,
31207
31304
  openWorldHint: true
31208
31305
  },
31209
- // Disabling a license key revokes a customer's access. Tag the call as
31210
- // destructive only when `disabled: true` so the rate limiter and audit log
31211
- // engage on revocation, while benign edits (expiry, activation limit) stay
31212
- // on the regular path.
31213
- isDestructive: (input) => input.disabled === true,
31306
+ // Disabling a license key revokes a customer's access outright. Changing
31307
+ // the activation limit can also revoke access -- setting it to 0, or to
31308
+ // any value below the customer's current activation count, kicks
31309
+ // already-activated instances offline. We can't tell from the input alone
31310
+ // whether a given limit change shrinks or grows, so treat ANY
31311
+ // `activationLimit` change as destructive alongside `disabled: true`.
31312
+ // Benign edits (expiry) stay on the regular path.
31313
+ isDestructive: (input) => input.disabled === true || input.activationLimit !== void 0,
31214
31314
  inputSchema: external_exports3.object({
31215
31315
  licenseKeyId: lsIdSchema.describe("The license key ID to update"),
31216
31316
  activationLimit: external_exports3.number().int().min(0).optional().describe("Maximum number of activations allowed (0 = unlimited)"),
@@ -31319,7 +31419,7 @@ var orderItemTools = [
31319
31419
  },
31320
31420
  {
31321
31421
  name: "ls_list_order_items",
31322
- description: "List all order items, optionally filtered by order or product. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31422
+ description: "List all order items, optionally filtered by order or product. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: orderId, productId, variantId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31323
31423
  annotations: {
31324
31424
  title: "List order items",
31325
31425
  readOnlyHint: true,
@@ -31335,6 +31435,7 @@ var orderItemTools = [
31335
31435
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31336
31436
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31337
31437
  }),
31438
+ requiredFilters: ["orderId", "productId", "variantId"],
31338
31439
  handler: listHandler("/order-items", { orderId: "order_id", productId: "product_id", variantId: "variant_id" })
31339
31440
  }
31340
31441
  ];
@@ -31458,7 +31559,7 @@ var priceTools = [
31458
31559
  },
31459
31560
  {
31460
31561
  name: "ls_list_prices",
31461
- description: "List all prices, optionally filtered by variant. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31562
+ description: "List all prices, optionally filtered by variant. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: variantId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31462
31563
  annotations: {
31463
31564
  title: "List prices",
31464
31565
  readOnlyHint: true,
@@ -31472,6 +31573,7 @@ var priceTools = [
31472
31573
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31473
31574
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31474
31575
  }),
31576
+ requiredFilters: ["variantId"],
31475
31577
  handler: listHandler("/prices", { variantId: "variant_id" })
31476
31578
  }
31477
31579
  ];
@@ -31683,7 +31785,7 @@ var subscriptionItemTools = [
31683
31785
  },
31684
31786
  {
31685
31787
  name: "ls_list_subscription_items",
31686
- description: "List all subscription items, optionally filtered by subscription or price. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31788
+ description: "List all subscription items, optionally filtered by subscription or price. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: subscriptionId, priceId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31687
31789
  annotations: {
31688
31790
  title: "List subscription items",
31689
31791
  readOnlyHint: true,
@@ -31698,6 +31800,7 @@ var subscriptionItemTools = [
31698
31800
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31699
31801
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31700
31802
  }),
31803
+ requiredFilters: ["subscriptionId", "priceId"],
31701
31804
  handler: listHandler("/subscription-items", { subscriptionId: "subscription_id", priceId: "price_id" })
31702
31805
  },
31703
31806
  {
@@ -31887,7 +31990,7 @@ var usageRecordTools = [
31887
31990
  },
31888
31991
  {
31889
31992
  name: "ls_list_usage_records",
31890
- description: "List all usage records, optionally filtered by subscription item. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
31993
+ description: "List all usage records, optionally filtered by subscription item. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: subscriptionItemId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31891
31994
  annotations: {
31892
31995
  title: "List usage records",
31893
31996
  readOnlyHint: true,
@@ -31901,6 +32004,7 @@ var usageRecordTools = [
31901
32004
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31902
32005
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31903
32006
  }),
32007
+ requiredFilters: ["subscriptionItemId"],
31904
32008
  handler: listHandler("/usage-records", { subscriptionItemId: "subscription_item_id" })
31905
32009
  },
31906
32010
  {
@@ -31980,7 +32084,7 @@ var variantTools = [
31980
32084
  },
31981
32085
  {
31982
32086
  name: "ls_list_variants",
31983
- description: "List all variants, optionally filtered by product. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total.",
32087
+ description: "List all variants, optionally filtered by product. Results are paginated \u2014 check meta.page in the response for currentPage, lastPage, and total. Cross-store note: when LEMONSQUEEZY_ALLOWED_STORE_IDS is set, this tool requires at least one of: productId. Even with that set, pair with a scoped LemonSqueezy API key for true cross-store enforcement -- the API key's visibility is the true boundary.",
31984
32088
  annotations: {
31985
32089
  title: "List variants",
31986
32090
  readOnlyHint: true,
@@ -31994,6 +32098,7 @@ var variantTools = [
31994
32098
  pageNumber: external_exports3.number().int().min(1).optional().describe("Page number (1-indexed)"),
31995
32099
  pageSize: external_exports3.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
31996
32100
  }),
32101
+ requiredFilters: ["productId"],
31997
32102
  handler: listHandler("/variants", { productId: "product_id" })
31998
32103
  }
31999
32104
  ];
@@ -32118,7 +32223,7 @@ var webhookTools = [
32118
32223
  ];
32119
32224
 
32120
32225
  // src/index.ts
32121
- var version2 = true ? "0.7.0" : (await null).createRequire(import.meta.url)("../package.json").version;
32226
+ var version2 = true ? "0.7.1" : (await null).createRequire(import.meta.url)("../package.json").version;
32122
32227
  var subcommand = process.argv[2];
32123
32228
  if (subcommand === "version" || subcommand === "--version") {
32124
32229
  console.log(version2);
@@ -32152,7 +32257,6 @@ var server = new McpServer({
32152
32257
  version: version2
32153
32258
  });
32154
32259
  for (const tool of allTools) {
32155
- const toolAcceptsStoreId = "storeId" in tool.inputSchema.shape;
32156
32260
  server.tool(
32157
32261
  tool.name,
32158
32262
  tool.description,
@@ -32163,7 +32267,7 @@ for (const tool of allTools) {
32163
32267
  const start = Date.now();
32164
32268
  try {
32165
32269
  if (isDestructive) checkDestructiveRateLimit();
32166
- checkStoreScopedToolInput(toolAcceptsStoreId, input);
32270
+ checkStoreScopedToolInput(tool, input);
32167
32271
  const result = await tool.handler(input);
32168
32272
  const response = result;
32169
32273
  const latency_ms = Date.now() - start;
@@ -32175,7 +32279,7 @@ for (const tool of allTools) {
32175
32279
  request_id: response.requestId,
32176
32280
  error: response.ok ? void 0 : response.error,
32177
32281
  audit: isDestructive ? true : void 0,
32178
- inputs: isDestructive ? input : void 0
32282
+ inputs: isDestructive ? redactSecrets(input) : void 0
32179
32283
  });
32180
32284
  if (!response.ok) {
32181
32285
  return {
@@ -32202,7 +32306,7 @@ for (const tool of allTools) {
32202
32306
  latency_ms,
32203
32307
  error: message,
32204
32308
  audit: isDestructive ? true : void 0,
32205
- inputs: isDestructive ? input : void 0
32309
+ inputs: isDestructive ? redactSecrets(input) : void 0
32206
32310
  });
32207
32311
  return {
32208
32312
  content: [{ type: "text", text: `Error: ${message}` }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/lemonsqueezy-mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "LemonSqueezy MCP server for managing your store from AI assistants",
5
5
  "license": "MIT",
6
6
  "author": "YawLabs <contact@yaw.sh>",