@yawlabs/lemonsqueezy-mcp 0.10.6 → 0.10.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `@yawlabs/lemonsqueezy-mcp` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versioning follows [SEMVER.md](./SEMVER.md).
4
4
 
5
+ ## [0.10.7] -- 2026-05-16
6
+
7
+ ### Security
8
+
9
+ - **Allowlist-bypass surface closed at the test layer.** `checkStoreScopedToolInput` in `src/wrapper.ts` enforces `LEMONSQUEEZY_ALLOWED_STORE_IDS` by reading the literal input field name `storeId` from the tool's Zod shape. Today every list tool that filters by store happens to use that name, but the convention wasn't asserted anywhere -- a future tool whose filterMap mapped a differently-named input (e.g. `store`) to `filter[store_id]` would silently bypass the allowlist. `src/api.ts` `listHandler` now exposes its `filterMap` on the returned handler, and `src/tools/tools.test.ts` adds an invariant that fails CI for any drift between the filter key (`store_id`) and the input field name (`storeId`).
10
+
11
+ ### Changed
12
+
13
+ - **`wrapper.ts` skips the success/error entry literal when no consumer wants it.** A non-destructive read at `LEMONSQUEEZY_LOG` unset (i.e. "off") previously built a full audit entry on every call, then `logEvent` discarded it inside its level check. Now the wrapper consults `wouldLogToolCall({ isDestructive, isError })` -- a new export from `src/logger.ts` -- before allocating. Destructive calls always build the entry because the audit-buffer push is independent of the log level. No observable behavior change; saves a small object literal per non-destructive read on the most common configuration.
14
+
5
15
  ## [0.10.6] -- 2026-05-16
6
16
 
7
17
  ### Security
@@ -333,7 +343,8 @@ Hardening pass for unattended automation against live billing flows.
333
343
 
334
344
  Initial release. 59 tools covering all 17 LemonSqueezy API resources.
335
345
 
