sbuilder-mcp 0.47.0 → 0.47.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/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ All notable changes to this project are documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.47.1] - 2026-09-14
10
+
11
+ ### Fixed
12
+ - `sb_page_create` with `is_homepage` set on a site that already has one no longer creates a duplicate that steals the star and leaves the platform's own original home page reachable at no address; it now adopts the existing home page instead, renaming it when the caller named it something else, and reports what it did instead of creating anything.
13
+
9
14
  ## [0.47.0] - 2026-09-14
10
15
 
11
16
  ### Added
package/CHANGELOG.vi.md CHANGED
@@ -6,6 +6,11 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
6
6
  Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.47.1] - 2026-09-14
10
+
11
+ ### Fixed
12
+ - `sb_page_create` với `is_homepage` được bật trên một site đã có sẵn trang chủ giờ không còn tạo ra một trang trùng lặp cướp mất ngôi sao và khiến trang chủ gốc của nền tảng trở nên không thể truy cập ở bất kỳ địa chỉ nào; giờ nó nhận lại trang chủ hiện có, đổi tên nếu người gọi đặt tên khác, và báo cáo việc đã làm thay vì tạo trang mới.
13
+
9
14
  ## [0.47.0] - 2026-09-14
10
15
 
11
16
  ### Added
@@ -311,6 +311,29 @@ async function siteChrome(ctx, siteId) {
311
311
  return {};
312
312
  }
313
313
  }
