@yawlabs/lemonsqueezy-mcp 0.4.1 → 0.6.0
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 +22 -5
- package/dist/index.js +82 -36
- package/package.json +54 -55
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ Add to `claude_desktop_config.json`:
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
## Tools (
|
|
57
|
+
## Tools (61)
|
|
58
58
|
|
|
59
59
|
### Users
|
|
60
60
|
- `ls_get_user` — Get the authenticated user
|
|
@@ -157,7 +157,7 @@ Add to `claude_desktop_config.json`:
|
|
|
157
157
|
|
|
158
158
|
## Features
|
|
159
159
|
|
|
160
|
-
- **Full API coverage** — All 17 LemonSqueezy API resources with
|
|
160
|
+
- **Full API coverage** — All 17 LemonSqueezy API resources with 61 tools
|
|
161
161
|
- **JSON:API support** — Filtering, pagination, and relationship inclusion on all list/get operations
|
|
162
162
|
- **Zero runtime dependencies** — Single bundled file for instant `npx` startup
|
|
163
163
|
- **License API** — Activate, validate, and deactivate license keys without an API key
|
|
@@ -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
|
|
178
|
-
| `LEMONSQUEEZY_MAX_REFUND_AMOUNT_CENTS` | Rejects `ls_refund_order` 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. |
|
|
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
|
+
| `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; 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
|
@@ -21073,6 +21073,24 @@ function checkDestructiveRateLimit(now = Date.now()) {
|
|
|
21073
21073
|
}
|
|
21074
21074
|
destructiveTimestamps.push(now);
|
|
21075
21075
|
}
|
|
21076
|
+
function isStoreAllowlistActive() {
|
|
21077
|
+
return loadOptions().allowedStoreIds !== null;
|
|
21078
|
+
}
|
|
21079
|
+
function checkStoreScopedToolInput(toolAcceptsStoreId, input) {
|
|
21080
|
+
if (!toolAcceptsStoreId) return;
|
|
21081
|
+
const raw = input.storeId;
|
|
21082
|
+
if (raw !== void 0 && raw !== null && raw !== "") {
|
|
21083
|
+
checkStoreAllowed(String(raw));
|
|
21084
|
+
return;
|
|
21085
|
+
}
|
|
21086
|
+
if (isStoreAllowlistActive()) {
|
|
21087
|
+
throw new GuardrailError("storeId is required when LEMONSQUEEZY_ALLOWED_STORE_IDS is set");
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
function isDestructiveCall(tool, input) {
|
|
21091
|
+
if (typeof tool.isDestructive === "function") return tool.isDestructive(input);
|
|
21092
|
+
return tool.annotations?.destructiveHint === true;
|
|
21093
|
+
}
|
|
21076
21094
|
|
|
21077
21095
|
// src/logger.ts
|
|
21078
21096
|
function isEnabled() {
|
|
@@ -21083,6 +21101,25 @@ function logEvent(entry) {
|
|
|
21083
21101
|
try {
|
|
21084
21102
|
const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
|
|
21085
21103
|
process.stderr.write(`${line}
|
|
21104
|
+
`);
|
|
21105
|
+
return;
|
|
21106
|
+
} catch {
|
|
21107
|
+
}
|
|
21108
|
+
try {
|
|
21109
|
+
const fallback = JSON.stringify({
|
|
21110
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21111
|
+
event: entry.event,
|
|
21112
|
+
tool: entry.tool,
|
|
21113
|
+
method: entry.method,
|
|
21114
|
+
path: entry.path,
|
|
21115
|
+
status: entry.status,
|
|
21116
|
+
latency_ms: entry.latency_ms,
|
|
21117
|
+
request_id: entry.request_id,
|
|
21118
|
+
audit: entry.audit,
|
|
21119
|
+
error: entry.error,
|
|
21120
|
+
log_error: "inputs_not_serializable"
|
|
21121
|
+
});
|
|
21122
|
+
process.stderr.write(`${fallback}
|
|
21086
21123
|
`);
|
|
21087
21124
|
} catch {
|
|
21088
21125
|
}
|
|
@@ -21223,6 +21260,9 @@ async function loadApiKey() {
|
|
|
21223
21260
|
|
|
21224
21261
|
// src/api.ts
|
|
21225
21262
|
var BASE_URL = "https://api.lemonsqueezy.com/v1";
|
|
21263
|
+
function encodePath(segment) {
|
|
21264
|
+
return encodeURIComponent(String(segment));
|
|
21265
|
+
}
|
|
21226
21266
|
function buildQuery(params) {
|
|
21227
21267
|
if (!params) return "";
|
|
21228
21268
|
const parts = [];
|
|
@@ -21421,7 +21461,7 @@ async function licenseRequest(path, body) {
|
|
|
21421
21461
|
function getHandler(endpoint, idField) {
|
|
21422
21462
|
return async (input) => {
|
|
21423
21463
|
const query = buildQuery({ include: input.include?.split(",") });
|
|
21424
|
-
return apiGet(`${endpoint}/${input[idField]}${query}`);
|
|
21464
|
+
return apiGet(`${endpoint}/${encodePath(input[idField])}${query}`);
|
|
21425
21465
|
};
|
|
21426
21466
|
}
|
|
21427
21467
|
function listHandler(endpoint, filterMap = {}) {
|
|
@@ -21548,7 +21588,7 @@ var checkoutTools = [
|
|
|
21548
21588
|
billingAddressZip: external_exports.string().max(1e4).optional().describe("Prefill billing ZIP/postal code"),
|
|
21549
21589
|
taxNumber: external_exports.string().max(1e4).optional().describe("Prefill tax/VAT number"),
|
|
21550
21590
|
discountCode: external_exports.string().max(1e4).optional().describe("Pre-apply a discount code"),
|
|
21551
|
-
customData: external_exports.record(external_exports.unknown()).optional().describe("Custom data object to attach to the order"),
|
|
21591
|
+
customData: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("Custom data object to attach to the order"),
|
|
21552
21592
|
expiresAt: external_exports.string().max(1e4).optional().describe("Checkout expiry date (ISO 8601 format)")
|
|
21553
21593
|
}),
|
|
21554
21594
|
handler: async (input) => {
|
|
@@ -21559,13 +21599,11 @@ var checkoutTools = [
|
|
|
21559
21599
|
const checkoutData = {};
|
|
21560
21600
|
if (input.email !== void 0) checkoutData.email = input.email;
|
|
21561
21601
|
if (input.name !== void 0) checkoutData.name = input.name;
|
|
21562
|
-
if (input.billingAddressCountry !== void 0)
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
zip: input.billingAddressZip
|
|
21568
|
-
};
|
|
21602
|
+
if (input.billingAddressCountry !== void 0 || input.billingAddressZip !== void 0) {
|
|
21603
|
+
const billingAddress = {};
|
|
21604
|
+
if (input.billingAddressCountry !== void 0) billingAddress.country = input.billingAddressCountry;
|
|
21605
|
+
if (input.billingAddressZip !== void 0) billingAddress.zip = input.billingAddressZip;
|
|
21606
|
+
checkoutData.billing_address = billingAddress;
|
|
21569
21607
|
}
|
|
21570
21608
|
if (input.taxNumber !== void 0) checkoutData.tax_number = input.taxNumber;
|
|
21571
21609
|
if (input.discountCode !== void 0) checkoutData.discount_code = input.discountCode;
|
|
@@ -21617,7 +21655,7 @@ var customerTools = [
|
|
|
21617
21655
|
},
|
|
21618
21656
|
inputSchema: external_exports.object({
|
|
21619
21657
|
storeId: external_exports.string().max(1e4).optional().describe("Filter by store ID"),
|
|
21620
|
-
email: external_exports.string().max(
|
|
21658
|
+
email: external_exports.string().email().max(320).optional().describe("Filter by customer email"),
|
|
21621
21659
|
include: external_exports.string().max(1e4).optional().describe("Comma-separated related resources to include (e.g. 'store,orders,subscriptions,license-keys')"),
|
|
21622
21660
|
pageNumber: external_exports.number().int().min(1).optional().describe("Page number (1-indexed)"),
|
|
21623
21661
|
pageSize: external_exports.number().int().min(1).max(100).optional().describe("Results per page (1-100)")
|
|
@@ -21637,7 +21675,7 @@ var customerTools = [
|
|
|
21637
21675
|
inputSchema: external_exports.object({
|
|
21638
21676
|
storeId: external_exports.string().max(1e4).describe("The store ID to create the customer in"),
|
|
21639
21677
|
name: external_exports.string().max(1e4).describe("Customer's full name"),
|
|
21640
|
-
email: external_exports.string().max(
|
|
21678
|
+
email: external_exports.string().email().max(320).describe("Customer's email address"),
|
|
21641
21679
|
city: external_exports.string().max(1e4).optional().describe("Customer's city"),
|
|
21642
21680
|
region: external_exports.string().max(1e4).optional().describe("Customer's region/state"),
|
|
21643
21681
|
country: external_exports.string().max(1e4).optional().describe("Customer's country (ISO 3166-1 alpha-2 code, e.g. 'US')")
|
|
@@ -21674,7 +21712,7 @@ var customerTools = [
|
|
|
21674
21712
|
inputSchema: external_exports.object({
|
|
21675
21713
|
customerId: external_exports.string().max(1e4).describe("The customer ID to update"),
|
|
21676
21714
|
name: external_exports.string().max(1e4).optional().describe("New name"),
|
|
21677
|
-
email: external_exports.string().max(
|
|
21715
|
+
email: external_exports.string().email().max(320).optional().describe("New email"),
|
|
21678
21716
|
city: external_exports.string().max(1e4).optional().describe("New city"),
|
|
21679
21717
|
region: external_exports.string().max(1e4).optional().describe("New region/state"),
|
|
21680
21718
|
country: external_exports.string().max(1e4).optional().describe("New country (ISO 3166-1 alpha-2 code)"),
|
|
@@ -21688,7 +21726,7 @@ var customerTools = [
|
|
|
21688
21726
|
if (input.region !== void 0) attributes.region = input.region;
|
|
21689
21727
|
if (input.country !== void 0) attributes.country = input.country;
|
|
21690
21728
|
if (input.status !== void 0) attributes.status = input.status;
|
|
21691
|
-
return apiPatch(`/customers/${input.customerId}`, {
|
|
21729
|
+
return apiPatch(`/customers/${encodePath(input.customerId)}`, {
|
|
21692
21730
|
data: {
|
|
21693
21731
|
type: "customers",
|
|
21694
21732
|
id: input.customerId,
|
|
@@ -21711,7 +21749,7 @@ var customerTools = [
|
|
|
21711
21749
|
customerId: external_exports.string().max(1e4).describe("The customer ID to archive")
|
|
21712
21750
|
}),
|
|
21713
21751
|
handler: async (input) => {
|
|
21714
|
-
return apiPatch(`/customers/${input.customerId}`, {
|
|
21752
|
+
return apiPatch(`/customers/${encodePath(input.customerId)}`, {
|
|
21715
21753
|
data: {
|
|
21716
21754
|
type: "customers",
|
|
21717
21755
|
id: input.customerId,
|
|
@@ -21869,7 +21907,7 @@ var discountTools = [
|
|
|
21869
21907
|
discountId: external_exports.string().max(1e4).describe("The discount ID to delete")
|
|
21870
21908
|
}),
|
|
21871
21909
|
handler: async (input) => {
|
|
21872
|
-
return apiDelete(`/discounts/${input.discountId}`);
|
|
21910
|
+
return apiDelete(`/discounts/${encodePath(input.discountId)}`);
|
|
21873
21911
|
}
|
|
21874
21912
|
}
|
|
21875
21913
|
];
|
|
@@ -22000,7 +22038,7 @@ var licenseKeyTools = [
|
|
|
22000
22038
|
},
|
|
22001
22039
|
{
|
|
22002
22040
|
name: "ls_update_license_key",
|
|
22003
|
-
description: "Update a license key's activation limit, expiry date, or disabled status.",
|
|
22041
|
+
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).",
|
|
22004
22042
|
annotations: {
|
|
22005
22043
|
title: "Update license key",
|
|
22006
22044
|
readOnlyHint: false,
|
|
@@ -22008,6 +22046,11 @@ var licenseKeyTools = [
|
|
|
22008
22046
|
idempotentHint: true,
|
|
22009
22047
|
openWorldHint: true
|
|
22010
22048
|
},
|
|
22049
|
+
// Disabling a license key revokes a customer's access. Tag the call as
|
|
22050
|
+
// destructive only when `disabled: true` so the rate limiter and audit log
|
|
22051
|
+
// engage on revocation, while benign edits (expiry, activation limit) stay
|
|
22052
|
+
// on the regular path.
|
|
22053
|
+
isDestructive: (input) => input.disabled === true,
|
|
22011
22054
|
inputSchema: external_exports.object({
|
|
22012
22055
|
licenseKeyId: external_exports.string().max(1e4).describe("The license key ID to update"),
|
|
22013
22056
|
activationLimit: external_exports.number().int().min(0).optional().describe("Maximum number of activations allowed (0 = unlimited)"),
|
|
@@ -22019,7 +22062,7 @@ var licenseKeyTools = [
|
|
|
22019
22062
|
if (input.activationLimit !== void 0) attributes.activation_limit = input.activationLimit;
|
|
22020
22063
|
if (input.disabled !== void 0) attributes.disabled = input.disabled;
|
|
22021
22064
|
if (input.expiresAt !== void 0) attributes.expires_at = input.expiresAt;
|
|
22022
|
-
return apiPatch(`/license-keys/${input.licenseKeyId}`, {
|
|
22065
|
+
return apiPatch(`/license-keys/${encodePath(input.licenseKeyId)}`, {
|
|
22023
22066
|
data: {
|
|
22024
22067
|
type: "license-keys",
|
|
22025
22068
|
id: input.licenseKeyId,
|
|
@@ -22168,7 +22211,7 @@ var orderTools = [
|
|
|
22168
22211
|
},
|
|
22169
22212
|
inputSchema: external_exports.object({
|
|
22170
22213
|
storeId: external_exports.string().max(1e4).optional().describe("Filter by store ID"),
|
|
22171
|
-
userEmail: external_exports.string().max(
|
|
22214
|
+
userEmail: external_exports.string().email().max(320).optional().describe("Filter by user email"),
|
|
22172
22215
|
include: external_exports.string().max(1e4).optional().describe(
|
|
22173
22216
|
"Comma-separated related resources to include (e.g. 'store,customer,order-items,subscriptions,license-keys,discount-redemptions')"
|
|
22174
22217
|
),
|
|
@@ -22209,7 +22252,7 @@ var orderTools = [
|
|
|
22209
22252
|
if (input.notes !== void 0) params.set("notes", input.notes);
|
|
22210
22253
|
if (input.locale !== void 0) params.set("locale", input.locale);
|
|
22211
22254
|
const qs = params.toString();
|
|
22212
|
-
return apiPost(`/orders/${input.orderId}/generate-invoice${qs ? `?${qs}` : ""}`);
|
|
22255
|
+
return apiPost(`/orders/${encodePath(input.orderId)}/generate-invoice${qs ? `?${qs}` : ""}`);
|
|
22213
22256
|
}
|
|
22214
22257
|
},
|
|
22215
22258
|
{
|
|
@@ -22228,7 +22271,7 @@ var orderTools = [
|
|
|
22228
22271
|
}),
|
|
22229
22272
|
handler: async (input) => {
|
|
22230
22273
|
checkRefundAmount(input.amount);
|
|
22231
|
-
return apiPost(`/orders/${input.orderId}/refund`, {
|
|
22274
|
+
return apiPost(`/orders/${encodePath(input.orderId)}/refund`, {
|
|
22232
22275
|
data: { type: "orders", id: input.orderId, attributes: { amount: input.amount } }
|
|
22233
22276
|
});
|
|
22234
22277
|
}
|
|
@@ -22428,7 +22471,9 @@ var subscriptionInvoiceTools = [
|
|
|
22428
22471
|
if (input.notes !== void 0) params.set("notes", input.notes);
|
|
22429
22472
|
if (input.locale !== void 0) params.set("locale", input.locale);
|
|
22430
22473
|
const qs = params.toString();
|
|
22431
|
-
return apiPost(
|
|
22474
|
+
return apiPost(
|
|
22475
|
+
`/subscription-invoices/${encodePath(input.subscriptionInvoiceId)}/generate-invoice${qs ? `?${qs}` : ""}`
|
|
22476
|
+
);
|
|
22432
22477
|
}
|
|
22433
22478
|
},
|
|
22434
22479
|
{
|
|
@@ -22446,7 +22491,8 @@ var subscriptionInvoiceTools = [
|
|
|
22446
22491
|
amount: external_exports.number().int().min(1).describe("Refund amount in cents (e.g. 1000 = $10.00)")
|
|
22447
22492
|
}),
|
|
22448
22493
|
handler: async (input) => {
|
|
22449
|
-
|
|
22494
|
+
checkRefundAmount(input.amount);
|
|
22495
|
+
return apiPost(`/subscription-invoices/${encodePath(input.subscriptionInvoiceId)}/refund`, {
|
|
22450
22496
|
data: {
|
|
22451
22497
|
type: "subscription-invoices",
|
|
22452
22498
|
id: input.subscriptionInvoiceId,
|
|
@@ -22509,7 +22555,7 @@ var subscriptionItemTools = [
|
|
|
22509
22555
|
quantity: external_exports.number().int().min(1).describe("New quantity for the subscription item")
|
|
22510
22556
|
}),
|
|
22511
22557
|
handler: async (input) => {
|
|
22512
|
-
return apiPatch(`/subscription-items/${input.subscriptionItemId}`, {
|
|
22558
|
+
return apiPatch(`/subscription-items/${encodePath(input.subscriptionItemId)}`, {
|
|
22513
22559
|
data: {
|
|
22514
22560
|
type: "subscription-items",
|
|
22515
22561
|
id: input.subscriptionItemId,
|
|
@@ -22532,7 +22578,7 @@ var subscriptionItemTools = [
|
|
|
22532
22578
|
subscriptionItemId: external_exports.string().max(1e4).describe("The subscription item ID")
|
|
22533
22579
|
}),
|
|
22534
22580
|
handler: async (input) => {
|
|
22535
|
-
return apiGet(`/subscription-items/${input.subscriptionItemId}/current-usage`);
|
|
22581
|
+
return apiGet(`/subscription-items/${encodePath(input.subscriptionItemId)}/current-usage`);
|
|
22536
22582
|
}
|
|
22537
22583
|
}
|
|
22538
22584
|
];
|
|
@@ -22573,7 +22619,7 @@ var subscriptionTools = [
|
|
|
22573
22619
|
orderItemId: external_exports.string().max(1e4).optional().describe("Filter by order item ID"),
|
|
22574
22620
|
productId: external_exports.string().max(1e4).optional().describe("Filter by product ID"),
|
|
22575
22621
|
variantId: external_exports.string().max(1e4).optional().describe("Filter by variant ID"),
|
|
22576
|
-
userEmail: external_exports.string().max(
|
|
22622
|
+
userEmail: external_exports.string().email().max(320).optional().describe("Filter by user email"),
|
|
22577
22623
|
status: external_exports.enum(["on_trial", "active", "paused", "past_due", "unpaid", "cancelled", "expired"]).optional().describe("Filter by subscription status"),
|
|
22578
22624
|
include: external_exports.string().max(1e4).optional().describe(
|
|
22579
22625
|
"Comma-separated related resources to include (e.g. 'store,customer,order,order-item,product,variant')"
|
|
@@ -22603,7 +22649,7 @@ var subscriptionTools = [
|
|
|
22603
22649
|
},
|
|
22604
22650
|
inputSchema: external_exports.object({
|
|
22605
22651
|
subscriptionId: external_exports.string().max(1e4).describe("The subscription ID to update"),
|
|
22606
|
-
variantId: external_exports.string().max(1e4).optional().describe("New variant ID for plan switching"),
|
|
22652
|
+
variantId: external_exports.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"),
|
|
22607
22653
|
pause: external_exports.enum(["void", "free", "resume"]).optional().describe("Pause mode: 'void' (pause, skip billing), 'free' (pause, keep access free), or 'resume' to unpause"),
|
|
22608
22654
|
cancelled: external_exports.literal(false).optional().describe(
|
|
22609
22655
|
"Set to false to un-cancel a subscription before it expires. To cancel, use ls_cancel_subscription instead."
|
|
@@ -22611,7 +22657,7 @@ var subscriptionTools = [
|
|
|
22611
22657
|
billingAnchor: external_exports.number().int().min(1).max(28).optional().describe("Day of month (1-28) to anchor billing to"),
|
|
22612
22658
|
invoiceImmediately: external_exports.boolean().optional().describe("If true, invoice immediately when updating (default false for prorated changes)"),
|
|
22613
22659
|
disableProrations: external_exports.boolean().optional().describe("If true, disable prorations when changing plans"),
|
|
22614
|
-
trialEndsAt: external_exports.string().max(1e4).optional().describe("Set trial end date (ISO 8601 format). Set to null to end trial immediately.")
|
|
22660
|
+
trialEndsAt: external_exports.string().max(1e4).nullable().optional().describe("Set trial end date (ISO 8601 format). Set to null to end trial immediately.")
|
|
22615
22661
|
}),
|
|
22616
22662
|
handler: async (input) => {
|
|
22617
22663
|
const attributes = {};
|
|
@@ -22622,7 +22668,7 @@ var subscriptionTools = [
|
|
|
22622
22668
|
if (input.invoiceImmediately !== void 0) attributes.invoice_immediately = input.invoiceImmediately;
|
|
22623
22669
|
if (input.disableProrations !== void 0) attributes.disable_prorations = input.disableProrations;
|
|
22624
22670
|
if (input.trialEndsAt !== void 0) attributes.trial_ends_at = input.trialEndsAt;
|
|
22625
|
-
return apiPatch(`/subscriptions/${input.subscriptionId}`, {
|
|
22671
|
+
return apiPatch(`/subscriptions/${encodePath(input.subscriptionId)}`, {
|
|
22626
22672
|
data: {
|
|
22627
22673
|
type: "subscriptions",
|
|
22628
22674
|
id: input.subscriptionId,
|
|
@@ -22645,7 +22691,7 @@ var subscriptionTools = [
|
|
|
22645
22691
|
subscriptionId: external_exports.string().max(1e4).describe("The subscription ID to cancel")
|
|
22646
22692
|
}),
|
|
22647
22693
|
handler: async (input) => {
|
|
22648
|
-
return apiDelete(`/subscriptions/${input.subscriptionId}`);
|
|
22694
|
+
return apiDelete(`/subscriptions/${encodePath(input.subscriptionId)}`);
|
|
22649
22695
|
}
|
|
22650
22696
|
}
|
|
22651
22697
|
];
|
|
@@ -22862,14 +22908,14 @@ var webhookTools = [
|
|
|
22862
22908
|
webhookId: external_exports.string().max(1e4).describe("The webhook ID to update"),
|
|
22863
22909
|
url: external_exports.string().max(1e4).optional().describe("New URL to send webhook events to"),
|
|
22864
22910
|
events: external_exports.array(external_exports.string()).optional().describe("Updated list of event types to subscribe to"),
|
|
22865
|
-
secret: external_exports.string().max(
|
|
22911
|
+
secret: external_exports.string().min(6).max(40).optional().describe("New signing secret")
|
|
22866
22912
|
}),
|
|
22867
22913
|
handler: async (input) => {
|
|
22868
22914
|
const attributes = {};
|
|
22869
22915
|
if (input.url !== void 0) attributes.url = input.url;
|
|
22870
22916
|
if (input.events !== void 0) attributes.events = input.events;
|
|
22871
22917
|
if (input.secret !== void 0) attributes.secret = input.secret;
|
|
22872
|
-
return apiPatch(`/webhooks/${input.webhookId}`, {
|
|
22918
|
+
return apiPatch(`/webhooks/${encodePath(input.webhookId)}`, {
|
|
22873
22919
|
data: {
|
|
22874
22920
|
type: "webhooks",
|
|
22875
22921
|
id: input.webhookId,
|
|
@@ -22892,13 +22938,13 @@ var webhookTools = [
|
|
|
22892
22938
|
webhookId: external_exports.string().max(1e4).describe("The webhook ID to delete")
|
|
22893
22939
|
}),
|
|
22894
22940
|
handler: async (input) => {
|
|
22895
|
-
return apiDelete(`/webhooks/${input.webhookId}`);
|
|
22941
|
+
return apiDelete(`/webhooks/${encodePath(input.webhookId)}`);
|
|
22896
22942
|
}
|
|
22897
22943
|
}
|
|
22898
22944
|
];
|
|
22899
22945
|
|
|
22900
22946
|
// src/index.ts
|
|
22901
|
-
var version2 = true ? "0.
|
|
22947
|
+
var version2 = true ? "0.6.0" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
22902
22948
|
var subcommand = process.argv[2];
|
|
22903
22949
|
if (subcommand === "version" || subcommand === "--version") {
|
|
22904
22950
|
console.log(version2);
|
|
@@ -22932,18 +22978,18 @@ var server = new McpServer({
|
|
|
22932
22978
|
version: version2
|
|
22933
22979
|
});
|
|
22934
22980
|
for (const tool of allTools) {
|
|
22935
|
-
const
|
|
22981
|
+
const toolAcceptsStoreId = "storeId" in tool.inputSchema.shape;
|
|
22936
22982
|
server.tool(
|
|
22937
22983
|
tool.name,
|
|
22938
22984
|
tool.description,
|
|
22939
22985
|
tool.inputSchema.shape,
|
|
22940
22986
|
tool.annotations,
|
|
22941
22987
|
async (input) => {
|
|
22988
|
+
const isDestructive = isDestructiveCall(tool, input);
|
|
22942
22989
|
const start = Date.now();
|
|
22943
22990
|
try {
|
|
22944
22991
|
if (isDestructive) checkDestructiveRateLimit();
|
|
22945
|
-
|
|
22946
|
-
if (typeof storeId === "string") checkStoreAllowed(storeId);
|
|
22992
|
+
checkStoreScopedToolInput(toolAcceptsStoreId, input);
|
|
22947
22993
|
const result = await tool.handler(input);
|
|
22948
22994
|
const response = result;
|
|
22949
22995
|
const latency_ms = Date.now() - start;
|
package/package.json
CHANGED
|
@@ -1,55 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@yawlabs/lemonsqueezy-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "LemonSqueezy MCP server for managing your store from AI assistants",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "YawLabs <contact@yaw.sh>",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/YawLabs/lemonsqueezy-mcp.git"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"lemonsqueezy",
|
|
13
|
-
"lemon-squeezy",
|
|
14
|
-
"mcp",
|
|
15
|
-
"model-context-protocol",
|
|
16
|
-
"ai",
|
|
17
|
-
"payments",
|
|
18
|
-
"subscriptions",
|
|
19
|
-
"licensing"
|
|
20
|
-
],
|
|
21
|
-
"type": "module",
|
|
22
|
-
"main": "dist/index.js",
|
|
23
|
-
"bin": {
|
|
24
|
-
"lemonsqueezy-mcp": "dist/index.js"
|
|
25
|
-
},
|
|
26
|
-
"files": [
|
|
27
|
-
"dist/index.js"
|
|
28
|
-
],
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "tsc && node build.mjs",
|
|
31
|
-
"dev": "tsc --watch",
|
|
32
|
-
"start": "node dist/index.js",
|
|
33
|
-
"test": "npm run build && node --test dist/**/*.test.js",
|
|
34
|
-
"test:
|
|
35
|
-
"
|
|
36
|
-
"lint": "biome check src/",
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@yawlabs/lemonsqueezy-mcp",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "LemonSqueezy MCP server for managing your store from AI assistants",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "YawLabs <contact@yaw.sh>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/YawLabs/lemonsqueezy-mcp.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"lemonsqueezy",
|
|
13
|
+
"lemon-squeezy",
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"ai",
|
|
17
|
+
"payments",
|
|
18
|
+
"subscriptions",
|
|
19
|
+
"licensing"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"bin": {
|
|
24
|
+
"lemonsqueezy-mcp": "dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/index.js"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc && node build.mjs",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"start": "node dist/index.js",
|
|
33
|
+
"test": "npm run build && node --test dist/**/*.test.js",
|
|
34
|
+
"test:integration": "npm run build && node --test dist/integration/*.test.js",
|
|
35
|
+
"lint": "biome check src/",
|
|
36
|
+
"lint:fix": "biome check --write src/",
|
|
37
|
+
"prepublishOnly": "npm run build && node --test dist/**/*.test.js"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {},
|
|
40
|
+
"overrides": {
|
|
41
|
+
"hono": "^4.12.14"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@biomejs/biome": "^2.4.12",
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
46
|
+
"@types/node": "^25.6.0",
|
|
47
|
+
"esbuild": "^0.28.0",
|
|
48
|
+
"typescript": "^6.0.3",
|
|
49
|
+
"zod": "^4.3.6"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
}
|
|
54
|
+
}
|