sbuilder-mcp 0.56.0 → 0.58.0

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,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.58.0] - 2026-09-15
10
+
11
+ ### Added
12
+ - `sb_review` now reports `blogCategoryPage` and `coursePage` readiness gaps when a site has blog categories or courses but no published page of the `blog` or `course` type, since `/blog-categories/{slug}` and `/courses/{slug}` fall back to those templates and every entity link 404s without them, completing the check against all five entity prefixes the storefront registers.
13
+
14
+ ## [0.57.0] - 2026-09-15
15
+
16
+ ### Added
17
+ - `sb_review` now reports a `categoryPage` readiness gap when a store has product categories but no published page of the `category` type, since `/collections/{slug}` falls back to that template and every collection link, including the ones in the site's own menu, 404s without it.
18
+ - `sb_review` now reports an `articleTemplate` readiness gap when a site has written blog articles but no published page of the `post` type, since `/blog/{slug}` falls back to that template and every article link, including the ones a listing page carries, 404s without it.
19
+
9
20
  ## [0.56.0] - 2026-09-15
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.58.0] - 2026-09-15
10
+
11
+ ### Added
12
+ - `sb_review` giờ báo lỗi sẵn sàng `blogCategoryPage` và `coursePage` khi site có danh mục blog hoặc khoá học nhưng chưa có trang loại `blog` hoặc `course` nào được publish, vì `/blog-categories/{slug}` và `/courses/{slug}` sẽ dùng các template đó, và mọi liên kết sẽ trả về 404 nếu thiếu — hoàn tất việc kiểm tra cho cả năm tiền tố thực thể mà storefront đăng ký.
13
+
14
+ ## [0.57.0] - 2026-09-15
15
+
16
+ ### Added
17
+ - `sb_review` giờ báo lỗi sẵn sàng `categoryPage` khi store có danh mục sản phẩm nhưng chưa có trang loại `category` nào được publish, vì `/collections/{slug}` sẽ dùng template đó, và mọi liên kết danh mục — kể cả những liên kết trong menu của chính site — sẽ trả về 404 nếu thiếu.
18
+ - `sb_review` giờ báo lỗi sẵn sàng `articleTemplate` khi site đã viết bài blog nhưng chưa có trang loại `post` nào được publish, vì `/blog/{slug}` sẽ dùng template đó, và mọi liên kết bài viết — kể cả những liên kết trong trang danh sách bài viết — sẽ trả về 404 nếu thiếu.
19
+
9
20
  ## [0.56.0] - 2026-09-15
10
21
 
11
22
  ### Added
@@ -24,7 +24,7 @@ export async function gatherReadiness(ctx, siteId, pageNodes) {
24
24
  }
25
25
  };
26
26
  const site = encodeURIComponent(siteId);
27
- const [pageList, gateways, shipping, globals, productList, categoryList, pageLinks, formList] = await Promise.all([
27
+ const [pageList, gateways, shipping, globals, productList, categoryList, pageLinks, formList, articleList, blogCategoryList, courseList] = await Promise.all([
28
28
  get(`/api/sites/${site}/pages`),
29
29
  get(`/api/sites/${site}/payment-gateways`),
30
30
  get(`/api/sites/${site}/shipping-methods`),
@@ -48,6 +48,17 @@ export async function gatherReadiness(ctx, siteId, pageNodes) {
48
48
  // blindness is what let one page quietly become the site's whole account
49
49
  // area, which is the shape `mergedAuthPage` reports.
50
50
  get(`/api/sites/${site}/forms`),
51
+ // THE ARTICLES, for the same reason the products are read: /blog/{slug}
52
+ // resolves to the site's `post` template, so a site that has written
53
+ // articles and has no template 404s every one of them.
54
+ get(`/api/sites/${site}/articles?limit=1`),
55
+ // THE LAST TWO ENTITY PREFIXES the storefront registers
56
+ // (storefront/entityroute.go): /blog-categories/{slug} and /courses/{slug}.
57
+ // Each resolves to the site's published page of its own TYPE, so each has
58
+ // the hole /products/{slug} has, and counting is how we know the site uses
59
+ // the prefix at all.
60
+ get(`/api/sites/${site}/blog-categories?limit=1`),
61
+ get(`/api/sites/${site}/courses?limit=1`),
51
62
  ]);
52
63
  // A gateway counts only when it is BOTH enabled and configured — the editor's
53
64
  // `live` getter also filters by the store's currency, which is a narrowing:
@@ -58,6 +69,10 @@ export async function gatherReadiness(ctx, siteId, pageNodes) {
58
69
  : null;
59
70
  const methods = shipping?.shippingMethods ?? shipping?.methods;
60
71
  const shippingMethods = Array.isArray(methods) ? methods.length : null;
72
+ // Either key, like the product categories below: this reader has met both
73
+ // spellings from the platform and guessing one would read a real list as none.
74
+ const rawBlogCats = blogCategoryList?.blogCategories ?? blogCategoryList?.categories;
75
+ const blogCats = Array.isArray(rawBlogCats) ? rawBlogCats : null;
61
76
  const cats = categoryList?.categories ?? categoryList?.productCategories;
62
77
  const categories = Array.isArray(cats) ? (categoryList?.total ?? cats.length) : null;
63
78
  const links = pageLinks?.links ?? pageLinks?.pageLinks;
@@ -94,5 +109,12 @@ export async function gatherReadiness(ctx, siteId, pageNodes) {
94
109
  categories,
95
110
  categoryPageLinks,
96
111
  forms: formList?.forms ?? null,
112
+ articles: Array.isArray(articleList?.articles)
113
+ ? (articleList?.total ?? articleList.articles.length)
114
+ : null,
115
+ blogCategories: blogCats ? (blogCategoryList?.total ?? blogCats.length) : null,
116
+ courses: Array.isArray(courseList?.courses)
117
+ ? (courseList?.total ?? courseList.courses.length)
118
+ : null,
97
119
  };
98
120
  }
@@ -178,6 +178,60 @@ export function readinessGaps(input) {
178
178
  });
179
179
  }
180
180
  }
