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 +5 -0
- package/CHANGELOG.vi.md +5 -0
- package/dist/tools/page.js +102 -1
- package/package.json +1 -1
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
|
package/dist/tools/page.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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",
|