@sakupa/mcp 0.7.17 → 0.7.18

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 +10 -88
  2. package/dist/index.js +10 -88
  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.17";
132
+ var SAKUPA_MCP_VERSION = "0.7.18";
133
133
 
134
134
  // ../core/dist/domain/errors.js
135
135
  var HTTP_STATUS = {
@@ -603,20 +603,6 @@ var HttpApiClient = class {
603
603
  }
604
604
  );
605
605
  }
606
- async unbindDomain(siteId, credential, req) {
607
- return this.call(
608
- "POST",
609
- `/v1/sites/${encodeURIComponent(siteId)}/domain/unbind`,
610
- { credential, body: req }
611
- );
612
- }
613
- async previewUnbindDomain(siteId, credential, req) {
614
- return this.call(
615
- "POST",
616
- `/v1/sites/${encodeURIComponent(siteId)}/domain/unbind/preview`,
617
- { credential, body: req }
618
- );
619
- }
620
606
  async recoverDomain(req) {
621
607
  return this.call("POST", "/v1/domains/recover", { body: req });
622
608
  }
@@ -2097,9 +2083,12 @@ ${res2.message}
2097
2083
  const res = await ctx.client.bindDomain(site.credential, req);
2098
2084
  const apex = res.apexDomain;
2099
2085
  const txtShort = shortHostFor(res.verificationRecord.name, apex);