181
+ // ARTICLES WRITTEN AND NOTHING TO RENDER THEM IN.
182
+ //
183
+ // `post` is the ARTICLE template: /blog/{slug} resolves to the site's
184
+ // published page of that type, so a site that has written articles and has
185
+ // none 404s every single one — including the links its own listing page
186
+ // carries, which is the page that makes the articles findable in the first
187
+ // place.
188
+ //
189
+ // ASKED OF EVERY SITE, above the store gate: a blog is not a commerce
190
+ // feature, and a brochure site with a news section has exactly this problem.
191
+ // Gated on having WRITTEN something, so a site with no blog hears nothing.
192
+ if (pages && (input.articles ?? 0) > 0 && !published(pages, 'post')) {
193
+ const draft = drafted(pages, 'post');
194
+ gaps.push({
195
+ id: 'articleTemplate',
196
+ draft,
197
+ problem: `This site has ${input.articles} article${input.articles === 1 ? '' : 's'} and no ` +
198
+ 'published page of the "post" type. /blog/{slug} needs one, so every article 404s — ' +
199
+ 'including the links a listing page carries.',
200
+ fix: draft
201
+ ? 'Publish the article template page that already exists.'
202
+ : 'Create a page of type "post" and publish it. It opens seeded with the cover, ' +
203
+ 'headline, date and body an article cannot be read without.',
204
+ });
205
+ }
206
+ // THE LAST TWO ENTITY PREFIXES, the same hole in the same shape.
207
+ //
208
+ // storefront/entityroute.go registers five: /products, /collections, /blog,
209
+ // /blog-categories and /courses. Each resolves to the site's published page of
210
+ // its own TYPE, so each 404s everything under it when that page is missing.
211
+ // Four of the five are now checked above; these are the other two.
212
+ //
213
+ // COUNTED, NOT ASSUMED. A site with no blog categories has nothing under
214
+ // /blog-categories to break, and a site with no courses has not installed the
215
+ // app — the count IS the gate, which is why neither needs to ask about the
216
+ // app separately. Both sit above the store gate: a course is sold, but a blog
217
+ // is not commerce, and a site can be neither and still have one.
218
+ for (const [count, type, id, prefix, what] of [
219
+ [input.blogCategories, 'blog', 'blogCategoryPage', '/blog-categories/{slug}', 'blog category'],
220
+ [input.courses, 'course', 'coursePage', '/courses/{slug}', 'course'],
221
+ ]) {
222
+ if (!pages || (count ?? 0) === 0 || published(pages, type))
223
+ continue;
224
+ const draft = drafted(pages, type);
225
+ gaps.push({
226
+ id,
227
+ draft,
228
+ problem: `This site has ${count} ${what}${count === 1 ? '' : count && count > 1 ? 's' : ''} and no ` +
229
+ `published page of the "${type}" type. ${prefix} needs one, so every ${what} link 404s.`,
230
+ fix: draft
231
+ ? `Publish the ${what} template page that already exists.`
232
+ : `Create a page of type "${type}" and publish it.`,
233
+ });
234
+ }
181
235
  if (!isStore(input))
182
236
  return gaps;
183
237
  if (pages && !published(pages, 'checkout')) {
@@ -219,6 +273,36 @@ export function readinessGaps(input) {
219
273
  : 'Create a page of type "product" and publish it.',
220
274
  });
221
275
  }
276
+ // AND THE SAME HOLE ON THE OTHER SIDE OF THE CATALOGUE.
277
+ //
278
+ // `/collections/{slug}` resolves the same way /products/{slug} does — through
279
+ // PublishedForEntity, to the category's own page or to the DEFAULT TEMPLATE
280
+ // for the `category` type — so a store with categories and no published one
281
+ // 404s every collection link, including the ones its own menu carries.
282
+ //
283
+ // `categoryScope` below is a different defect and assumes this page EXISTS:
284
+ // it reports a template SHARED across categories, which is the complaint you
285
+ // can only make once there is a template to share. With none, every URL fails
286
+ // outright and the scope question does not arise.
287
+ //
288
+ // GATED ON HAVING CATEGORIES, unlike productPage. A store can genuinely sell
289
+ // without collections — one shop, one flat catalogue — and telling it to build
290
+ // a template for a thing it does not use is the nag this file keeps warning
291
+ // about. Silent when the count could not be read, like everything else here.
292
+ if (pages && (input.categories ?? 0) > 0 && !published(pages, 'category')) {
293
+ const draft = drafted(pages, 'category');
294
+ gaps.push({
295
+ id: 'categoryPage',
296
+ draft,
297
+ problem: `This store has ${input.categories} categor${input.categories === 1 ? 'y' : 'ies'} and no ` +
298
+ 'published page of the "category" type. /collections/{slug} needs one, so every ' +
299
+ 'collection link 404s — including the ones in the site\'s own menu.',
300
+ fix: draft
301
+ ? 'Publish the category template page that already exists.'
302
+ : 'Create a page of type "category" and publish it. It opens seeded with the collection ' +
303
+ 'title and a product repeater.',
304
+ });
305
+ }
222
306
  // NOTHING TO SELL. Checked before the delivery option, because a shipping
223
307
  // method for an empty catalogue is furniture.
224
308
  if (input.products) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.56.0",
3
+ "version": "0.58.0",
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",