@yawlabs/lemonsqueezy-mcp 0.14.2 → 1.0.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/dist/index.js CHANGED
@@ -31561,6 +31561,56 @@ function logEvent(entry) {
31561
31561
  }
31562
31562
  }
31563
31563
 
31564
+ // src/redact.ts
31565
+ var SECRET_KEY_RE = /^(secret|password|token|api[_-]?key|bearer|authorization|signing[_-]?secret|private[_-]?key|pin|ssn|social[_-]?security[_-]?number|credit[_-]?card|card[_-]?number|cvv|cvc)$/i;
31566
+ var JWT_VALUE_RE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}$/;
31567
+ var LICENSE_KEY_RE = /^license[_-]?key$/i;
31568
+ var MIN_LICENSE_KEY_MASK_LENGTH = 16;
31569
+ var REDACTED = "[REDACTED]";
31570
+ var CIRCULAR = "[CIRCULAR]";
31571
+ function maskLicenseKey(value) {
31572
+ if (typeof value !== "string" || value.length < MIN_LICENSE_KEY_MASK_LENGTH) return REDACTED;
31573
+ return `[REDACTED:last4=${value.slice(-4)}]`;
31574
+ }
31575
+ var MAX_DEPTH = 32;
31576
+ function isPlainObject3(value) {
31577
+ if (value === null || typeof value !== "object") return false;
31578
+ const proto = Object.getPrototypeOf(value);
31579
+ return proto === Object.prototype || proto === null;
31580
+ }
31581
+ function looksLikeBearerToken(value) {
31582
+ if (typeof value !== "string") return false;
31583
+ if (value.length < 20) return false;
31584
+ return JWT_VALUE_RE.test(value);
31585
+ }
31586
+ function redactInner(value, ancestors, memo, depth) {
31587
+ if (depth > MAX_DEPTH) return CIRCULAR;
31588
+ if (typeof value === "string" && looksLikeBearerToken(value)) return REDACTED;
31589
+ if (value === null || typeof value !== "object") return value;
31590
+ const isArray = Array.isArray(value);
31591
+ if (!isArray && !isPlainObject3(value)) return value;
31592
+ if (ancestors.has(value)) return CIRCULAR;
31593
+ const cached3 = memo.get(value);
31594
+ if (cached3 !== void 0 && cached3.depth <= depth) return cached3.result;
31595
+ ancestors.add(value);
31596
+ let result;
31597
+ if (isArray) {
31598
+ result = value.map((item) => redactInner(item, ancestors, memo, depth + 1));
31599
+ } else {
31600
+ const out = {};
31601
+ for (const [key, val] of Object.entries(value)) {
31602
+ out[key] = SECRET_KEY_RE.test(key) ? REDACTED : LICENSE_KEY_RE.test(key) ? maskLicenseKey(val) : redactInner(val, ancestors, memo, depth + 1);
31603
+ }
31604
+ result = out;
31605
+ }
31606
+ ancestors.delete(value);
31607
+ memo.set(value, { depth, result });
31608
+ return result;
31609
+ }
31610
+ function redactSecrets(input) {
31611
+ return redactInner(input, /* @__PURE__ */ new WeakSet(), /* @__PURE__ */ new WeakMap(), 0);
31612
+ }
31613
+
31564
31614
  // src/retry.ts
31565
31615
  var REQUEST_TIMEOUT_MS = 3e4;
31566
31616
  var DEFAULT_RETRY_WAIT_MS = 1e3;
