@yawlabs/lemonsqueezy-mcp 0.10.7 → 0.10.8

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,22 @@
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.8] -- 2026-05-19
6
+
7
+ ### Security
8
+
9
+ - **Sink response body size guard extended to the error branch.** `sinkRequest` in `src/tools/sink.ts` previously only size-checked 2xx response bodies; a misbehaving sink returning a giant 4xx/5xx body could still buffer the whole thing into memory before any limit fired. A new `readBodyOrSizeError` helper pre-checks `Content-Length` against the 10 MB cap and is applied to both the error and success branches. The post-read length check on the 2xx path is retained as belt-and-braces against a lying `Content-Length`.
10
+
11
+ ### Changed
12
+
13
+ - **2xx body-read mid-stream failures now collapse to the uniform `{ ok: false, error }` shape.** Previously a socket reset partway through reading the body propagated as an exception out of `sinkRequest`, surfacing as a less-informative error via the wrapper's catch-all. The 2xx body read is now wrapped in a try/catch that returns `Sink response body read failed: <message>` -- consistent with every other failure mode in the function.
14
+ - **`src/secret.ts` cache-hit branch tightened.** The test-mode cache-hit branch previously called `announceTestModeOnce()` redundantly (the flag is set on the first miss, so the cache-hit call was dead). Removed the call and the now-obsolete defensive comment.
15
+
16
+ ### Tests
17
+
18
+ - **Sink coverage expanded** to pin `authorityClass` per tool (`read` for `ls_sink_events_list` / `ls_sink_stats`, `mutate` for `ls_sink_event_mark_processed`), 4xx-oversized-Content-Length, 2xx-lying-Content-Length, and 2xx mid-stream body-read failures. `stubFetch` gained an optional `responseHeaders` field so error-branch tests no longer override `globalThis.fetch` inline.
19
+ - **`parseCommand` contract pinned** in `src/secret.test.ts` for the four tokenizer edges that the rest of the suite only touched by accident: unterminated quote, all-quotes-collapse-to-empty, quoted-args-with-spaces (both quote styles), and quote-then-bare-word concatenation. Also added a 64 KB `maxBuffer` overflow test that exercises the `execFile` `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` rewrap path.
20
+
5
21
  ## [0.10.7] -- 2026-05-16
6
22
 
7
23
  ### Security
@@ -343,7 +359,8 @@ Hardening pass for unattended automation against live billing flows.
343
359
 
344
360
  Initial release. 59 tools covering all 17 LemonSqueezy API resources.
345
361
 
346
- [Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.7...HEAD
362
+ [Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.8...HEAD
363
+ [0.10.8]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.7...v0.10.8
347
364
  [0.10.7]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.6...v0.10.7
348
365
  [0.10.6]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.5...v0.10.6
349
366
  [0.10.5]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.4...v0.10.5
package/dist/index.js CHANGED
@@ -30653,10 +30653,7 @@ async function loadApiKey() {
30653
30653
  if (testRaw && testRaw.trim() !== "") {
30654
30654
  const fingerprint2 = fingerprintFor("test", testRaw);
30655
30655
  const hit2 = fromCache(fingerprint2);
30656
- if (hit2 !== null) {
30657
- announceTestModeOnce();
30658
- return hit2;
30659
- }
30656
+ if (hit2 !== null) return hit2;
30660
30657
  announceTestModeOnce();
30661
30658
  intoCache(fingerprint2, testRaw);
30662
30659
  return testRaw;
@@ -31863,6 +31860,16 @@ function loadSinkConfig() {
31863
31860
  function isToolHandlerResponse(value) {
31864
31861
  return "ok" in value;
31865
31862
  }
31863
+ async function readBodyOrSizeError(res) {
31864
+ const contentLength = res.headers.get("content-length");
31865
+ if (contentLength) {
31866
+ const declared = Number.parseInt(contentLength, 10);
31867
+ if (Number.isFinite(declared) && declared > MAX_BODY_SIZE_BYTES) {
31868
+ return { ok: false, declared };
31869
+ }
31870
+ }
31871
+ return { ok: true, text: await res.text() };
31872
+ }
31866
31873
  function buildSinkPath(path, params) {
31867
31874
  const parts = [];
31868
31875
  for (const [k, v] of Object.entries(params)) {
@@ -31897,7 +31904,12 @@ async function sinkRequest(config2, method, pathAndQuery) {
31897
31904
  if (!res.ok) {
31898
31905
  let body = "";
31899
31906
  try {
31900
- body = await res.text();
31907
+ const readResult2 = await readBodyOrSizeError(res);
31908
+ if (readResult2.ok) {
31909
+ body = readResult2.text;
31910
+ } else {
31911
+ body = `[body too large: ${readResult2.declared} bytes exceeds ${MAX_BODY_SIZE_BYTES} byte limit]`;
31912
+ }
31901
31913
  } catch {
31902
31914
  }
31903
31915
  let detail = body;
@@ -31923,7 +31935,20 @@ async function sinkRequest(config2, method, pathAndQuery) {
31923
31935
  error: `Sink returned ${res.status}: ${detail || res.statusText}`
31924
31936
  };
31925
31937
  }
31926
- const text = await res.text();
31938
+ let readResult;
31939
+ try {
31940
+ readResult = await readBodyOrSizeError(res);
31941
+ } catch (err) {
31942
+ const message = err instanceof Error ? err.message : String(err);
31943
+ return { ok: false, error: `Sink response body read failed: ${message}` };
31944
+ }
31945
+ if (!readResult.ok) {
31946
+ return {
31947
+ ok: false,
31948
+ error: `Sink response body too large: ${readResult.declared} bytes exceeds ${MAX_BODY_SIZE_BYTES} byte limit`
31949
+ };
31950
+ }
31951
+ const text = readResult.text;
31927
31952
  if (!text.trim()) return { ok: true, data: {} };
31928
31953
  if (text.length > MAX_BODY_SIZE_BYTES) {
31929
31954
  return {
@@ -32806,7 +32831,7 @@ function readAuditLogResource(uri) {
32806
32831
  }
32807
32832
 
32808
32833
  // src/index.ts
32809
- var version2 = true ? "0.10.7" : (await null).createRequire(import.meta.url)("../package.json").version;
32834
+ var version2 = true ? "0.10.8" : (await null).createRequire(import.meta.url)("../package.json").version;
32810
32835
  var subcommand = process.argv[2];
32811
32836
  if (subcommand === "version" || subcommand === "--version") {
32812
32837
  console.log(version2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/lemonsqueezy-mcp",
3
- "version": "0.10.7",
3
+ "version": "0.10.8",
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",