336
- [Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.6...HEAD
346
+ [Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.7...HEAD
347
+ [0.10.7]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.6...v0.10.7
337
348
  [0.10.6]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.5...v0.10.6
338
349
  [0.10.5]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.4...v0.10.5
339
350
  [0.10.4]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.3...v0.10.4
package/dist/index.js CHANGED
@@ -30410,6 +30410,19 @@ function shouldLog(entry, level) {
30410
30410
  return isErrorEntry(entry);
30411
30411
  }
30412
30412
  }
30413
+ function wouldLogToolCall(opts) {
30414
+ const level = getLogLevel();
30415
+ switch (level) {
30416
+ case "off":
30417
+ return false;
30418
+ case "all":
30419
+ return true;
30420
+ case "audit":
30421
+ return opts.isDestructive || opts.isError;
30422
+ case "error":
30423
+ return opts.isError;
30424
+ }
30425
+ }
30413
30426
  function safeString(value) {
30414
30427
  if (value === void 0) return void 0;
30415
30428
  if (typeof value === "string") return value;
@@ -30886,7 +30899,7 @@ function getHandler(endpoint, idField) {
30886
30899
  };
30887
30900
  }
30888
30901
  function listHandler(endpoint, filterMap = {}) {
30889
- return async (input) => {
30902
+ const handler = async (input) => {
30890
30903
  const filter = {};
30891
30904
  for (const [inputKey, apiKey] of Object.entries(filterMap)) {
30892
30905
  const val = input[inputKey];
@@ -30899,6 +30912,7 @@ function listHandler(endpoint, filterMap = {}) {
30899
30912
  });
30900
30913
  return apiGet(`${endpoint}${query}`);
30901
30914
  };
30915
+ return Object.assign(handler, { filterMap });
30902
30916
  }
30903
30917
  async function apiGet(path) {
30904
30918
  return apiRequest("GET", path);
@@ -32719,19 +32733,22 @@ function createToolHandler(tool) {
32719
32733
  checkStoreScopedToolInput(toolAsRecord, inputAsRecord);
32720
32734
  const response = await tool.handler(input);
32721
32735
  const latency_ms = Date.now() - start;
32722
- const successEntry = {
32723
- event: "tool_call",
32724
- tool: tool.name,
32725
- status: response.ok ? "ok" : "error",
32726
- latency_ms,
32727
- request_id: response.requestId,
32728
- error: response.ok ? void 0 : response.error,
32729
- audit: isDestructive ? true : void 0,
32730
- inputs: isDestructive ? redactSecrets(input) : void 0
32731
- };
32732
- logEvent(successEntry);
32733
- if (isDestructive) {
32734
- pushAuditEntry({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...successEntry });
32736
+ const wouldLog = wouldLogToolCall({ isDestructive, isError: !response.ok });
32737
+ if (wouldLog || isDestructive) {
32738
+ const successEntry = {
32739
+ event: "tool_call",
32740
+ tool: tool.name,
32741
+ status: response.ok ? "ok" : "error",
32742
+ latency_ms,
32743
+ request_id: response.requestId,
32744
+ error: response.ok ? void 0 : response.error,
32745
+ audit: isDestructive ? true : void 0,
32746
+ inputs: isDestructive ? redactSecrets(input) : void 0
32747
+ };
32748
+ if (wouldLog) logEvent(successEntry);
32749
+ if (isDestructive) {
32750
+ pushAuditEntry({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...successEntry });
32751
+ }
32735
32752
  }
32736
32753
  if (!response.ok) {
32737
32754
  return {
@@ -32751,18 +32768,21 @@ function createToolHandler(tool) {
32751
32768
  } catch (err) {
32752
32769
  const message = err instanceof Error ? err.message : String(err);
32753
32770
  const latency_ms = Date.now() - start;
32754
- const errorEntry = {
32755
- event: "tool_call",
32756
- tool: tool.name,
32757
- status: err instanceof GuardrailError ? "guardrail_block" : "exception",
32758
- latency_ms,
32759
- error: message,
32760
- audit: isDestructive ? true : void 0,
32761
- inputs: isDestructive ? redactSecrets(input) : void 0
32762
- };
32763
- logEvent(errorEntry);
32764
- if (isDestructive) {
32765
- pushAuditEntry({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...errorEntry });
32771
+ const wouldLog = wouldLogToolCall({ isDestructive, isError: true });
32772
+ if (wouldLog || isDestructive) {
32773
+ const errorEntry = {
32774
+ event: "tool_call",
32775
+ tool: tool.name,
32776
+ status: err instanceof GuardrailError ? "guardrail_block" : "exception",
32777
+ latency_ms,
32778
+ error: message,
32779
+ audit: isDestructive ? true : void 0,
32780
+ inputs: isDestructive ? redactSecrets(input) : void 0
32781
+ };
32782
+ if (wouldLog) logEvent(errorEntry);
32783
+ if (isDestructive) {
32784
+ pushAuditEntry({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...errorEntry });
32785
+ }
32766
32786
  }
32767
32787
  return {
32768
32788
  content: [{ type: "text", text: `Error: ${message}` }],
@@ -32786,7 +32806,7 @@ function readAuditLogResource(uri) {
32786
32806
  }
32787
32807
 
32788
32808
  // src/index.ts
32789
- var version2 = true ? "0.10.6" : (await null).createRequire(import.meta.url)("../package.json").version;
32809
+ var version2 = true ? "0.10.7" : (await null).createRequire(import.meta.url)("../package.json").version;
32790
32810
  var subcommand = process.argv[2];
32791
32811
  if (subcommand === "version" || subcommand === "--version") {
32792
32812
  console.log(version2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/lemonsqueezy-mcp",
3
- "version": "0.10.6",
3
+ "version": "0.10.7",
4
4
  "mcpName": "io.github.YawLabs/lemonsqueezy-mcp",
5
5
  "description": "LemonSqueezy MCP server for managing your store from AI assistants",
6
6
  "license": "MIT",