@@ -31778,6 +31828,10 @@ function invalidateApiKeyCache() {
31778
31828
  // src/api.ts
31779
31829
  var BASE_URL = "https://api.lemonsqueezy.com/v1";
31780
31830
  var lsIdSchema = external_exports.string().max(1e4).regex(/^[1-9]\d*$/, "ID must be a positive integer string (e.g. '12345')");
31831
+ var lsUuidSchema = external_exports.string().max(1e4).regex(
31832
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
31833
+ "ID must be a UUID (e.g. '5e8b546c-c561-4a2c-a586-40c18bb2a195')"
31834
+ );
31781
31835
  function encodePath(segment) {
31782
31836
  return encodeURIComponent(String(segment));
31783
31837
  }
@@ -31809,30 +31863,17 @@ function decorateError(error51, requestId) {
31809
31863
  return requestId ? `${error51} (request_id: ${requestId})` : error51;
31810
31864
  }
31811
31865
  function extractErrorMessage(parsed) {
31812
- const first = parsed.errors?.[0];
31813
- return first?.detail ?? first?.title ?? parsed.error ?? null;
31866
+ if (typeof parsed !== "object" || parsed === null) return null;
31867
+ const envelope = parsed;
31868
+ const first = Array.isArray(envelope.errors) ? envelope.errors[0] : void 0;
31869
+ const str = (v) => typeof v === "string" ? v : void 0;
31870
+ return str(first?.detail) ?? str(first?.title) ?? str(envelope.error) ?? null;
31814
31871
  }
31815
31872
  async function handleErrorResponse(res, route, latency_ms, requestId) {
31816
31873
  const errorBody = await res.text();
31874
+ let parsed;
31817
31875
  try {
31818
- const parsed = JSON.parse(errorBody);
31819
- const detail = extractErrorMessage(parsed) ?? errorBody;
31820
- logEvent({
31821
- event: "http_call",
31822
- method: route.method,
31823
- path: route.path,
31824
- status: res.status,
31825
- latency_ms,
31826
- request_id: requestId,
31827
- error: detail
31828
- });
31829
- return {
31830
- ok: false,
31831
- status: res.status,
31832
- data: parsed,
31833
- error: decorateError(detail, requestId),
31834
- requestId
31835
- };
31876
+ parsed = JSON.parse(errorBody);
31836
31877
  } catch {
31837
31878
  logEvent({
31838
31879
  event: "http_call",
@@ -31850,6 +31891,23 @@ async function handleErrorResponse(res, route, latency_ms, requestId) {
31850
31891
  requestId
31851
31892
  };
31852
31893
  }
31894
+ const detail = extractErrorMessage(parsed) ?? JSON.stringify(redactSecrets(parsed));
31895
+ logEvent({
31896
+ event: "http_call",
31897
+ method: route.method,
31898
+ path: route.path,
31899
+ status: res.status,
31900
+ latency_ms,
31901
+ request_id: requestId,
31902
+ error: detail
31903
+ });
31904
+ return {
31905
+ ok: false,
31906
+ status: res.status,
31907
+ data: parsed,
31908
+ error: decorateError(detail, requestId),
31909
+ requestId
31910
+ };
31853
31911
  }
31854
31912
  async function readJsonBody(res) {
31855
31913
  const bodyText = await res.text();
@@ -32080,7 +32138,9 @@ var checkoutTools = [
32080
32138
  openWorldHint: true
32081
32139
  },
32082
32140
  inputSchema: external_exports.object({
32083
- checkoutId: lsIdSchema.describe("The checkout ID"),
32141
+ checkoutId: lsUuidSchema.describe(
32142
+ "The checkout ID (a UUID, as returned by ls_list_checkouts / ls_create_checkout)"
32143
+ ),
32084
32144
  include: external_exports.string().max(1e4).optional().describe("Comma-separated related resources to include (e.g. 'store,variant')")
32085
32145
  }),
32086
32146
  handler: getHandler("/checkouts", "checkoutId")
@@ -32248,7 +32308,12 @@ var customerTools = [
32248
32308
  annotations: {
32249
32309
  title: "Update customer",
32250
32310
  readOnlyHint: false,
32251
- destructiveHint: false,
32311
+ // Static true: MCP defines false as "only additive updates", and every
32312
+ // field here overwrites the existing value (status can archive). The
32313
+ // predicate below still decides per call whether the server-side
32314
+ // destructive limiter and audit log engage (isDestructiveCall in
32315
+ // guardrails.ts never reads this hint when a predicate exists).
32316
+ destructiveHint: true,
32252
32317
  idempotentHint: true,
32253
32318
  openWorldHint: true
32254
32319
  },
@@ -32615,25 +32680,31 @@ var licenseKeyTools = [
32615
32680
  {
32616
32681
  name: "ls_update_license_key",
32617
32682
  authorityClass: "key",
32618
- 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).",
32683
+ description: "Update a license key's activation limit, expiry date, or disabled status. Setting `disabled: true`, changing `activationLimit`, or changing `expiresAt` can revoke customer access and is treated as destructive (rate-limited and audited); `disabled: false` alone is not.",
32619
32684
  annotations: {
32620
32685
  title: "Update license key",
32621
32686
  readOnlyHint: false,
32622
- destructiveHint: false,
32687
+ // Static true: MCP defines false as "only additive updates", and
32688
+ // several inputs here can revoke access. The predicate below still
32689
+ // decides per call whether the server-side destructive limiter and
32690
+ // audit log engage (isDestructiveCall in guardrails.ts never reads this
32691
+ // hint when a predicate exists).
32692
+ destructiveHint: true,
32623
32693
  idempotentHint: true,
32624
32694
  openWorldHint: true
32625
32695
  },
32626
32696
  // Disabling a license key revokes a customer's access outright. Changing
32627
- // the activation limit can also revoke access -- setting it to 0, or to
32628
- // any value below the customer's current activation count, kicks
32629
- // already-activated instances offline. We can't tell from the input alone
32630
- // whether a given limit change shrinks or grows, so treat ANY
32631
- // `activationLimit` change as destructive alongside `disabled: true`.
32632
- // Benign edits (expiry) stay on the regular path.
32633
- isDestructive: (input) => input.disabled === true || input.activationLimit !== void 0,
32697
+ // the activation limit or the expiry can also narrow it -- a lower cap on
32698
+ // activations, an earlier end date. We can't tell from the input alone
32699
+ // whether a given change narrows or widens access (a number or a date can
32700
+ // go either way against the current value, which we don't fetch), so
32701
+ // treat ANY `activationLimit` or `expiresAt` change as destructive
32702
+ // alongside `disabled: true`, including null. Only `disabled: false`
32703
+ // (re-enabling) stays on the regular path.
32704
+ isDestructive: (input) => input.disabled === true || input.activationLimit !== void 0 || input.expiresAt !== void 0,
32634
32705
  inputSchema: external_exports.object({
32635
32706
  licenseKeyId: lsIdSchema.describe("The license key ID to update"),
32636
- activationLimit: external_exports.number().int().min(0).optional().describe("Maximum number of activations allowed (0 = unlimited)"),
32707
+ activationLimit: external_exports.number().int().min(0).nullable().optional().describe("Maximum number of activations allowed. Pass null for unlimited."),
32637
32708
  disabled: external_exports.boolean().optional().describe("Set to true to disable this license key"),
32638
32709
  expiresAt: external_exports.string().max(1e4).nullable().optional().describe("Expiry date (ISO 8601 format). Set to null to remove expiry.")
32639
32710
  }),
@@ -32706,11 +32777,11 @@ var licenseTools = [
32706
32777
  {
32707
32778
  name: "ls_deactivate_license",
32708
32779
  authorityClass: "key",
32709
- description: "Deactivate a license key instance. Does not require an API key \u2014 uses the license key itself for auth.",
32780
+ description: "Deactivate a license key instance, revoking that instance's access. Destructive: rate-limited and audited, with the license key masked in the audit entry. Does not require an API key \u2014 uses the license key itself for auth.",
32710
32781
  annotations: {
32711
32782
  title: "Deactivate license",
32712
32783
  readOnlyHint: false,
32713
- destructiveHint: false,
32784
+ destructiveHint: true,
32714
32785
  idempotentHint: true,
32715
32786
  openWorldHint: true
32716
32787
  },
@@ -33569,25 +33640,43 @@ var subscriptionTools = [
33569
33640
  {
33570
33641
  name: "ls_update_subscription",
33571
33642
  authorityClass: "recurring",
33572
- description: "Update a subscription. Can change the variant (plan switch), pause/unpause, set billing anchor, or update invoice details. Pausing (`pause: 'void'` or `'free'`) or switching plan (`variantId`) is customer-impacting and is treated as destructive (rate-limited and audited); resuming and the billing-neutral edits are not. Use ls_cancel_subscription for cancellation.",
33643
+ description: "Update a subscription. Can change the variant (plan switch), pause/unpause, set billing anchor, or update invoice details. Pausing (`pause: 'void'` or `'free'`), switching plan (`variantId`), changing the billing anchor (`billingAnchor`), invoicing immediately (`invoiceImmediately: true`), or changing the trial end (`trialEndsAt`) changes what the customer pays or when, and is treated as destructive (rate-limited and audited); resuming (`pause: 'resume'`), un-cancelling (`cancelled: false`), `invoiceImmediately: false` and `disableProrations` are not. Use ls_cancel_subscription for cancellation.",
33573
33644
  annotations: {
33574
33645
  title: "Update subscription",
33575
33646
  readOnlyHint: false,
33576
- destructiveHint: false,
33647
+ // Static true: MCP defines false as "only additive updates", and
33648
+ // several inputs here pause, re-plan or re-bill the customer. The
33649
+ // predicate below still decides per call whether the server-side
33650
+ // destructive limiter and audit log engage (isDestructiveCall in
33651
+ // guardrails.ts never reads this hint when a predicate exists).
33652
+ destructiveHint: true,
33577
33653
  idempotentHint: true,
33578
33654
  openWorldHint: true
33579
33655
  },
33580
- // A pause (void/free) or plan switch is the customer-impacting subset of
33581
- // this tool's surface; treat those calls as destructive so they engage
33582
- // the rate limiter and audit log. Intentional exclusions, all of which
33583
- // either reverse a destructive action or are billing-neutral:
33584
- // - pause === "resume" un-pause, restoring access
33585
- // - cancelled === false un-cancel before expiry, restoring access
33586
- // - billingAnchor changes anchor day, no immediate charge
33587
- // - trialEndsAt extending or ending a trial
33588
- // - invoiceImmediately toggle on the next prorated edit
33589
- // - disableProrations toggle on plan-change behavior
33590
- isDestructive: (input) => typeof input.pause === "string" && input.pause !== "resume" || typeof input.variantId === "string",
33656
+ // The calls that change what the customer pays, or when, are destructive
33657
+ // so they engage the rate limiter and audit log:
33658
+ // - pause "void" / "free" pauses the subscription
33659
+ // - variantId plan switch
33660
+ // - billingAnchor per LemonSqueezy's update-subscription docs,
33661
+ // a new anchor issues a paid, prorated trial
33662
+ // that is charged when it ends
33663
+ // - invoiceImmediately: true per the same docs, the update is charged
33664
+ // now: a prorated invoice is generated and
33665
+ // payment attempted
33666
+ // - trialEndsAt (any value) ending or shortening a trial brings the
33667
+ // first charge forward; extending it does
33668
+ // not, but telling which needs a fetch, so
33669
+ // any change (null included) counts
33670
+ // Intentional exclusions:
33671
+ // - pause === "resume" un-pause
33672
+ // - cancelled === false un-cancel before expiry
33673
+ // These two are a consistent pair: each reverses a destructive action
33674
+ // and returns the customer to billing they had already agreed to, so
33675
+ // they are treated alike.
33676
+ // - invoiceImmediately: false the default proration behaviour
33677
+ // - disableProrations toggle on plan-change behavior; the plan
33678
+ // change itself (variantId) already counts
33679
+ isDestructive: (input) => typeof input.pause === "string" && input.pause !== "resume" || typeof input.variantId === "string" || input.billingAnchor !== void 0 || input.invoiceImmediately === true || input.trialEndsAt !== void 0,
33591
33680
  inputSchema: external_exports.object({
33592
33681
  subscriptionId: lsIdSchema.describe("The subscription ID to update"),
33593
33682
  variantId: lsIdSchema.optional().describe("New variant ID for plan switching"),
@@ -33685,7 +33774,12 @@ var usageRecordTools = [
33685
33774
  },
33686
33775
  {
33687
33776
  name: "ls_create_usage_record",
33688
- authorityClass: "mutate",
33777
+ // `recurring`, not `mutate`: a usage record changes the metered bill on a
33778
+ // subscription item, the same authority as ls_update_subscription_item's
33779
+ // quantity change. So LEMONSQUEEZY_DISABLE_CLASSES=recurring blocks it,
33780
+ // and a `recurring:N` budget in LEMONSQUEEZY_RATE_LIMIT_PER_CLASS counts
33781
+ // usage reports alongside subscription changes.
33782
+ authorityClass: "recurring",
33689
33783
  description: "Report usage for a metered subscription item. Use 'increment' action to add to the current usage, or 'set' to replace it.",
33690
33784
  annotations: {
33691
33785
  title: "Create usage record",
@@ -33865,14 +33959,22 @@ var webhookTools = [
33865
33959
  annotations: {
33866
33960
  title: "Update webhook",
33867
33961
  readOnlyHint: false,
33868
- destructiveHint: false,
33962
+ // Static true: MCP defines false as "only additive updates", and every
33963
+ // field here overwrites the existing value. The predicate below still
33964
+ // decides per call whether the server-side destructive limiter and
33965
+ // audit log engage (isDestructiveCall in guardrails.ts never reads this
33966
+ // hint when a predicate exists).
33967
+ destructiveHint: true,
33869
33968
  idempotentHint: true,
33870
33969
  openWorldHint: true
33871
33970
  },
33872
- // Rotating the signing secret silently breaks signature verification on
33873
- // every receiver until they redeploy with the new value. URL and events
33874
- // changes are observable on the next event but don't break anything in
33875
- // flight, so they stay on the regular path.
33971
+ // Rotating the signing secret breaks signature verification on every
33972
+ // receiver until they redeploy with the new value, and the agent cannot
33973
+ // undo it: the secret is returned only once (see ls_create_webhook), so
33974
+ // the old value cannot be read back and restored. URL and events
33975
+ // changes can be undone -- ls_get_webhook returns the current URL and
33976
+ // events, and a PATCH puts them back -- so they stay on the regular path.
33977
+ // The `secret` input reaches the audit log redacted by key name.
33876
33978
  isDestructive: (input) => input.secret !== void 0,
33877
33979
  inputSchema: external_exports.object({
33878
33980
  webhookId: lsIdSchema.describe("The webhook ID to update"),
@@ -33941,50 +34043,6 @@ function readAuditEntries(limit) {
33941
34043
  return out;
33942
34044
  }
33943
34045
 
33944
- // src/redact.ts
33945
- var SECRET_KEY_RE = /^(secret|password|token|api[_-]?key|bearer|authorization|signing[_-]?secret|private[_-]?key|pin|ssn|social[_-]?security[_-]?number|credit[_-]?card|card[_-]?number|cvv|cvc)$/i;
33946
- var JWT_VALUE_RE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}$/;
33947
- var REDACTED = "[REDACTED]";
33948
- var CIRCULAR = "[CIRCULAR]";
33949
- var MAX_DEPTH = 32;
33950
- function isPlainObject3(value) {
33951
- if (value === null || typeof value !== "object") return false;
33952
- const proto = Object.getPrototypeOf(value);
33953
- return proto === Object.prototype || proto === null;
33954
- }
33955
- function looksLikeBearerToken(value) {
33956
- if (typeof value !== "string") return false;
33957
- if (value.length < 20) return false;
33958
- return JWT_VALUE_RE.test(value);
33959
- }
33960
- function redactInner(value, ancestors, memo, depth) {
33961
- if (depth > MAX_DEPTH) return CIRCULAR;
33962
- if (typeof value === "string" && looksLikeBearerToken(value)) return REDACTED;
33963
- if (value === null || typeof value !== "object") return value;
33964
- const isArray = Array.isArray(value);
33965
- if (!isArray && !isPlainObject3(value)) return value;
33966
- if (ancestors.has(value)) return CIRCULAR;
33967
- const cached3 = memo.get(value);
33968
- if (cached3 !== void 0 && cached3.depth <= depth) return cached3.result;
33969
- ancestors.add(value);
33970
- let result;
33971
- if (isArray) {
33972
- result = value.map((item) => redactInner(item, ancestors, memo, depth + 1));
33973
- } else {
33974
- const out = {};
33975
- for (const [key, val] of Object.entries(value)) {
33976
- out[key] = SECRET_KEY_RE.test(key) ? REDACTED : redactInner(val, ancestors, memo, depth + 1);
33977
- }
33978
- result = out;
33979
- }
33980
- ancestors.delete(value);
33981
- memo.set(value, { depth, result });
33982
- return result;
33983
- }
33984
- function redactSecrets(input) {
33985
- return redactInner(input, /* @__PURE__ */ new WeakSet(), /* @__PURE__ */ new WeakMap(), 0);
33986
- }
33987
-
33988
34046
  // src/wrapper.ts
33989
34047
  function createToolHandler(tool) {
33990
34048
  return async (input) => {
@@ -34102,7 +34160,7 @@ function readAuditLogResource(uri) {
34102
34160
  }
34103
34161
 
34104
34162
  // src/index.ts
34105
- var version2 = true ? "0.14.2" : createRequire(import.meta.url)("../package.json").version;
34163
+ var version2 = true ? "1.0.0" : createRequire(import.meta.url)("../package.json").version;
34106
34164
  var subcommand = process.argv[2];
34107
34165
  if (subcommand === "version" || subcommand === "--version") {
34108
34166
  console.log(version2);
package/package.json CHANGED
@@ -1,77 +1,77 @@
1
- {
2
- "name": "@yawlabs/lemonsqueezy-mcp",
3
- "version": "0.14.2",
4
- "mcpName": "io.github.YawLabs/lemonsqueezy-mcp",
5
- "description": "Lemon Squeezy MCP server: stores, orders, subscriptions, license keys, refunds, and webhooks.",
6
- "license": "MIT",
7
- "author": "YawLabs <contact@yaw.sh>",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/YawLabs/lemonsqueezy-mcp.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/YawLabs/lemonsqueezy-mcp/issues"
14
- },
15
- "keywords": [
16
- "lemonsqueezy",
17
- "lemon-squeezy",
18
- "mcp",
19
- "mcp-server",
20
- "model-context-protocol",
21
- "ai",
22
- "agent",
23
- "claude",
24
- "claude-code",
25
- "claude-desktop",
26
- "payments",
27
- "subscriptions",
28
- "refunds",
29
- "license-keys",
30
- "licensing",
31
- "checkout",
32
- "webhooks",
33
- "billing",
34
- "cursor",
35
- "ai-agents"
36
- ],
37
- "type": "module",
38
- "main": "dist/index.js",
39
- "bin": {
40
- "lemonsqueezy-mcp": "bin/lemonsqueezy-mcp.mjs"
41
- },
42
- "files": [
43
- "bin/lemonsqueezy-mcp.mjs",
44
- "dist/index.js",
45
- "CHANGELOG.md"
46
- ],
47
- "scripts": {
48
- "build": "tsc && node build.mjs",
49
- "dev": "tsc --watch",
50
- "start": "node dist/index.js",
51
- "test": "npm run build && node --test-timeout=300000 --test \"dist/**/*.test.js\"",
52
- "test:integration": "npm run build && node --test-timeout=300000 --test dist/integration/*.test.js",
53
- "lint": "node scripts/lint.mjs check src/",
54
- "lint:fix": "node scripts/lint.mjs check --write src/",
55
- "check:oam": "oam check",
56
- "build:binary:oam": "node scripts/build-binary.mjs --oam",
57
- "gen:containerfile": "node scripts/sync-containerfile.mjs",
58
- "check:containerfile": "node scripts/sync-containerfile.mjs --check",
59
- "prepublishOnly": "npm run build"
60
- },
61
- "overrides": {
62
- "hono": "^4.13.5"
63
- },
64
- "devDependencies": {
65
- "@biomejs/biome": "^2.4.12",
66
- "@modelcontextprotocol/sdk": "^1.30.0",
67
- "@types/node": "^26.1.1",
68
- "esbuild": "^0.28.1",
69
- "postject": "^1.0.0-alpha.6",
70
- "typescript": "^7.0.2",
71
- "zod": "^4.3.6"
72
- },
73
- "engines": {
74
- "node": ">=22"
75
- },
76
- "homepage": "https://yaw.sh/mcp-servers/lemonsqueezy-mcp/"
77
- }
1
+ {
2
+ "name": "@yawlabs/lemonsqueezy-mcp",
3
+ "version": "1.0.0",
4
+ "mcpName": "io.github.YawLabs/lemonsqueezy-mcp",
5
+ "description": "Lemon Squeezy MCP server: stores, orders, subscriptions, license keys, refunds, and webhooks.",
6
+ "license": "MIT",
7
+ "author": "YawLabs <contact@yaw.sh>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/YawLabs/lemonsqueezy-mcp.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/YawLabs/lemonsqueezy-mcp/issues"
14
+ },
15
+ "keywords": [
16
+ "lemonsqueezy",
17
+ "lemon-squeezy",
18
+ "mcp",
19
+ "mcp-server",
20
+ "model-context-protocol",
21
+ "ai",
22
+ "agent",
23
+ "claude",
24
+ "claude-code",
25
+ "claude-desktop",
26
+ "payments",
27
+ "subscriptions",
28
+ "refunds",
29
+ "license-keys",
30
+ "licensing",
31
+ "checkout",
32
+ "webhooks",
33
+ "billing",
34
+ "cursor",
35
+ "ai-agents"
36
+ ],
37
+ "type": "module",
38
+ "main": "dist/index.js",
39
+ "bin": {
40
+ "lemonsqueezy-mcp": "bin/lemonsqueezy-mcp.mjs"
41
+ },
42
+ "files": [
43
+ "bin/lemonsqueezy-mcp.mjs",
44
+ "dist/index.js",
45
+ "CHANGELOG.md"
46
+ ],
47
+ "scripts": {
48
+ "build": "tsc && node build.mjs",
49
+ "dev": "tsc --watch",
50
+ "start": "node dist/index.js",
51
+ "test": "npm run build && node --test-timeout=300000 --test \"dist/**/*.test.js\"",
52
+ "test:integration": "node scripts/require-integration-env.mjs && npm run build && node --test-timeout=300000 --test dist/integration/*.test.js",
53
+ "lint": "node scripts/lint.mjs check src/",
54
+ "lint:fix": "node scripts/lint.mjs check --write src/",
55
+ "check:oam": "oam check",
56
+ "build:binary:oam": "node scripts/build-binary.mjs --oam",
57
+ "gen:containerfile": "node scripts/sync-containerfile.mjs",
58
+ "check:containerfile": "node scripts/sync-containerfile.mjs --check",
59
+ "prepublishOnly": "npm run build"
60
+ },
61
+ "overrides": {
62
+ "hono": "^4.13.5"
63
+ },
64
+ "devDependencies": {
65
+ "@biomejs/biome": "^2.4.12",
66
+ "@modelcontextprotocol/sdk": "^1.30.0",
67
+ "@types/node": "^26.1.1",
68
+ "esbuild": "^0.28.1",
69
+ "postject": "^1.0.0-alpha.6",
70
+ "typescript": "^7.0.2",
71
+ "zod": "^4.3.6"
72
+ },
73
+ "engines": {
74
+ "node": ">=22"
75
+ },
76
+ "homepage": "https://yaw.sh/mcp-servers/lemonsqueezy-mcp/"
77
+ }