sbuilder-mcp 0.83.0 → 0.83.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 +11 -0
- package/CHANGELOG.vi.md +11 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/dist/catalog/elements.generated.js +12 -0
- package/dist/domains/site/readiness.js +10 -2
- package/dist/domains/site/review.js +4 -2
- package/dist/domains/site/storepage.js +34 -1
- package/dist/tools/page.js +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ 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.83.1] - 2026-10-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_bind` accepts `brand.*` and `blogCategory.*` binding sources, raising the known binding sources from 82 to 94.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- `sb_page_create` with the `brand` or `course` page type, or with `seed: false`, returns a `next` hint explaining how to build the page, since publishing an empty one makes every entity URL answer 500.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `sb_review` no longer reports `dead_binding_source` for valid `blogCategory.*` bindings.
|
|
19
|
+
|
|
9
20
|
## [0.83.0] - 2026-10-11
|
|
10
21
|
|
|
11
22
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,17 @@ 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.83.1] - 2026-10-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_bind` nhận nguồn binding `brand.*` và `blogCategory.*`, nâng số nguồn binding được biết từ 82 lên 94.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- `sb_page_create` với loại trang `brand` hoặc `course`, hoặc với `seed: false`, trả gợi ý `next` nêu cách dựng trang, vì publish trang rỗng khiến mọi URL thực thể trả 500.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `sb_review` không còn báo `dead_binding_source` cho binding `blogCategory.*` hợp lệ.
|
|
19
|
+
|
|
9
20
|
## [0.83.0] - 2026-10-11
|
|
10
21
|
|
|
11
22
|
### Added
|
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ in this client: one would put the very key the platform exists to hold back into
|
|
|
119
119
|
| `sb_undo` | Undo / redo this session's writes — page and form edits and `sb_api_call` PUTs; a page is refused if someone else saved it since. The platform's versions and history (`sb_page_version`) outlive this process |
|
|
120
120
|
|
|
121
121
|
Twenty-eight tools, **830 API operations** (302 of the 381 writes carrying a body shape read
|
|
122
|
-
off the handler), 123 elements,
|
|
122
|
+
off the handler), 123 elements, 94 binding sources. `sb_api_find`
|
|
123
123
|
is an index rather than a tool per endpoint, so the tool list stays short while everything
|
|
124
124
|
the platform can do stays reachable — and operations added to the platform arrive with the
|
|
125
125
|
next `npm run codegen`.
|
package/README.vi.md
CHANGED
|
@@ -116,7 +116,7 @@ nhét ngược lại vào mọi bản cài.
|
|
|
116
116
|
| `sb_undo` | Hoàn tác / làm lại các thao tác ghi trong phiên — chỉnh sửa trang, form và PUT qua `sb_api_call`; trang bị từ chối nếu người khác đã lưu sau đó. Version và history của nền tảng (`sb_page_version`) sống lâu hơn process này |
|
|
117
117
|
|
|
118
118
|
Hai mươi tám tool, **830 operation API** (302 trong 381 lệnh ghi có hình dạng body đọc thẳng
|
|
119
|
-
từ handler), 123 element,
|
|
119
|
+
từ handler), 123 element, 94 nguồn binding. `sb_api_find` là
|
|
120
120
|
một chỉ mục chứ không phải mỗi endpoint một tool, nên danh sách tool vẫn ngắn trong khi mọi
|
|
121
121
|
thứ nền tảng làm được vẫn với tới — và operation mới thêm bên nền tảng sẽ tự có sau lần
|
|
122
122
|
`npm run codegen` kế tiếp.
|
|
@@ -41952,6 +41952,17 @@ export const BINDING_SOURCES = [
|
|
|
41952
41952
|
"article.tags",
|
|
41953
41953
|
"article.title",
|
|
41954
41954
|
"article.url",
|
|
41955
|
+
"blogCategory.description",
|
|
41956
|
+
"blogCategory.image",
|
|
41957
|
+
"blogCategory.title",
|
|
41958
|
+
"blogCategory.url",
|
|
41959
|
+
"brand.description",
|
|
41960
|
+
"brand.image",
|
|
41961
|
+
"brand.title",
|
|
41962
|
+
"brand.url",
|
|
41963
|
+
"cartLine.lineTotalCents",
|
|
41964
|
+
"cartLine.quantity",
|
|
41965
|
+
"cartLine.variant",
|
|
41955
41966
|
"category.description",
|
|
41956
41967
|
"category.image",
|
|
41957
41968
|
"category.title",
|
|
@@ -42008,6 +42019,7 @@ export const BINDING_SOURCES = [
|
|
|
42008
42019
|
"review.stars",
|
|
42009
42020
|
"review.title",
|
|
42010
42021
|
"review.verified",
|
|
42022
|
+
"searchRow.url",
|
|
42011
42023
|
"site.addressLine",
|
|
42012
42024
|
"site.country",
|
|
42013
42025
|
"site.description",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { cartGalleryThumbs, cartRelocalize, cartSeedLocale } from './cartlang.js';
|
|
20
20
|
import { PRODUCT_ACTION_BINDING_ID } from './navhref.js';
|
|
21
|
+
import { hasSeed } from './storepage.js';
|
|
21
22
|
/** Element types that only make sense in a store. */
|
|
22
23
|
const COMMERCE_TYPES = [
|
|
23
24
|
'cart-order',
|
|
@@ -435,14 +436,21 @@ export function readinessGaps(input) {
|
|
|
435
436
|
if (!pages || (count ?? 0) === 0 || published(pages, type))
|
|
436
437
|
continue;
|
|
437
438
|
const draft = drafted(pages, type);
|
|
439
|
+
// A type the platform ships no seed for opens EMPTY, and an empty entity
|
|
440
|
+
// template answers 500 at every one of its URLs (storefront/entityroute.go:
|
|
441
|
+
// empty body → "entity render failed") — so "publish it" alone trades the
|
|
442
|
+
// 404s for 500s and clears this gap. Measured on a brand page.
|
|
443
|
+
const withContent = hasSeed(type)
|
|
444
|
+
? ''
|
|
445
|
+
: ` WITH content on it — an empty one answers 500 at every ${prefix}; sb_page_create's \`next\` says what to add`;
|
|
438
446
|
gaps.push({
|
|
439
447
|
id,
|
|
440
448
|
draft,
|
|
441
449
|
problem: `This site has ${count} ${what}${count === 1 ? '' : count && count > 1 ? 's' : ''} and no ` +
|
|
442
450
|
`published page of the "${type}" type. ${prefix} needs one, so every ${what} link 404s.`,
|
|
443
451
|
fix: draft
|
|
444
|
-
? `Publish the ${what} template page that already exists.`
|
|
445
|
-
: `Create a page of type "${type}" and publish it.`,
|
|
452
|
+
? `Publish the ${what} template page that already exists${withContent}.`
|
|
453
|
+
: `Create a page of type "${type}" and publish it${withContent}.`,
|
|
446
454
|
});
|
|
447
455
|
}
|
|
448
456
|
// A CART DRAWER IN ANOTHER LANGUAGE. Asked before the store gate: a drawer
|
|
@@ -131,17 +131,19 @@ function drawsItsOwnContent(type) {
|
|
|
131
131
|
return (BOUND_SPECIALS[type] ?? []).some((k) => !LINK_SPECIALS.has(k));
|
|
132
132
|
}
|
|
133
133
|
/** Binding-source prefixes that name a RECORD (not `site.*`, which every page has). */
|
|
134
|
-
const RECORD_ENTITIES = new Set(['product', 'category', 'course', 'article', 'blogCategory']);
|
|
134
|
+
const RECORD_ENTITIES = new Set(['product', 'category', 'course', 'article', 'blogCategory', 'brand']);
|
|
135
135
|
/**
|
|
136
136
|
* Which records a page TYPE supplies outside any repeater — read off the
|
|
137
137
|
* platform's own seeds (STORE_PAGE_SEEDS bind exactly these at top level).
|
|
138
138
|
*/
|
|
139
|
-
const PAGE_ENTITIES = {
|
|
139
|
+
export const PAGE_ENTITIES = {
|
|
140
140
|
product: ['product'],
|
|
141
141
|
category: ['category'],
|
|
142
142
|
course: ['course'],
|
|
143
143
|
post: ['article'],
|
|
144
144
|
blog: ['blogCategory', 'category'],
|
|
145
|
+
// /brands/{slug} — the page supplies brandContext (web_builder f70fe86d1).
|
|
146
|
+
brand: ['brand'],
|
|
145
147
|
};
|
|
146
148
|
/** Does this node or an ancestor PIN a record (`bindings[0].target.id`)? */
|
|
147
149
|
function pinnedAbove(d, id) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { COMPLETION_HEADLINE, COMPLETION_HEADLINE_SENTINEL, PAGE_LAYOUT_SEEDS, STORE_PAGE_SEEDS } from '../../catalog/storepages.generated.js';
|
|
1
|
+
import { COMPLETION_HEADLINE, COMPLETION_HEADLINE_SENTINEL, PAGE_LAYOUT_SEEDS, PAGE_TYPES, STORE_PAGE_SEEDS } from '../../catalog/storepages.generated.js';
|
|
2
|
+
import { BINDING_SOURCES } from '../../catalog/elements.generated.js';
|
|
2
3
|
import { withFreshIds } from './ids.js';
|
|
4
|
+
import { PAGE_ENTITIES } from './review.js';
|
|
3
5
|
/**
|
|
4
6
|
* WHAT A NEW STORE PAGE OPENS WITH — for an agent, as for a merchant.
|
|
5
7
|
*
|
|
@@ -55,6 +57,37 @@ export function seedDocument(type, opts = {}) {
|
|
|
55
57
|
const escaped = JSON.stringify(headline).slice(1, -1);
|
|
56
58
|
return withFreshIds(JSON.parse(raw.split(COMPLETION_HEADLINE_SENTINEL).join(escaped)));
|
|
57
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* What to put on an ENTITY TEMPLATE that opens empty, or null.
|
|
62
|
+
*
|
|
63
|
+
* An entity template (a type whose route carries `{slug}`) with an empty
|
|
64
|
+
* document answers 500 "entity render failed" at EVERY one of its URLs once
|
|
65
|
+
* published — `storefront/entityroute.go` refuses an empty body rather than
|
|
66
|
+
* serve a blank 200. Measured on a brand page: empty → 500 at /brands/acme,
|
|
67
|
+
* one heading → 200. The editor seeds some of these types and not others
|
|
68
|
+
* (brand and course today), and `seed:false` or a refused seed write leaves any
|
|
69
|
+
* of them empty, so the rule is "entity template + nothing seeded", not a name.
|
|
70
|
+
*
|
|
71
|
+
* A NOTE, NOT AN INVENTED SEED: the platform ships none for brand, and a seed
|
|
72
|
+
* authored here would be a document nothing upstream keeps current.
|
|
73
|
+
*/
|
|
74
|
+
export function emptyEntityTemplateNote(type) {
|
|
75
|
+
const route = PAGE_TYPES.find((t) => t.type === type)?.routePattern;
|
|
76
|
+
const entity = type ? PAGE_ENTITIES[type]?.[0] : undefined;
|
|
77
|
+
if (!route?.includes('{slug}') || !entity)
|
|
78
|
+
return null;
|
|
79
|
+
const has = (k) => BINDING_SOURCES.includes(`${entity}.${k}`);
|
|
80
|
+
const parts = [
|
|
81
|
+
...(has('title') ? [`a heading bound to ${entity}.title`] : []),
|
|
82
|
+
...(has('description') ? [`a text bound to ${entity}.description`] : []),
|
|
83
|
+
...(type === 'brand'
|
|
84
|
+
? ['a product grid narrowed to the brand the URL names: sb_template_use template_id "sb_product_shelf", then sb_set its list-dataset config.collectionType "page_brand" (a bare list-dataset has no card and renders empty)']
|
|
85
|
+
: []),
|
|
86
|
+
];
|
|
87
|
+
return (`This "${type}" page is EMPTY. Published like that, every ${route} answers 500 ` +
|
|
88
|
+
'"entity render failed". ' +
|
|
89
|
+
`Before publishing, sb_add ${parts.join(', ')} — bind the heading and text with sb_bind, field "specials.text".`);
|
|
90
|
+
}
|
|
58
91
|
/** A one-line summary of what a seed puts on the page, for the tool result. */
|
|
59
92
|
export function seedSummary(type) {
|
|
60
93
|
const seed = STORE_PAGE_SEEDS[type];
|
package/dist/tools/page.js
CHANGED
|
@@ -12,7 +12,7 @@ import { addSubtree, setMany, baseOnlyKeys, moveNode, removeNode, duplicateNode,
|
|
|
12
12
|
import { baseOnlyNote } from '../domains/site/baseonly.js';
|
|
13
13
|
import { detachNote, presetIdOf, presetLayer } from '../domains/site/theme.js';
|
|
14
14
|
import { inertHintsFor } from '../domains/site/inert.js';
|
|
15
|
-
import { hasSeed, layoutDocument, seedDocument, seedSummary, seededTypes, } from '../domains/site/storepage.js';
|
|
15
|
+
import { emptyEntityTemplateNote, hasSeed, layoutDocument, seedDocument, seedSummary, seededTypes, } from '../domains/site/storepage.js';
|
|
16
16
|
import { PAGE_TYPES } from '../catalog/storepages.generated.js';
|
|
17
17
|
import { skinLevelNote } from '../domains/site/fieldskin.js';
|
|
18
18
|
import { siteTheme } from '../domains/site/theme-fetch.js';
|
|
@@ -2358,6 +2358,7 @@ export function registerPageTools(server, ctx) {
|
|
|
2358
2358
|
body: redact(body),
|
|
2359
2359
|
...(summary ? { would_seed: { type, ...summary } } : {}),
|
|
2360
2360
|
...(layout ? { would_open_as: layout } : {}),
|
|
2361
|
+
...(!summary && !layout && emptyEntityTemplateNote(type) ? { next: emptyEntityTemplateNote(type) } : {}),
|
|
2361
2362
|
...(wear.header || wear.footer
|
|
2362
2363
|
? { would_wear: { ...(wear.header ? { header: wear.header } : {}), ...(wear.footer ? { footer: wear.footer } : {}) } }
|
|
2363
2364
|
: {}),
|
|
@@ -2415,9 +2416,14 @@ export function registerPageTools(server, ctx) {
|
|
|
2415
2416
|
}
|
|
2416
2417
|
}
|
|
2417
2418
|
const wearing = typeof newId === 'string' && newId ? await wearChrome(session, site_id, newId, wear) : undefined;
|
|
2419
|
+
// An entity template that ended up with nothing on it 500s at every
|
|
2420
|
+
// entity URL once published — say what to add, whatever the reason
|
|
2421
|
+
// (no platform seed, seed:false, a refused seed write).
|
|
2422
|
+
const blankNote = !seeded || 'failed' in seeded ? emptyEntityTemplateNote(type) : null;
|
|
2418
2423
|
return text({
|
|
2419
2424
|
...res,
|
|
2420
2425
|
...(seeded ? { seeded } : {}),
|
|
2426
|
+
...(blankNote ? { next: blankNote } : {}),
|
|
2421
2427
|
...(wearing ? { chrome: wearing } : {}),
|
|
2422
2428
|
...(renamed
|
|
2423
2429
|
? {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.83.
|
|
3
|
+
"version": "0.83.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",
|