@yawlabs/lemonsqueezy-mcp 0.5.0 → 0.6.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/README.md +19 -2
- package/dist/index.js +102 -35
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -174,9 +174,9 @@ All configuration is via environment variables. Only `LEMONSQUEEZY_API_KEY` (or
|
|
|
174
174
|
| --- | --- |
|
|
175
175
|
| `LEMONSQUEEZY_API_KEY` | LemonSqueezy API token. |
|
|
176
176
|
| `LEMONSQUEEZY_API_KEY_COMMAND` | Command whose stdout produces the API key. Overrides `LEMONSQUEEZY_API_KEY`. Output is cached for 1 hour. Use this to pull short-lived credentials from a vault (`op read`, `gcloud secrets versions access`, etc.) without writing them to env vars. |
|
|
177
|
-
| `LEMONSQUEEZY_ALLOWED_STORE_IDS` | Comma-separated allowlist of store IDs. When set
|
|
177
|
+
| `LEMONSQUEEZY_ALLOWED_STORE_IDS` | Comma-separated allowlist of store IDs. When set: (1) any tool whose input includes a `storeId` rejects calls to a non-allowed store; (2) tools that *accept* a `storeId` filter (e.g. `ls_list_orders`, `ls_list_subscriptions`) require it — calls without one are blocked so a missing filter cannot return data from every store the API key can see. Tools with no `storeId` field at all (e.g. `ls_refund_order`, `ls_list_stores`) are not gated by this — pair with `LEMONSQUEEZY_MAX_REFUND_AMOUNT_CENTS` and `LEMONSQUEEZY_DESTRUCTIVE_RATE_LIMIT`. |
|
|
178
178
|
| `LEMONSQUEEZY_MAX_REFUND_AMOUNT_CENTS` | Rejects `ls_refund_order` and `ls_refund_subscription_invoice` calls above this amount. |
|
|
179
|
-
| `LEMONSQUEEZY_DESTRUCTIVE_RATE_LIMIT` | Max destructive tool calls per 60-second rolling window. In-process limit — per MCP server instance, not global. |
|
|
179
|
+
| `LEMONSQUEEZY_DESTRUCTIVE_RATE_LIMIT` | Max destructive tool calls per 60-second rolling window. In-process limit — per MCP server instance, not global; each `npx` cold start resets the window. Counts include `ls_update_license_key` calls that set `disabled: true`. |
|
|
180
180
|
| `LEMONSQUEEZY_LOG=json` | Emit one JSON log line to stderr per tool and HTTP call. Destructive calls are tagged `audit: true` and include their inputs. |
|
|
181
181
|
|
|
182
182
|
### Logging format
|
|
@@ -214,6 +214,23 @@ npm test # full unit + handler suite
|
|
|
214
214
|
npm run test:integration # requires LEMONSQUEEZY_TEST_API_KEY + LEMONSQUEEZY_TEST_STORE_ID
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
## Releasing
|
|
218
|
+
|
|
219
|
+
Releases are cut locally — there is no CI pipeline. From a clean checkout of `main`:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
./release.sh 0.6.0
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The script lints, tests, builds, bumps the version, commits and tags, pushes to `origin`, publishes to npm, and creates a GitHub release. Each step is idempotent — re-running with the same version after a partial failure resumes from where it stopped.
|
|
226
|
+
|
|
227
|
+
One-time setup on the release machine:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm login --auth-type=web # publisher of @yawlabs/lemonsqueezy-mcp
|
|
231
|
+
gh auth login # GitHub CLI for the release-creation step
|
|
232
|
+
```
|
|
233
|
+
|
|
217
234
|
## License
|
|
218
235
|
|
|
219
236
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -30176,6 +30176,24 @@ function checkDestructiveRateLimit(now = Date.now()) {
|
|
|
30176
30176
|
}
|
|
30177
30177
|
destructiveTimestamps.push(now);
|
|
30178
30178
|
}
|
|
30179
|
+
function isStoreAllowlistActive() {
|
|
30180
|
+
return loadOptions().allowedStoreIds !== null;
|
|
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");
|
|
30191
|
+
}
|
|
30192
|
+
}
|
|
30193
|
+
function isDestructiveCall(tool, input) {
|
|
30194
|
+
if (typeof tool.isDestructive === "function") return tool.isDestructive(input);
|
|
30195
|
+
return tool.annotations?.destructiveHint === true;
|
|
30196
|
+
}
|
|
30179
30197
|
|
|
30180
30198
|
// src/logger.ts
|
|
30181
30199
|
function isEnabled() {
|
|
@@ -30186,6 +30204,25 @@ function logEvent(entry) {
|
|
|
30186
30204
|
try {
|
|
30187
30205
|
const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
|
|
30188
30206
|
process.stderr.write(`${line}
|
|
30207
|
+
`);
|
|
30208
|
+
return;
|
|
30209
|
+
} catch {
|
|
30210
|
+
}
|
|
30211
|
+
try {
|
|
30212
|
+
const fallback = JSON.stringify({
|
|
30213
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30214
|
+
event: entry.event,
|
|
30215
|
+
tool: entry.tool,
|
|
30216
|
+
method: entry.method,
|
|
30217
|
+
path: entry.path,
|
|
30218
|
+
status: entry.status,
|
|
30219
|
+
latency_ms: entry.latency_ms,
|
|
30220
|
+
request_id: entry.request_id,
|
|
30221
|
+
audit: entry.audit,
|
|
30222
|
+
error: entry.error,
|
|
30223
|
+
log_error: "inputs_not_serializable"
|
|
30224
|
+
});
|
|
30225
|
+
process.stderr.write(`${fallback}
|
|
30189
30226
|
`);
|
|
30190
30227
|
} catch {
|
|
30191
30228
|
}
|
|
@@ -30326,6 +30363,9 @@ async function loadApiKey() {
|
|
|
30326
30363
|
|
|
30327
30364
|
// src/api.ts
|
|
30328
30365
|
var BASE_URL = "https://api.lemonsqueezy.com/v1";
|
|
30366
|
+
function encodePath(segment) {
|
|
30367
|
+
return encodeURIComponent(String(segment));
|
|
30368
|
+
}
|
|
30329
30369
|
function buildQuery(params) {
|
|
30330
30370
|
if (!params) return "";
|
|
30331
30371
|
const parts = [];
|
|
@@ -30362,7 +30402,7 @@ async function apiRequest(method, path, body) {
|
|
|
30362
30402
|
headers["Content-Type"] = "application/vnd.api+json";
|
|
30363
30403
|
fetchBody = JSON.stringify(body);
|
|
30364
30404
|
}
|
|
30365
|
-
const url2 =
|
|
30405
|
+
const url2 = `${BASE_URL}${path}`;
|
|
30366
30406
|
const idempotent = method === "GET" || method === "DELETE";
|
|
30367
30407
|
let res;
|
|
30368
30408
|
try {
|
|
@@ -30430,7 +30470,8 @@ async function apiRequest(method, path, body) {
|
|
|
30430
30470
|
if (res.status === 204) {
|
|
30431
30471
|
return { ok: true, status: res.status, requestId };
|
|
30432
30472
|
}
|
|
30433
|
-
const
|
|
30473
|
+
const bodyText = await res.text();
|
|
30474
|
+
const data = bodyText.trim() ? JSON.parse(bodyText) : void 0;
|
|
30434
30475
|
return { ok: true, status: res.status, data, requestId };
|
|
30435
30476
|
}
|
|
30436
30477
|
async function licenseRequest(path, body) {
|
|
@@ -30518,13 +30559,14 @@ async function licenseRequest(path, body) {
|
|
|
30518
30559
|
latency_ms,
|
|
30519
30560
|
request_id: requestId
|
|
30520
30561
|
});
|
|
30521
|
-
const
|
|
30562
|
+
const bodyText = await res.text();
|
|
30563
|
+
const data = bodyText.trim() ? JSON.parse(bodyText) : void 0;
|
|
30522
30564
|
return { ok: true, status: res.status, data, requestId };
|
|
30523
30565
|
}
|
|
30524
30566
|
function getHandler(endpoint, idField) {
|
|
30525
30567
|
return async (input) => {
|
|
30526
30568
|
const query = buildQuery({ include: input.include?.split(",") });
|
|
30527
|
-
return apiGet(`${endpoint}/${input[idField]}${query}`);
|
|
30569
|
+
return apiGet(`${endpoint}/${encodePath(input[idField])}${query}`);
|
|
30528
30570
|
};
|
|
30529
30571
|
}
|
|
30530
30572
|
function listHandler(endpoint, filterMap = {}) {
|
|
@@ -30662,13 +30704,11 @@ var checkoutTools = [
|
|
|
30662
30704
|
const checkoutData = {};
|
|
30663
30705
|
if (input.email !== void 0) checkoutData.email = input.email;
|
|
30664
30706
|
if (input.name !== void 0) checkoutData.name = input.name;
|
|
30665
|
-
if (input.billingAddressCountry !== void 0)
|
|
30666
|
-
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
zip: input.billingAddressZip
|
|
30671
|
-
};
|
|
30707
|
+
if (input.billingAddressCountry !== void 0 || input.billingAddressZip !== void 0) {
|
|
30708
|
+
const billingAddress = {};
|
|
30709
|
+
if (input.billingAddressCountry !== void 0) billingAddress.country = input.billingAddressCountry;
|
|
30710
|
+
if (input.billingAddressZip !== void 0) billingAddress.zip = input.billingAddressZip;
|
|
30711
|
+
checkoutData.billing_address = billingAddress;
|
|
30672
30712
|
}
|
|
30673
30713
|
if (input.taxNumber !== void 0) checkoutData.tax_number = input.taxNumber;
|
|
30674
30714
|
if (input.discountCode !== void 0) checkoutData.discount_code = input.discountCode;
|
|
@@ -30774,6 +30814,12 @@ var customerTools = [
|
|
|
30774
30814
|
idempotentHint: true,
|
|
30775
30815
|
openWorldHint: true
|
|
30776
30816
|
},
|
|
30817
|
+
// Setting status to "archived" via this tool is the same operation as
|
|
30818
|
+
// ls_archive_customer and must engage the same rate limiter / audit log;
|
|
30819
|
+
// otherwise the dedicated archive tool's destructive flag becomes a side
|
|
30820
|
+
// channel anyone can route around. Other field edits (name, email,
|
|
30821
|
+
// address) stay on the regular path.
|
|
30822
|
+
isDestructive: (input) => input.status === "archived",
|
|
30777
30823
|
inputSchema: external_exports3.object({
|
|
30778
30824
|
customerId: external_exports3.string().max(1e4).describe("The customer ID to update"),
|
|
30779
30825
|
name: external_exports3.string().max(1e4).optional().describe("New name"),
|
|
@@ -30791,7 +30837,7 @@ var customerTools = [
|
|
|
30791
30837
|
if (input.region !== void 0) attributes.region = input.region;
|
|
30792
30838
|
if (input.country !== void 0) attributes.country = input.country;
|
|
30793
30839
|
if (input.status !== void 0) attributes.status = input.status;
|
|
30794
|
-
return apiPatch(`/customers/${input.customerId}`, {
|
|
30840
|
+
return apiPatch(`/customers/${encodePath(input.customerId)}`, {
|
|
30795
30841
|
data: {
|
|
30796
30842
|
type: "customers",
|
|
30797
30843
|
id: input.customerId,
|
|
@@ -30806,7 +30852,7 @@ var customerTools = [
|
|
|
30806
30852
|
annotations: {
|
|
30807
30853
|
title: "Archive customer",
|
|
30808
30854
|
readOnlyHint: false,
|
|
30809
|
-
destructiveHint:
|
|
30855
|
+
destructiveHint: true,
|
|
30810
30856
|
idempotentHint: true,
|
|
30811
30857
|
openWorldHint: true
|
|
30812
30858
|
},
|
|
@@ -30814,7 +30860,7 @@ var customerTools = [
|
|
|
30814
30860
|
customerId: external_exports3.string().max(1e4).describe("The customer ID to archive")
|
|
30815
30861
|
}),
|
|
30816
30862
|
handler: async (input) => {
|
|
30817
|
-
return apiPatch(`/customers/${input.customerId}`, {
|
|
30863
|
+
return apiPatch(`/customers/${encodePath(input.customerId)}`, {
|
|
30818
30864
|
data: {
|
|
30819
30865
|
type: "customers",
|
|
30820
30866
|
id: input.customerId,
|
|
@@ -30972,7 +31018,7 @@ var discountTools = [
|
|
|
30972
31018
|
discountId: external_exports3.string().max(1e4).describe("The discount ID to delete")
|
|
30973
31019
|
}),
|
|
30974
31020
|
handler: async (input) => {
|
|
30975
|
-
return apiDelete(`/discounts/${input.discountId}`);
|
|
31021
|
+
return apiDelete(`/discounts/${encodePath(input.discountId)}`);
|
|
30976
31022
|
}
|
|
30977
31023
|
}
|
|
30978
31024
|
];
|
|
@@ -31103,7 +31149,7 @@ var licenseKeyTools = [
|
|
|
31103
31149
|
},
|
|
31104
31150
|
{
|
|
31105
31151
|
name: "ls_update_license_key",
|
|
31106
|
-
description: "Update a license key's activation limit, expiry date, or disabled status.",
|
|
31152
|
+
description: "Update a license key's activation limit, expiry date, or disabled status. Setting `disabled: true` revokes customer access and is treated as destructive (rate-limited and audited).",
|
|
31107
31153
|
annotations: {
|
|
31108
31154
|
title: "Update license key",
|
|
31109
31155
|
readOnlyHint: false,
|
|
@@ -31111,18 +31157,23 @@ var licenseKeyTools = [
|
|
|
31111
31157
|
idempotentHint: true,
|
|
31112
31158
|
openWorldHint: true
|
|
31113
31159
|
},
|
|
31160
|
+
// Disabling a license key revokes a customer's access. Tag the call as
|
|
31161
|
+
// destructive only when `disabled: true` so the rate limiter and audit log
|
|
31162
|
+
// engage on revocation, while benign edits (expiry, activation limit) stay
|
|
31163
|
+
// on the regular path.
|
|
31164
|
+
isDestructive: (input) => input.disabled === true,
|
|
31114
31165
|
inputSchema: external_exports3.object({
|
|
31115
31166
|
licenseKeyId: external_exports3.string().max(1e4).describe("The license key ID to update"),
|
|
31116
31167
|
activationLimit: external_exports3.number().int().min(0).optional().describe("Maximum number of activations allowed (0 = unlimited)"),
|
|
31117
31168
|
disabled: external_exports3.boolean().optional().describe("Set to true to disable this license key"),
|
|
31118
|
-
expiresAt: external_exports3.string().max(1e4).optional().describe("Expiry date (ISO 8601 format). Set to null to remove expiry.")
|
|
31169
|
+
expiresAt: external_exports3.string().max(1e4).nullable().optional().describe("Expiry date (ISO 8601 format). Set to null to remove expiry.")
|
|
31119
31170
|
}),
|
|
31120
31171
|
handler: async (input) => {
|
|
31121
31172
|
const attributes = {};
|
|
31122
31173
|
if (input.activationLimit !== void 0) attributes.activation_limit = input.activationLimit;
|
|
31123
31174
|
if (input.disabled !== void 0) attributes.disabled = input.disabled;
|
|
31124
31175
|
if (input.expiresAt !== void 0) attributes.expires_at = input.expiresAt;
|
|
31125
|
-
return apiPatch(`/license-keys/${input.licenseKeyId}`, {
|
|
31176
|
+
return apiPatch(`/license-keys/${encodePath(input.licenseKeyId)}`, {
|
|
31126
31177
|
data: {
|
|
31127
31178
|
type: "license-keys",
|
|
31128
31179
|
id: input.licenseKeyId,
|
|
@@ -31312,7 +31363,7 @@ var orderTools = [
|
|
|
31312
31363
|
if (input.notes !== void 0) params.set("notes", input.notes);
|
|
31313
31364
|
if (input.locale !== void 0) params.set("locale", input.locale);
|
|
31314
31365
|
const qs = params.toString();
|
|
31315
|
-
return apiPost(`/orders/${input.orderId}/generate-invoice${qs ? `?${qs}` : ""}`);
|
|
31366
|
+
return apiPost(`/orders/${encodePath(input.orderId)}/generate-invoice${qs ? `?${qs}` : ""}`);
|
|
31316
31367
|
}
|
|
31317
31368
|
},
|
|
31318
31369
|
{
|
|
@@ -31331,7 +31382,7 @@ var orderTools = [
|
|
|
31331
31382
|
}),
|
|
31332
31383
|
handler: async (input) => {
|
|
31333
31384
|
checkRefundAmount(input.amount);
|
|
31334
|
-
return apiPost(`/orders/${input.orderId}/refund`, {
|
|
31385
|
+
return apiPost(`/orders/${encodePath(input.orderId)}/refund`, {
|
|
31335
31386
|
data: { type: "orders", id: input.orderId, attributes: { amount: input.amount } }
|
|
31336
31387
|
});
|
|
31337
31388
|
}
|
|
@@ -31531,7 +31582,9 @@ var subscriptionInvoiceTools = [
|
|
|
31531
31582
|
if (input.notes !== void 0) params.set("notes", input.notes);
|
|
31532
31583
|
if (input.locale !== void 0) params.set("locale", input.locale);
|
|
31533
31584
|
const qs = params.toString();
|
|
31534
|
-
return apiPost(
|
|
31585
|
+
return apiPost(
|
|
31586
|
+
`/subscription-invoices/${encodePath(input.subscriptionInvoiceId)}/generate-invoice${qs ? `?${qs}` : ""}`
|
|
31587
|
+
);
|
|
31535
31588
|
}
|
|
31536
31589
|
},
|
|
31537
31590
|
{
|
|
@@ -31550,7 +31603,7 @@ var subscriptionInvoiceTools = [
|
|
|
31550
31603
|
}),
|
|
31551
31604
|
handler: async (input) => {
|
|
31552
31605
|
checkRefundAmount(input.amount);
|
|
31553
|
-
return apiPost(`/subscription-invoices/${input.subscriptionInvoiceId}/refund`, {
|
|
31606
|
+
return apiPost(`/subscription-invoices/${encodePath(input.subscriptionInvoiceId)}/refund`, {
|
|
31554
31607
|
data: {
|
|
31555
31608
|
type: "subscription-invoices",
|
|
31556
31609
|
id: input.subscriptionInvoiceId,
|
|
@@ -31604,7 +31657,7 @@ var subscriptionItemTools = [
|
|
|
31604
31657
|
annotations: {
|
|
31605
31658
|
title: "Update subscription item",
|
|
31606
31659
|
readOnlyHint: false,
|
|
31607
|
-
destructiveHint:
|
|
31660
|
+
destructiveHint: true,
|
|
31608
31661
|
idempotentHint: true,
|
|
31609
31662
|
openWorldHint: true
|
|
31610
31663
|
},
|
|
@@ -31613,7 +31666,7 @@ var subscriptionItemTools = [
|
|
|
31613
31666
|
quantity: external_exports3.number().int().min(1).describe("New quantity for the subscription item")
|
|
31614
31667
|
}),
|
|
31615
31668
|
handler: async (input) => {
|
|
31616
|
-
return apiPatch(`/subscription-items/${input.subscriptionItemId}`, {
|
|
31669
|
+
return apiPatch(`/subscription-items/${encodePath(input.subscriptionItemId)}`, {
|
|
31617
31670
|
data: {
|
|
31618
31671
|
type: "subscription-items",
|
|
31619
31672
|
id: input.subscriptionItemId,
|
|
@@ -31636,7 +31689,7 @@ var subscriptionItemTools = [
|
|
|
31636
31689
|
subscriptionItemId: external_exports3.string().max(1e4).describe("The subscription item ID")
|
|
31637
31690
|
}),
|
|
31638
31691
|
handler: async (input) => {
|
|
31639
|
-
return apiGet(`/subscription-items/${input.subscriptionItemId}/current-usage`);
|
|
31692
|
+
return apiGet(`/subscription-items/${encodePath(input.subscriptionItemId)}/current-usage`);
|
|
31640
31693
|
}
|
|
31641
31694
|
}
|
|
31642
31695
|
];
|
|
@@ -31705,9 +31758,20 @@ var subscriptionTools = [
|
|
|
31705
31758
|
idempotentHint: true,
|
|
31706
31759
|
openWorldHint: true
|
|
31707
31760
|
},
|
|
31761
|
+
// A pause (void/free) or plan switch is the customer-impacting subset of
|
|
31762
|
+
// this tool's surface; treat those calls as destructive so they engage
|
|
31763
|
+
// the rate limiter and audit log. Intentional exclusions, all of which
|
|
31764
|
+
// either reverse a destructive action or are billing-neutral:
|
|
31765
|
+
// - pause === "resume" un-pause, restoring access
|
|
31766
|
+
// - cancelled === false un-cancel before expiry, restoring access
|
|
31767
|
+
// - billingAnchor changes anchor day, no immediate charge
|
|
31768
|
+
// - trialEndsAt extending or ending a trial
|
|
31769
|
+
// - invoiceImmediately toggle on the next prorated edit
|
|
31770
|
+
// - disableProrations toggle on plan-change behavior
|
|
31771
|
+
isDestructive: (input) => typeof input.pause === "string" && input.pause !== "resume" || typeof input.variantId === "string",
|
|
31708
31772
|
inputSchema: external_exports3.object({
|
|
31709
31773
|
subscriptionId: external_exports3.string().max(1e4).describe("The subscription ID to update"),
|
|
31710
|
-
variantId: external_exports3.string().max(1e4).optional().describe("New variant ID for plan switching"),
|
|
31774
|
+
variantId: external_exports3.string().max(1e4).regex(/^[1-9]\d*$/, "variantId must be a positive integer string (e.g. '12345')").optional().describe("New variant ID for plan switching"),
|
|
31711
31775
|
pause: external_exports3.enum(["void", "free", "resume"]).optional().describe("Pause mode: 'void' (pause, skip billing), 'free' (pause, keep access free), or 'resume' to unpause"),
|
|
31712
31776
|
cancelled: external_exports3.literal(false).optional().describe(
|
|
31713
31777
|
"Set to false to un-cancel a subscription before it expires. To cancel, use ls_cancel_subscription instead."
|
|
@@ -31726,7 +31790,7 @@ var subscriptionTools = [
|
|
|
31726
31790
|
if (input.invoiceImmediately !== void 0) attributes.invoice_immediately = input.invoiceImmediately;
|
|
31727
31791
|
if (input.disableProrations !== void 0) attributes.disable_prorations = input.disableProrations;
|
|
31728
31792
|
if (input.trialEndsAt !== void 0) attributes.trial_ends_at = input.trialEndsAt;
|
|
31729
|
-
return apiPatch(`/subscriptions/${input.subscriptionId}`, {
|
|
31793
|
+
return apiPatch(`/subscriptions/${encodePath(input.subscriptionId)}`, {
|
|
31730
31794
|
data: {
|
|
31731
31795
|
type: "subscriptions",
|
|
31732
31796
|
id: input.subscriptionId,
|
|
@@ -31749,7 +31813,7 @@ var subscriptionTools = [
|
|
|
31749
31813
|
subscriptionId: external_exports3.string().max(1e4).describe("The subscription ID to cancel")
|
|
31750
31814
|
}),
|
|
31751
31815
|
handler: async (input) => {
|
|
31752
|
-
return apiDelete(`/subscriptions/${input.subscriptionId}`);
|
|
31816
|
+
return apiDelete(`/subscriptions/${encodePath(input.subscriptionId)}`);
|
|
31753
31817
|
}
|
|
31754
31818
|
}
|
|
31755
31819
|
];
|
|
@@ -31796,7 +31860,10 @@ var usageRecordTools = [
|
|
|
31796
31860
|
annotations: {
|
|
31797
31861
|
title: "Create usage record",
|
|
31798
31862
|
readOnlyHint: false,
|
|
31799
|
-
|
|
31863
|
+
// Usage records feed the meter that determines the customer's bill --
|
|
31864
|
+
// 'increment' adds to current usage, 'set' overwrites it. Either mode
|
|
31865
|
+
// moves real money, so engage the rate limiter and audit log.
|
|
31866
|
+
destructiveHint: true,
|
|
31800
31867
|
idempotentHint: false,
|
|
31801
31868
|
openWorldHint: true
|
|
31802
31869
|
},
|
|
@@ -31973,7 +32040,7 @@ var webhookTools = [
|
|
|
31973
32040
|
if (input.url !== void 0) attributes.url = input.url;
|
|
31974
32041
|
if (input.events !== void 0) attributes.events = input.events;
|
|
31975
32042
|
if (input.secret !== void 0) attributes.secret = input.secret;
|
|
31976
|
-
return apiPatch(`/webhooks/${input.webhookId}`, {
|
|
32043
|
+
return apiPatch(`/webhooks/${encodePath(input.webhookId)}`, {
|
|
31977
32044
|
data: {
|
|
31978
32045
|
type: "webhooks",
|
|
31979
32046
|
id: input.webhookId,
|
|
@@ -31996,13 +32063,13 @@ var webhookTools = [
|
|
|
31996
32063
|
webhookId: external_exports3.string().max(1e4).describe("The webhook ID to delete")
|
|
31997
32064
|
}),
|
|
31998
32065
|
handler: async (input) => {
|
|
31999
|
-
return apiDelete(`/webhooks/${input.webhookId}`);
|
|
32066
|
+
return apiDelete(`/webhooks/${encodePath(input.webhookId)}`);
|
|
32000
32067
|
}
|
|
32001
32068
|
}
|
|
32002
32069
|
];
|
|
32003
32070
|
|
|
32004
32071
|
// src/index.ts
|
|
32005
|
-
var version2 = true ? "0.
|
|
32072
|
+
var version2 = true ? "0.6.1" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
32006
32073
|
var subcommand = process.argv[2];
|
|
32007
32074
|
if (subcommand === "version" || subcommand === "--version") {
|
|
32008
32075
|
console.log(version2);
|
|
@@ -32036,18 +32103,18 @@ var server = new McpServer({
|
|
|
32036
32103
|
version: version2
|
|
32037
32104
|
});
|
|
32038
32105
|
for (const tool of allTools) {
|
|
32039
|
-
const
|
|
32106
|
+
const toolAcceptsStoreId = "storeId" in tool.inputSchema.shape;
|
|
32040
32107
|
server.tool(
|
|
32041
32108
|
tool.name,
|
|
32042
32109
|
tool.description,
|
|
32043
32110
|
tool.inputSchema.shape,
|
|
32044
32111
|
tool.annotations,
|
|
32045
32112
|
async (input) => {
|
|
32113
|
+
const isDestructive = isDestructiveCall(tool, input);
|
|
32046
32114
|
const start = Date.now();
|
|
32047
32115
|
try {
|
|
32048
32116
|
if (isDestructive) checkDestructiveRateLimit();
|
|
32049
|
-
|
|
32050
|
-
if (typeof storeId === "string") checkStoreAllowed(storeId);
|
|
32117
|
+
checkStoreScopedToolInput(toolAcceptsStoreId, input);
|
|
32051
32118
|
const result = await tool.handler(input);
|
|
32052
32119
|
const response = result;
|
|
32053
32120
|
const latency_ms = Date.now() - start;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/lemonsqueezy-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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>",
|
|
@@ -31,11 +31,10 @@
|
|
|
31
31
|
"dev": "tsc --watch",
|
|
32
32
|
"start": "node dist/index.js",
|
|
33
33
|
"test": "npm run build && node --test dist/**/*.test.js",
|
|
34
|
-
"test:ci": "npm run test",
|
|
35
34
|
"test:integration": "npm run build && node --test dist/integration/*.test.js",
|
|
36
35
|
"lint": "biome check src/",
|
|
37
36
|
"lint:fix": "biome check --write src/",
|
|
38
|
-
"prepublishOnly": "npm run build
|
|
37
|
+
"prepublishOnly": "npm run build"
|
|
39
38
|
},
|
|
40
39
|
"dependencies": {},
|
|
41
40
|
"overrides": {
|
|
@@ -50,6 +49,6 @@
|
|
|
50
49
|
"zod": "^4.3.6"
|
|
51
50
|
},
|
|
52
51
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
52
|
+
"node": ">=20"
|
|
54
53
|
}
|
|
55
54
|
}
|