@yawlabs/lemonsqueezy-mcp 0.10.8 → 0.10.10
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 +33 -1
- package/dist/index.js +15 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
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.10] -- 2026-05-22
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`release.sh` no longer demands a workstation npm session for CI-publish flows.** The pre-flight `npm whoami` gate was a vestige of the local-only-deploy era (added in `53de878`); when CI publishing was restored in `e3ef87a` the gate was kept but the publish itself moved to CI's org-level `NPM_TOKEN`, so the workstation never uses that credential. Blocked the v0.10.10 release attempt with "npm is not authenticated" despite the intended publish path not needing it. Mirrors `tailscale-mcp/release.sh`, which never had the gate.
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- **Webhook `url` restricted to http/https schemes.** `ls_create_webhook` and `ls_update_webhook` previously accepted any string up to 10k chars; `z.string().url()` alone would have additionally accepted every URL-parseable scheme (`mailto:`, `file:`, `javascript:`, `ftp:`, `chrome-extension:`, ...). Anything non-http(s) stored as a webhook target is unreachable in the best case and an injection sink in the worst. The new `httpsUrlSchema` chains `.url()` with a `.refine()` enforcing `^https?://`, applied on both tools.
|
|
14
|
+
- **`ls_update_webhook` secret rotation gated as destructive.** Rotating the signing secret silently breaks signature verification on every receiver until they redeploy with the new value. The tool now declares an `isDestructive` predicate flagging any `secret` change, routing those calls through `checkDestructiveRateLimit` + audit logging via the wrapper's existing duck-typed integration. URL and events changes stay on the regular path.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`ls_update_webhook` rejects empty PATCH locally.** Calling with only `webhookId` and no fields to change previously sent an empty `attributes: {}` PATCH that the API would 422 with a less clear message. The handler now throws `"ls_update_webhook requires at least one of: url, events, secret"` before the round-trip.
|
|
19
|
+
- **`ls_update_webhook` description warns about secret rotation.** The tool-level description now calls out that setting `secret` is destructive (rate-limited and audited) so LLM callers see the consequence before invoking.
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
|
|
23
|
+
- **Regression coverage for the `events.min(1)` fix** (`f7374bd`): schema-rejection tests on both `ls_create_webhook` and `ls_update_webhook` lock in the empty-array rejection.
|
|
24
|
+
- **URL validation coverage**: non-URL string rejection on both tools, four non-http(s) schemes (`mailto:`, `file:`, `javascript:`, `ftp:`) rejected on both tools, http and https explicit acceptance.
|
|
25
|
+
- **`ls_update_webhook` predicate tests** mirror the `ls_update_license_key` shape: secret-only as destructive, url-only and events-only as non-destructive, empty input as non-destructive.
|
|
26
|
+
- **`ls_update_webhook` URL-encoding** test added in parallel to the existing `ls_delete_webhook` coverage.
|
|
27
|
+
- **Handler-side empty-PATCH rejection** test asserts the new error message.
|
|
28
|
+
|
|
29
|
+
## [0.10.9] -- 2026-05-22
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **Webhook `events` array now requires at least one entry.** `ls_create_webhook` and `ls_update_webhook` previously let an empty array pass local Zod validation, so a no-op webhook configuration only failed at the LemonSqueezy API as a 422. The new `.min(1)` makes the rejection local with a clearer message; on update, the field stays optional but, when supplied, must be non-empty.
|
|
34
|
+
|
|
5
35
|
## [0.10.8] -- 2026-05-19
|
|
6
36
|
|
|
7
37
|
### Security
|
|
@@ -359,7 +389,9 @@ Hardening pass for unattended automation against live billing flows.
|
|
|
359
389
|
|
|
360
390
|
Initial release. 59 tools covering all 17 LemonSqueezy API resources.
|
|
361
391
|
|
|
362
|
-
[Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.
|
|
392
|
+
[Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.9...HEAD
|
|
393
|
+
[0.10.10]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.9...v0.10.10
|
|
394
|
+
[0.10.9]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.8...v0.10.9
|
|
363
395
|
[0.10.8]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.7...v0.10.8
|
|
364
396
|
[0.10.7]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.6...v0.10.7
|
|
365
397
|
[0.10.6]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.5...v0.10.6
|
package/dist/index.js
CHANGED
|
@@ -32541,6 +32541,7 @@ var variantTools = [
|
|
|
32541
32541
|
];
|
|
32542
32542
|
|
|
32543
32543
|
// src/tools/webhooks.ts
|
|
32544
|
+
var httpsUrlSchema = external_exports3.string().url().max(1e4).refine((u) => /^https?:\/\//i.test(u), { message: "url must start with http:// or https://" });
|
|
32544
32545
|
var webhookTools = [
|
|
32545
32546
|
{
|
|
32546
32547
|
name: "ls_get_webhook",
|
|
@@ -32591,8 +32592,8 @@ var webhookTools = [
|
|
|
32591
32592
|
},
|
|
32592
32593
|
inputSchema: external_exports3.object({
|
|
32593
32594
|
storeId: lsIdSchema.describe("The store ID"),
|
|
32594
|
-
url:
|
|
32595
|
-
events: external_exports3.array(external_exports3.string()).describe(
|
|
32595
|
+
url: httpsUrlSchema.describe("The URL to send webhook events to (must be a valid http/https URL)"),
|
|
32596
|
+
events: external_exports3.array(external_exports3.string()).min(1).describe(
|
|
32596
32597
|
"Event types to subscribe to (e.g. ['order_created', 'subscription_created', 'subscription_updated', 'subscription_cancelled', 'subscription_payment_success', 'subscription_payment_failed', 'license_key_created'])"
|
|
32597
32598
|
),
|
|
32598
32599
|
secret: external_exports3.string().min(6).max(40).describe("A signing secret for verifying webhook payloads")
|
|
@@ -32616,7 +32617,7 @@ var webhookTools = [
|
|
|
32616
32617
|
{
|
|
32617
32618
|
name: "ls_update_webhook",
|
|
32618
32619
|
authorityClass: "webhook",
|
|
32619
|
-
description: "Update an existing webhook's URL, events, or secret.",
|
|
32620
|
+
description: "Update an existing webhook's URL, events, or secret. Setting `secret` rotates the signing secret and breaks signature verification on the receiver until they update their copy -- this is treated as destructive (rate-limited and audited).",
|
|
32620
32621
|
annotations: {
|
|
32621
32622
|
title: "Update webhook",
|
|
32622
32623
|
readOnlyHint: false,
|
|
@@ -32624,10 +32625,15 @@ var webhookTools = [
|
|
|
32624
32625
|
idempotentHint: true,
|
|
32625
32626
|
openWorldHint: true
|
|
32626
32627
|
},
|
|
32628
|
+
// Rotating the signing secret silently breaks signature verification on
|
|
32629
|
+
// every receiver until they redeploy with the new value. URL and events
|
|
32630
|
+
// changes are observable on the next event but don't break anything in
|
|
32631
|
+
// flight, so they stay on the regular path.
|
|
32632
|
+
isDestructive: (input) => input.secret !== void 0,
|
|
32627
32633
|
inputSchema: external_exports3.object({
|
|
32628
32634
|
webhookId: lsIdSchema.describe("The webhook ID to update"),
|
|
32629
|
-
url:
|
|
32630
|
-
events: external_exports3.array(external_exports3.string()).optional().describe("Updated list of event types to subscribe to"),
|
|
32635
|
+
url: httpsUrlSchema.optional().describe("New URL to send webhook events to (must be a valid http/https URL)"),
|
|
32636
|
+
events: external_exports3.array(external_exports3.string()).min(1).optional().describe("Updated list of event types to subscribe to"),
|
|
32631
32637
|
secret: external_exports3.string().min(6).max(40).optional().describe("New signing secret")
|
|
32632
32638
|
}),
|
|
32633
32639
|
handler: async (input) => {
|
|
@@ -32635,6 +32641,9 @@ var webhookTools = [
|
|
|
32635
32641
|
if (input.url !== void 0) attributes.url = input.url;
|
|
32636
32642
|
if (input.events !== void 0) attributes.events = input.events;
|
|
32637
32643
|
if (input.secret !== void 0) attributes.secret = input.secret;
|
|
32644
|
+
if (Object.keys(attributes).length === 0) {
|
|
32645
|
+
throw new Error("ls_update_webhook requires at least one of: url, events, secret");
|
|
32646
|
+
}
|
|
32638
32647
|
return apiPatch(`/webhooks/${encodePath(input.webhookId)}`, {
|
|
32639
32648
|
data: {
|
|
32640
32649
|
type: "webhooks",
|
|
@@ -32831,7 +32840,7 @@ function readAuditLogResource(uri) {
|
|
|
32831
32840
|
}
|
|
32832
32841
|
|
|
32833
32842
|
// src/index.ts
|
|
32834
|
-
var version2 = true ? "0.10.
|
|
32843
|
+
var version2 = true ? "0.10.10" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
32835
32844
|
var subcommand = process.argv[2];
|
|
32836
32845
|
if (subcommand === "version" || subcommand === "--version") {
|
|
32837
32846
|
console.log(version2);
|
package/package.json
CHANGED