@sakupa/mcp 0.7.27 → 0.7.29

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.
Files changed (3) hide show
  1. package/dist/bin.js +22 -11
  2. package/dist/index.js +22 -11
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -129,7 +129,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
129
129
  var ALLOWED_HIDDEN_PATHS = [".well-known/"];
130
130
 
131
131
  // ../core/dist/domain/version.js
132
- var SAKUPA_MCP_VERSION = "0.7.27";
132
+ var SAKUPA_MCP_VERSION = "0.7.29";
133
133
 
134
134
  // ../core/dist/domain/errors.js
135
135
  var HTTP_STATUS = {
@@ -2631,7 +2631,7 @@ NO content was uploaded or changed by this call \u2014 to publish new or edited
2631
2631
  server.registerTool(
2632
2632
  "status",
2633
2633
  {
2634
- description: "Show the current status of this project's Sakupa site: URL, mode (free/paid), expiry, subscription state, custom domains, size, last deployment and warnings.",
2634
+ description: "Show the current status of this project's Sakupa site: URL, mode (free/paid), expiry, custom domains, size, last deployment and warnings. For a paid site this tool also automatically returns the complete authoritative billing snapshot; users never need to know or name a separate billing tool to get accurate subscription information.",
2635
2635
  outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2636
2636
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
2637
2637
  inputSchema: { projectDir: projectDirInput }
@@ -2642,12 +2642,18 @@ NO content was uploaded or changed by this call \u2014 to publish new or edited
2642
2642
  const site = requireSiteFile(ctx);
2643
2643
  const res = await ctx.client.getSiteStatus(site.siteId, site.credential);
2644
2644
  noteSiteMode(res.siteId, res.mode);
2645
+ const billing = res.mode === "paid" ? await ctx.client.getBillingStatus(site.siteId, site.credential) : void 0;
2645
2646
  const binding = res.pendingDomainBinding ? await describePendingBinding(ctx.client, site.credential, res.pendingDomainBinding) : void 0;
2646
- return textJson("status_returned", `Site status:${binding?.note ?? ""}`, {
2647
- ...res,
2648
- projectDir: ctx.projectDir,
2649
- ...binding?.checklist ? { dnsChecklist: binding.checklist } : {}
2650
- });
2647
+ return textJson(
2648
+ "status_returned",
2649
+ billing ? `Site status with AUTHORITATIVE BILLING SNAPSHOT. When answering any subscription question, use the nested billing object and report the current plan, scheduled renewal or cancellation, effective time, current entitlement, billing period, usage state and one-time carry when present:${binding?.note ?? ""}` : `Site status:${binding?.note ?? ""}`,
2650
+ {
2651
+ ...res,
2652
+ projectDir: ctx.projectDir,
2653
+ ...billing ? { billing } : {},
2654
+ ...binding?.checklist ? { dnsChecklist: binding.checklist } : {}
2655
+ }
2656
+ );
2651
2657
  } catch (e) {
2652
2658
  return toolError(e);
2653
2659
  }
@@ -2819,7 +2825,7 @@ When the user says the TXT is set, run bind "status". It verifies ownership and
2819
2825
  server.registerTool(
2820
2826
  "billing",
2821
2827
  {
2822
- description: "Show this site's hosting subscription: current plan, next renewal plan, payment state, current paid entitlement, reconciled usage, estimated usage tier, bound custom domains and risks. Owner-only (uses the credential in .sakupa/site.json).",
2828
+ description: "Return the sole authoritative source for this site's hosting subscription: current plan, next renewal plan or cancellation, effective time, payment state, current paid entitlement, reconciled usage, estimated usage tier, bound custom domains and risks. Owner-only (uses the credential in .sakupa/site.json).",
2823
2829
  outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2824
2830
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
2825
2831
  inputSchema: { projectDir: projectDirInput }
@@ -2831,15 +2837,20 @@ When the user says the TXT is set, run bind "status". It verifies ownership and
2831
2837
  const res = await ctx.client.getBillingStatus(site.siteId, site.credential);
2832
2838
  noteSiteMode(res.siteId, res.mode);
2833
2839
  const lines = [
2834
- `Billing status for site ${res.siteId} (mode: ${res.mode})`,
2840
+ `AUTHORITATIVE BILLING SNAPSHOT for site ${res.siteId} (mode: ${res.mode})`,
2835
2841
  res.permanentUrl ? `Permanent URL: ${res.permanentUrl}` : void 0,
2836
2842
  res.plan ? `Current plan: ${res.plan} (JPY ${res.monthlyPriceJpy ?? tierPriceJpy(res.plan)}/month)` : "Current plan: (no subscription yet)",
2837
2843
  res.subscriptionStatus ? `Subscription payment state: ${res.subscriptionStatus}` : void 0,
2838
- res.cancelAtPeriodEnd ? "Renewal: CANCELED \u2014 the site reverts to free at the end of the already-paid month" : res.renewalPlan ? `Next renewal plan: ${res.renewalPlan} (JPY ${res.renewalMonthlyPriceJpy ?? tierPriceJpy(res.renewalPlan)}/month)` + (res.renewalEffectiveAt ? `, effective ${res.renewalEffectiveAt}` : " (unchanged)") : void 0,
2844
+ res.cancelAtPeriodEnd === true ? `Renewal: CANCELED \u2014 the site reverts to free at ${res.cancellationEffectiveAt ?? res.currentPeriodEnd ?? "the end of the already-paid month"}` : res.renewalPlan ? `Next renewal plan: ${res.renewalPlan} (JPY ${res.renewalMonthlyPriceJpy ?? tierPriceJpy(res.renewalPlan)}/month)` + (res.renewalEffectiveAt ? `, effective ${res.renewalEffectiveAt}` : " (unchanged)") : res.cancelAtPeriodEnd === false ? "Renewal cancellation: no" : void 0,
2839
2845
  res.periodEntitlementPlan ? `Current paid entitlement: ${res.periodEntitlementPlan}` : void 0,
2840
2846
  res.currentPeriodStart ? `Current paid period: ${res.currentPeriodStart} -> ${res.currentPeriodEnd ?? "?"}` : void 0,
2847
+ `Usage state: ${res.usageState}`,
2848
+ res.currentPeriodUsage ? `Current usage: storage ${res.currentPeriodUsage.storageBytes} bytes; transfer ${res.currentPeriodUsage.transferBytes} bytes; requests ${res.currentPeriodUsage.requests}` : void 0,
2849
+ res.currentPeriodCountedTransferBytes !== void 0 ? `Transfer counted against current plan: ${res.currentPeriodCountedTransferBytes} bytes` : void 0,
2850
+ res.currentPlanLimits ? `Current plan limits: storage ${res.currentPlanLimits.storageBytes} bytes; transfer ${res.currentPlanLimits.transferBytes} bytes; requests ${res.currentPlanLimits.requests}` : void 0,
2841
2851
  res.estimatedUsageTier ? `Estimated usage tier: ${res.estimatedUsageTier}` : void 0,
2842
2852
  res.lastReconciledAt ? `Last usage reconciliation: ${res.lastReconciledAt}` : void 0,
2853
+ res.usageLagSeconds !== void 0 ? `Usage lag: ${res.usageLagSeconds} seconds` : void 0,
2843
2854
  res.oneTimeTransferCarry ? `One-time transfer carry: ${res.oneTimeTransferCarry.remainingBytes} bytes remaining (granted ${res.oneTimeTransferCarry.grantedBytes}, expires ${res.oneTimeTransferCarry.expiresAt})` : void 0,
2844
2855
  res.boundHostnames.length > 0 ? `Bound custom domains: ${res.boundHostnames.join(", ")}` : void 0,
2845
2856
  res.risks.pastDue ? "ATTENTION: renewal payment failing \u2014 update the payment method (portal). Serving continues while Stripe retries; if Stripe gives up, the site reverts to free." : void 0
@@ -3372,7 +3383,7 @@ function registerBillingTools(server, baseCtx) {
3372
3383
  outcome: "waiting_user",
3373
3384
  resultCode: "stripe_subscription_management_required",
3374
3385
  operationId: args.operationId,
3375
- summary: "Stripe subscription-management link created; the subscription has NOT changed yet. Stripe is the authoritative place to choose Water, Personal, Share, Business, or period-end cancellation. Query billing after the user finishes.",
3386
+ summary: "Stripe subscription-management link created; the subscription has NOT changed yet. Stripe is the authoritative place to choose Water, Personal, Share, Business, or period-end cancellation. After Stripe confirmation, upgrades start a new billing cycle immediately at full price; downgrades and cancellation take effect at the current period end. Query billing after the user finishes.",
3376
3387
  data: { result },
3377
3388
  userAction: {
3378
3389
  type: "open_url",
package/dist/index.js CHANGED
@@ -124,7 +124,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
124
124
  var ALLOWED_HIDDEN_PATHS = [".well-known/"];
125
125
 
126
126
  // ../core/dist/domain/version.js
127
- var SAKUPA_MCP_VERSION = "0.7.27";
127
+ var SAKUPA_MCP_VERSION = "0.7.29";
128
128
 
129
129
  // ../core/dist/domain/errors.js
130
130
  var HTTP_STATUS = {
@@ -2720,7 +2720,7 @@ NO content was uploaded or changed by this call \u2014 to publish new or edited
2720
2720
  server.registerTool(
2721
2721
  "status",
2722
2722
  {
2723
- description: "Show the current status of this project's Sakupa site: URL, mode (free/paid), expiry, subscription state, custom domains, size, last deployment and warnings.",
2723
+ description: "Show the current status of this project's Sakupa site: URL, mode (free/paid), expiry, custom domains, size, last deployment and warnings. For a paid site this tool also automatically returns the complete authoritative billing snapshot; users never need to know or name a separate billing tool to get accurate subscription information.",
2724
2724
  outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2725
2725
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
2726
2726
  inputSchema: { projectDir: projectDirInput }
@@ -2731,12 +2731,18 @@ NO content was uploaded or changed by this call \u2014 to publish new or edited
2731
2731
  const site = requireSiteFile(ctx);
2732
2732
  const res = await ctx.client.getSiteStatus(site.siteId, site.credential);
2733
2733
  noteSiteMode(res.siteId, res.mode);
2734
+ const billing = res.mode === "paid" ? await ctx.client.getBillingStatus(site.siteId, site.credential) : void 0;
2734
2735
  const binding = res.pendingDomainBinding ? await describePendingBinding(ctx.client, site.credential, res.pendingDomainBinding) : void 0;
2735
- return textJson("status_returned", `Site status:${binding?.note ?? ""}`, {
2736
- ...res,
2737
- projectDir: ctx.projectDir,
2738
- ...binding?.checklist ? { dnsChecklist: binding.checklist } : {}
2739
- });
2736
+ return textJson(
2737
+ "status_returned",
2738
+ billing ? `Site status with AUTHORITATIVE BILLING SNAPSHOT. When answering any subscription question, use the nested billing object and report the current plan, scheduled renewal or cancellation, effective time, current entitlement, billing period, usage state and one-time carry when present:${binding?.note ?? ""}` : `Site status:${binding?.note ?? ""}`,
2739
+ {
2740
+ ...res,
2741
+ projectDir: ctx.projectDir,
2742
+ ...billing ? { billing } : {},
2743
+ ...binding?.checklist ? { dnsChecklist: binding.checklist } : {}
2744
+ }
2745
+ );
2740
2746
  } catch (e) {
2741
2747
  return toolError(e);
2742
2748
  }
@@ -2908,7 +2914,7 @@ When the user says the TXT is set, run bind "status". It verifies ownership and
2908
2914
  server.registerTool(
2909
2915
  "billing",
2910
2916
  {
2911
- description: "Show this site's hosting subscription: current plan, next renewal plan, payment state, current paid entitlement, reconciled usage, estimated usage tier, bound custom domains and risks. Owner-only (uses the credential in .sakupa/site.json).",
2917
+ description: "Return the sole authoritative source for this site's hosting subscription: current plan, next renewal plan or cancellation, effective time, payment state, current paid entitlement, reconciled usage, estimated usage tier, bound custom domains and risks. Owner-only (uses the credential in .sakupa/site.json).",
2912
2918
  outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2913
2919
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
2914
2920
  inputSchema: { projectDir: projectDirInput }
@@ -2920,15 +2926,20 @@ When the user says the TXT is set, run bind "status". It verifies ownership and
2920
2926
  const res = await ctx.client.getBillingStatus(site.siteId, site.credential);
2921
2927
  noteSiteMode(res.siteId, res.mode);
2922
2928
  const lines = [
2923
- `Billing status for site ${res.siteId} (mode: ${res.mode})`,
2929
+ `AUTHORITATIVE BILLING SNAPSHOT for site ${res.siteId} (mode: ${res.mode})`,
2924
2930
  res.permanentUrl ? `Permanent URL: ${res.permanentUrl}` : void 0,
2925
2931
  res.plan ? `Current plan: ${res.plan} (JPY ${res.monthlyPriceJpy ?? tierPriceJpy(res.plan)}/month)` : "Current plan: (no subscription yet)",
2926
2932
  res.subscriptionStatus ? `Subscription payment state: ${res.subscriptionStatus}` : void 0,
2927
- res.cancelAtPeriodEnd ? "Renewal: CANCELED \u2014 the site reverts to free at the end of the already-paid month" : res.renewalPlan ? `Next renewal plan: ${res.renewalPlan} (JPY ${res.renewalMonthlyPriceJpy ?? tierPriceJpy(res.renewalPlan)}/month)` + (res.renewalEffectiveAt ? `, effective ${res.renewalEffectiveAt}` : " (unchanged)") : void 0,
2933
+ res.cancelAtPeriodEnd === true ? `Renewal: CANCELED \u2014 the site reverts to free at ${res.cancellationEffectiveAt ?? res.currentPeriodEnd ?? "the end of the already-paid month"}` : res.renewalPlan ? `Next renewal plan: ${res.renewalPlan} (JPY ${res.renewalMonthlyPriceJpy ?? tierPriceJpy(res.renewalPlan)}/month)` + (res.renewalEffectiveAt ? `, effective ${res.renewalEffectiveAt}` : " (unchanged)") : res.cancelAtPeriodEnd === false ? "Renewal cancellation: no" : void 0,
2928
2934
  res.periodEntitlementPlan ? `Current paid entitlement: ${res.periodEntitlementPlan}` : void 0,
2929
2935
  res.currentPeriodStart ? `Current paid period: ${res.currentPeriodStart} -> ${res.currentPeriodEnd ?? "?"}` : void 0,
2936
+ `Usage state: ${res.usageState}`,
2937
+ res.currentPeriodUsage ? `Current usage: storage ${res.currentPeriodUsage.storageBytes} bytes; transfer ${res.currentPeriodUsage.transferBytes} bytes; requests ${res.currentPeriodUsage.requests}` : void 0,
2938
+ res.currentPeriodCountedTransferBytes !== void 0 ? `Transfer counted against current plan: ${res.currentPeriodCountedTransferBytes} bytes` : void 0,
2939
+ res.currentPlanLimits ? `Current plan limits: storage ${res.currentPlanLimits.storageBytes} bytes; transfer ${res.currentPlanLimits.transferBytes} bytes; requests ${res.currentPlanLimits.requests}` : void 0,
2930
2940
  res.estimatedUsageTier ? `Estimated usage tier: ${res.estimatedUsageTier}` : void 0,
2931
2941
  res.lastReconciledAt ? `Last usage reconciliation: ${res.lastReconciledAt}` : void 0,
2942
+ res.usageLagSeconds !== void 0 ? `Usage lag: ${res.usageLagSeconds} seconds` : void 0,
2932
2943
  res.oneTimeTransferCarry ? `One-time transfer carry: ${res.oneTimeTransferCarry.remainingBytes} bytes remaining (granted ${res.oneTimeTransferCarry.grantedBytes}, expires ${res.oneTimeTransferCarry.expiresAt})` : void 0,
2933
2944
  res.boundHostnames.length > 0 ? `Bound custom domains: ${res.boundHostnames.join(", ")}` : void 0,
2934
2945
  res.risks.pastDue ? "ATTENTION: renewal payment failing \u2014 update the payment method (portal). Serving continues while Stripe retries; if Stripe gives up, the site reverts to free." : void 0
@@ -3565,7 +3576,7 @@ function registerBillingTools(server, baseCtx) {
3565
3576
  outcome: "waiting_user",
3566
3577
  resultCode: "stripe_subscription_management_required",
3567
3578
  operationId: args.operationId,
3568
- summary: "Stripe subscription-management link created; the subscription has NOT changed yet. Stripe is the authoritative place to choose Water, Personal, Share, Business, or period-end cancellation. Query billing after the user finishes.",
3579
+ summary: "Stripe subscription-management link created; the subscription has NOT changed yet. Stripe is the authoritative place to choose Water, Personal, Share, Business, or period-end cancellation. After Stripe confirmation, upgrades start a new billing cycle immediately at full price; downgrades and cancellation take effect at the current period end. Query billing after the user finishes.",
3569
3580
  data: { result },
3570
3581
  userAction: {
3571
3582
  type: "open_url",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakupa/mcp",
3
- "version": "0.7.27",
3
+ "version": "0.7.29",
4
4
  "description": "Sakupa MCP server: publish AI-made static sites from your AI tool. AI-made pages, live in seconds.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",