314
+ /**
315
+ * The site's own home page, or null when it genuinely has none.
316
+ *
317
+ * THROWS RATHER THAN GUESSES. Answering "none" for a listing that could not be
318
+ * read is the one wrong answer available here: it is indistinguishable from an
319
+ * empty site, and the caller acts on it by creating a home page the site
320
+ * already had. `siteChrome` above may swallow its read because a page created
321
+ * without chrome is a page a person can fix; this one may not, because the page
322
+ * it creates cannot be un-created and takes the star off whichever page held it.
323
+ */
324
+ async function existingHomepage(ctx, siteId) {
325
+ const listed = (await request({
326
+ base: ctx.base,
327
+ method: 'GET',
328
+ path: `/api/sites/${encodeURIComponent(siteId)}/pages`,
329
+ token: siteToken(ctx),
330
+ fetchImpl: ctx.fetchImpl,
331
+ }));
332
+ const home = (listed.pages ?? []).find((p) => p.isHomepage === true);
333
+ if (!home || typeof home.id !== 'string' || !home.id)
334
+ return null;
335
+ return { id: home.id, name: typeof home.name === 'string' ? home.name : '' };
336
+ }
314
337
  export function registerPageTools(server, ctx) {
315
338
  const session = new PageSession(ctx);
316
339
  server.registerTool('sb_page_open', {
@@ -1012,7 +1035,85 @@ export function registerPageTools(server, ctx) {
1012
1035
  // fine, while `siteChrome` asks whether the SITE has globals and it does.
1013
1036
  // Measured: three pages built with these tools, every one of them bare,
1014
1037
  // beside a store page carrying its header as ROOT's first child.
1015
- const wear = chrome !== false ? await siteChrome(ctx, site_id) : {};
1038
+ // A SITE HAS ONE HOME PAGE, AND BY THE TIME AN AGENT ASKS FOR ONE IT
1039
+ // USUALLY EXISTS ALREADY.
1040
+ //
1041
+ // `isHomepage: true` does not mean "make this the home page" to the
1042
+ // platform. It means MOVE THE STAR: CreatePage demotes whoever holds it
1043
+ // and promotes this one. An agent building a store reads the flag the
1044
+ // first way and asks for it on a site the editor already gave a home page
1045
+ // to, so the site ends up with two — the new one on "/", the old one
1046
+ // demoted and holding nothing. Measured on a real store: pg_439cb121
1047
+ // "Home" and pg_237719d4 "Trang chủ", both slug "", both path "/", the
1048
+ // first reachable at no address at all and still listed as a page.
1049
+ //
1050
+ // So the flag is honoured as what the caller meant — the site's home page
1051
+ // — which is the one it already has. Adopted, renamed when the caller
1052
+ // named it something else, never duplicated. The same rule sb_import_site
1053
+ // already follows when its entry page lands on a site with a home page.
1054
+ //
1055
+ // REPLACING the home page with a DIFFERENT page stays possible and stays
1056
+ // explicit: create it without the flag, build it, then PATCH isHomepage
1057
+ // through sb_api_call. That is a decision, and it should read like one.
1058
+ const adopt = is_homepage === true ? await existingHomepage(ctx, site_id) : null;
1059
+ const rename = adopt && name.trim() !== '' && name !== adopt.name ? name : '';
1060
+ // Read AFTER the adopt decision and skipped when it holds: siteChrome
1061
+ // reads the header and footer off the home page, so asking it which
1062
+ // chrome to dress the home page in is two requests to answer "its own".
1063
+ const wear = chrome !== false && !adopt ? await siteChrome(ctx, site_id) : {};
1064
+ if (adopt && dry_run !== false) {
1065
+ return text({
1066
+ dry_run: true,
1067
+ into: 'the existing home page',
1068
+ page: adopt,
1069
+ ...(rename ? { would_rename: { from: adopt.name, to: rename } } : {}),
1070
+ note: 'Nothing would be created. This site already has a home page and a site has ' +
1071
+ 'exactly one, so a create carrying isHomepage would have taken the star off ' +
1072
+ `${JSON.stringify(adopt.name)} and left it with no address. Open ${adopt.id} ` +
1073
+ 'with sb_page_open and build it.',
1074
+ });
1075
+ }
1076
+ if (adopt) {
1077
+ // THE RENAME IS THE ONLY WRITE. Not the seed — this page may already be
1078
+ // the site's front door, and overwriting a document nobody asked to
1079
+ // replace is the one thing worse than the duplicate this branch exists
1080
+ // to prevent. Not the chrome either: siteChrome reads the header and
1081
+ // footer OFF the home page, so this page is where they came from.
1082
+ let renamed_to;
1083
+ let rename_failed;
1084
+ if (rename) {
1085
+ try {
1086
+ await request({
1087
+ base: ctx.base,
1088
+ method: 'PATCH',
1089
+ path: `${path}/${encodeURIComponent(adopt.id)}`,
1090
+ token: siteToken(ctx),
1091
+ body: { name: rename },
1092
+ fetchImpl: ctx.fetchImpl,
1093
+ });
1094
+ renamed_to = rename;
1095
+ }
1096
+ catch (e) {
1097
+ // The page is still the right one to build on, so a failed rename
1098
+ // is reported, never raised — the caller asked for a home page and
1099
+ // this is it, under its old name.
1100
+ rename_failed = e.message.replace(/^sbuilder:\s*/, '').slice(0, 160);
1101
+ }
1102
+ }
1103
+ return text({
1104
+ into: 'the existing home page',
1105
+ page: { id: adopt.id, name: renamed_to ?? adopt.name },
1106
+ ...(renamed_to ? { renamed: { from: adopt.name, to: renamed_to } } : {}),
1107
+ ...(rename_failed ? { rename_failed } : {}),
1108
+ ...(slug ? { slug_ignored: 'A home page is served at "/" and carries no slug.' } : {}),
1109
+ ...(type && type !== 'page' ? { type_ignored: `Kept as it is; adopting does not retype a page to "${type}".` } : {}),
1110
+ note: 'Nothing was created. This site already had a home page and a site has exactly ' +
1111
+ 'one, so creating another would have taken the star off this page and left it ' +
1112
+ `with no address. Open ${adopt.id} with sb_page_open and build it. To hand the ` +
1113
+ 'home page over to a DIFFERENT page instead, create that page WITHOUT ' +
1114
+ 'is_homepage and then PATCH isHomepage on it through sb_api_call.',
1115
+ });
1116
+ }
1016
1117
  if (dry_run !== false) {
1017
1118
  return text({
1018
1119
  dry_run: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
5
5
  "mcpName": "io.github.vuluu2k/sbuilder-mcp",
6
6
  "type": "module",