2086
+ const switchNotice = res.supersedes ? `NOTE FOR THE USER: this site already serves ${res.supersedes}. ${res.supersedes} keeps working the whole time; once www.${apex} is confirmed live, it automatically REPLACES ${res.supersedes} (zero downtime). Tell the user this before they start.
2087
+
2088
+ ` : "";
2100
2089
  return text(
2101
2090
  "domain_verification_started",
2102
- `Domain binding started for ${apex} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
2091
+ switchNotice + `Domain binding started for ${apex} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
2103
2092
 
2104
2093
  STEP 1 of 2 \u2014 add ALL THREE records NOW:
2105
2094
 
@@ -2120,6 +2109,7 @@ Drive the whole flow with bind "status": it live-checks every record and names t
2120
2109
  includedHostnames: res.includedHostnames,
2121
2110
  verificationRecord: res.verificationRecord,
2122
2111
  servingTarget: res.servingTarget,
2112
+ ...res.supersedes ? { supersedes: res.supersedes } : {},
2123
2113
  requiredRecords: [
2124
2114
  {
2125
2115
  type: "TXT",
@@ -2536,15 +2526,6 @@ var deleteConfirmation = z5.object({
2536
2526
  expectedBoundHostnames: z5.array(z5.string()),
2537
2527
  acknowledge: z5.literal("delete_and_cancel_renewal")
2538
2528
  });
2539
- var unbindConfirmation = z5.object({
2540
- siteId: z5.string().min(1),
2541
- bindingId: z5.string().min(1),
2542
- expectedBindingUpdatedAt: z5.string().datetime(),
2543
- expectedBindingStatus: z5.enum(["provisioning", "active"]),
2544
- apexDomain: z5.string().min(1),
2545
- expectedBoundHostnames: z5.array(z5.string()),
2546
- acknowledge: z5.literal("unbind_and_remove_custom_hostnames")
2547
- });
2548
2529
  function registerLifecycleTools(server, baseCtx) {
2549
2530
  server.registerTool(
2550
2531
  "delete",
@@ -2606,66 +2587,6 @@ function registerLifecycleTools(server, baseCtx) {
2606
2587
  }
2607
2588
  }
2608
2589
  );
2609
- server.registerTool(
2610
- "unbind",
2611
- {
2612
- description: "Preview or execute removal of the custom apex/www serving surface while preserving the subscription and permanent Sakupa URL.",
2613
- inputSchema: {
2614
- projectDir: projectDirInput,
2615
- action: z5.enum(["preview", "confirm"]),
2616
- operationId: z5.string().min(1).optional(),
2617
- confirmation: unbindConfirmation.optional()
2618
- },
2619
- outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2620
- annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
2621
- },
2622
- async (args) => {
2623
- try {
2624
- const ctx = withProjectDir(baseCtx, args.projectDir);
2625
- const site = requireSiteFile(ctx);
2626
- const operationId = args.operationId ?? randomUUID2();
2627
- if (args.action === "preview") {
2628
- const preview = await ctx.client.previewUnbindDomain(site.siteId, site.credential, {
2629
- operationId
2630
- });
2631
- return structuredToolResult({
2632
- schemaVersion: 1,
2633
- outcome: "waiting_user",
2634
- resultCode: "unbind_confirmation_required",
2635
- operationId,
2636
- summary: `Exact binding snapshot returned; unbinding removes ONLY the custom domain \u2014 the subscription, content, and permanent URL stay unchanged. To proceed, call unbind again with action:"confirm", operationId:"${operationId}", and the confirmation object from data.preview.confirmation.`,
2637
- data: { preview },
2638
- nextActions: [
2639
- { tool: "unbind", allowed: true, reasonCode: "exact_confirmation_required" }
2640
- ]
2641
- });
2642
- }
2643
- if (!args.confirmation) {
2644
- throw new LocalGuidanceError(
2645
- "invalid_request",
2646
- 'To CONFIRM unbind, first call unbind with action:"preview" to get the exact confirmation object bound to the current binding, then call again with action:"confirm", the same operationId, and that confirmation object.'
2647
- );
2648
- }
2649
- const result = await ctx.client.unbindDomain(site.siteId, site.credential, {
2650
- operationId,
2651
- confirmation: args.confirmation
2652
- });
2653
- const { boundDomain: _removed, ...remaining } = site;
2654
- writeSiteFile(ctx.projectDir, remaining);
2655
- return structuredToolResult({
2656
- schemaVersion: 1,
2657
- outcome: result.servingDeletionPending ? "pending_provider" : "completed",
2658
- resultCode: "domain_unbound",
2659
- operationId,
2660
- summary: `Custom domain unbound; the subscription, deployed content, and permanent Sakupa URL are unchanged. (project: ${ctx.projectDir})`,
2661
- data: { result },
2662
- nextActions: [{ tool: "status", allowed: true }]
2663
- });
2664
- } catch (error) {
2665
- return toolError(error);
2666
- }
2667
- }
2668
- );
2669
2590
  }
2670
2591
 
2671
2592
  // src/transport.ts
@@ -2764,8 +2685,9 @@ Workflow:
2764
2685
  serving surface alongside the permanent URL. Ownership is proven only by DNS control; the
2765
2686
  first verified request wins; unverified requests expire after 72 hours. billing,
2766
2687
  upgrade, portal and recover manage the paid
2767
- lifecycle; unbind removes the custom domain (the permanent URL keeps serving) and
2768
- delete tears the whole site down after explicit confirmation.
2688
+ lifecycle; delete tears the whole site down after explicit confirmation. Binding a
2689
+ NEW domain while one is live is a zero-downtime SWITCH: the old domain keeps serving
2690
+ until the new domain's www is confirmed live, then it is replaced automatically.
2769
2691
  Plan changes are confirmed only on Stripe and synchronized by Stripe webhook.
2770
2692
  A cancellation keeps the site paid through the current period. Sakupa reverts it to a free
2771
2693
  24h site and removes paid data after Stripe sends the signed final-cancellation webhook.
@@ -2779,7 +2701,7 @@ the user opened (for framework projects, where package.json lives \u2014 never t
2779
2701
  build folder; output is auto-detected). The server NEVER guesses a directory and refuses
2780
2702
  calls without one: only you can see which directory the user is actually in. After every deploy, TELL the user which environment it went to (deploy results carry an
2781
2703
  Explicit Environment line: TEST vs PRODUCTION). analyze, deploy, status,
2782
- refresh, delete and unbind echo
2704
+ refresh and delete echo
2783
2705
  the directory they acted on \u2014 verify it matches the user's active project.
2784
2706
 
2785
2707
  When the same operation fails twice in a row, or the user is clearly stuck or
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.17";
127
+ var SAKUPA_MCP_VERSION = "0.7.18";
128
128
 
129
129
  // ../core/dist/domain/errors.js
130
130
  var HTTP_STATUS = {
@@ -673,20 +673,6 @@ var HttpApiClient = class {
673
673
  }
674
674
  );
675
675
  }
676
- async unbindDomain(siteId, credential, req) {
677
- return this.call(
678
- "POST",
679
- `/v1/sites/${encodeURIComponent(siteId)}/domain/unbind`,
680
- { credential, body: req }
681
- );
682
- }
683
- async previewUnbindDomain(siteId, credential, req) {
684
- return this.call(
685
- "POST",
686
- `/v1/sites/${encodeURIComponent(siteId)}/domain/unbind/preview`,
687
- { credential, body: req }
688
- );
689
- }
690
676
  async recoverDomain(req) {
691
677
  return this.call("POST", "/v1/domains/recover", { body: req });
692
678
  }
@@ -2163,9 +2149,12 @@ ${res2.message}
2163
2149
  const res = await ctx.client.bindDomain(site.credential, req);
2164
2150
  const apex = res.apexDomain;
2165
2151
  const txtShort = shortHostFor(res.verificationRecord.name, apex);
2152
+ const switchNotice = res.supersedes ? `NOTE FOR THE USER: this site already serves ${res.supersedes}. ${res.supersedes} keeps working the whole time; once www.${apex} is confirmed live, it automatically REPLACES ${res.supersedes} (zero downtime). Tell the user this before they start.
2153
+
2154
+ ` : "";
2166
2155
  return text(
2167
2156
  "domain_verification_started",
2168
- `Domain binding started for ${apex} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
2157
+ switchNotice + `Domain binding started for ${apex} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
2169
2158
 
2170
2159
  STEP 1 of 2 \u2014 add ALL THREE records NOW:
2171
2160
 
@@ -2186,6 +2175,7 @@ Drive the whole flow with bind "status": it live-checks every record and names t
2186
2175
  includedHostnames: res.includedHostnames,
2187
2176
  verificationRecord: res.verificationRecord,
2188
2177
  servingTarget: res.servingTarget,
2178
+ ...res.supersedes ? { supersedes: res.supersedes } : {},
2189
2179
  requiredRecords: [
2190
2180
  {
2191
2181
  type: "TXT",
@@ -2530,15 +2520,6 @@ var deleteConfirmation = z4.object({
2530
2520
  expectedBoundHostnames: z4.array(z4.string()),
2531
2521
  acknowledge: z4.literal("delete_and_cancel_renewal")
2532
2522
  });
2533
- var unbindConfirmation = z4.object({
2534
- siteId: z4.string().min(1),
2535
- bindingId: z4.string().min(1),
2536
- expectedBindingUpdatedAt: z4.string().datetime(),
2537
- expectedBindingStatus: z4.enum(["provisioning", "active"]),
2538
- apexDomain: z4.string().min(1),
2539
- expectedBoundHostnames: z4.array(z4.string()),
2540
- acknowledge: z4.literal("unbind_and_remove_custom_hostnames")
2541
- });
2542
2523
  function registerLifecycleTools(server, baseCtx) {
2543
2524
  server.registerTool(
2544
2525
  "delete",
@@ -2600,66 +2581,6 @@ function registerLifecycleTools(server, baseCtx) {
2600
2581
  }
2601
2582
  }
2602
2583
  );
2603
- server.registerTool(
2604
- "unbind",
2605
- {
2606
- description: "Preview or execute removal of the custom apex/www serving surface while preserving the subscription and permanent Sakupa URL.",
2607
- inputSchema: {
2608
- projectDir: projectDirInput,
2609
- action: z4.enum(["preview", "confirm"]),
2610
- operationId: z4.string().min(1).optional(),
2611
- confirmation: unbindConfirmation.optional()
2612
- },
2613
- outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
2614
- annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
2615
- },
2616
- async (args) => {
2617
- try {
2618
- const ctx = withProjectDir(baseCtx, args.projectDir);
2619
- const site = requireSiteFile(ctx);
2620
- const operationId = args.operationId ?? randomUUID2();
2621
- if (args.action === "preview") {
2622
- const preview = await ctx.client.previewUnbindDomain(site.siteId, site.credential, {
2623
- operationId
2624
- });
2625
- return structuredToolResult({
2626
- schemaVersion: 1,
2627
- outcome: "waiting_user",
2628
- resultCode: "unbind_confirmation_required",
2629
- operationId,
2630
- summary: `Exact binding snapshot returned; unbinding removes ONLY the custom domain \u2014 the subscription, content, and permanent URL stay unchanged. To proceed, call unbind again with action:"confirm", operationId:"${operationId}", and the confirmation object from data.preview.confirmation.`,
2631
- data: { preview },
2632
- nextActions: [
2633
- { tool: "unbind", allowed: true, reasonCode: "exact_confirmation_required" }
2634
- ]
2635
- });
2636
- }
2637
- if (!args.confirmation) {
2638
- throw new LocalGuidanceError(
2639
- "invalid_request",
2640
- 'To CONFIRM unbind, first call unbind with action:"preview" to get the exact confirmation object bound to the current binding, then call again with action:"confirm", the same operationId, and that confirmation object.'
2641
- );
2642
- }
2643
- const result = await ctx.client.unbindDomain(site.siteId, site.credential, {
2644
- operationId,
2645
- confirmation: args.confirmation
2646
- });
2647
- const { boundDomain: _removed, ...remaining } = site;
2648
- writeSiteFile(ctx.projectDir, remaining);
2649
- return structuredToolResult({
2650
- schemaVersion: 1,
2651
- outcome: result.servingDeletionPending ? "pending_provider" : "completed",
2652
- resultCode: "domain_unbound",
2653
- operationId,
2654
- summary: `Custom domain unbound; the subscription, deployed content, and permanent Sakupa URL are unchanged. (project: ${ctx.projectDir})`,
2655
- data: { result },
2656
- nextActions: [{ tool: "status", allowed: true }]
2657
- });
2658
- } catch (error) {
2659
- return toolError(error);
2660
- }
2661
- }
2662
- );
2663
2584
  }
2664
2585
 
2665
2586
  // src/server.ts
@@ -2759,8 +2680,9 @@ Workflow:
2759
2680
  serving surface alongside the permanent URL. Ownership is proven only by DNS control; the
2760
2681
  first verified request wins; unverified requests expire after 72 hours. billing,
2761
2682
  upgrade, portal and recover manage the paid
2762
- lifecycle; unbind removes the custom domain (the permanent URL keeps serving) and
2763
- delete tears the whole site down after explicit confirmation.
2683
+ lifecycle; delete tears the whole site down after explicit confirmation. Binding a
2684
+ NEW domain while one is live is a zero-downtime SWITCH: the old domain keeps serving
2685
+ until the new domain's www is confirmed live, then it is replaced automatically.
2764
2686
  Plan changes are confirmed only on Stripe and synchronized by Stripe webhook.
2765
2687
  A cancellation keeps the site paid through the current period. Sakupa reverts it to a free
2766
2688
  24h site and removes paid data after Stripe sends the signed final-cancellation webhook.
@@ -2774,7 +2696,7 @@ the user opened (for framework projects, where package.json lives \u2014 never t
2774
2696
  build folder; output is auto-detected). The server NEVER guesses a directory and refuses
2775
2697
  calls without one: only you can see which directory the user is actually in. After every deploy, TELL the user which environment it went to (deploy results carry an
2776
2698
  Explicit Environment line: TEST vs PRODUCTION). analyze, deploy, status,
2777
- refresh, delete and unbind echo
2699
+ refresh and delete echo
2778
2700
  the directory they acted on \u2014 verify it matches the user's active project.
2779
2701
 
2780
2702
  When the same operation fails twice in a row, or the user is clearly stuck or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakupa/mcp",
3
- "version": "0.7.17",
3
+ "version": "0.7.18",
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",