sbuilder-mcp 0.61.0 → 0.61.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 +9 -0
- package/CHANGELOG.vi.md +9 -0
- package/dist/catalog/deadkeys.generated.js +1 -1
- package/dist/catalog/elements.generated.js +32 -0
- package/dist/catalog/source.generated.js +7 -0
- package/dist/domains/site/vocabulary.js +25 -2
- package/dist/tools/session.js +22 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ 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.61.1] - 2026-09-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_connect` now reports how old the generated catalog's platform knowledge is, in words (e.g. "45 days old"), along with the web_builder commit it was generated from, so a stale install reads as staleness instead of as a missing element or key.
|
|
13
|
+
- `sb_set`'s precondition warning now also covers `filterMatch: "all"` on filter elements, which only takes effect where a product can hold several of a source's values.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `sb_set`'s precondition warning no longer reports a false positive when a sibling `specials` key is simply unset, since a `null` clause now correctly means "not set" instead of being compared as a literal value.
|
|
17
|
+
|
|
9
18
|
## [0.61.0] - 2026-09-16
|
|
10
19
|
|
|
11
20
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,15 @@ 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.61.1] - 2026-09-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_connect` giờ báo cáo kiến thức nền tảng của catalog được sinh ra đã cũ đến mức nào, bằng lời (ví dụ "45 days old"), cùng với commit web_builder mà nó được sinh ra từ đó, để một bản cài đặt lỗi thời được đọc đúng là lỗi thời thay vì bị hiểu nhầm là thiếu element hay key.
|
|
13
|
+
- Cảnh báo tiền điều kiện của `sb_set` giờ bao phủ thêm `filterMatch: "all"` trên các element bộ lọc, giá trị này chỉ có tác dụng ở nơi một sản phẩm có thể mang nhiều giá trị của một nguồn.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Cảnh báo tiền điều kiện của `sb_set` không còn báo sai (false positive) khi một key `specials` liền kề đơn giản là chưa được thiết lập, vì một clause `null` giờ được hiểu đúng là "chưa thiết lập" thay vì bị so sánh như một giá trị literal.
|
|
17
|
+
|
|
9
18
|
## [0.61.0] - 2026-09-16
|
|
10
19
|
|
|
11
20
|
### Added
|
|
@@ -36905,6 +36905,38 @@ export const WRITE_PRECONDITIONS = [
|
|
|
36905
36905
|
}
|
|
36906
36906
|
],
|
|
36907
36907
|
"otherwise": "no extra row is drawn. It is the way BACK OUT of a browse tree — a link to the category the page is serving — so it needs the page to be followed and the rows to be links. Under in-place filtering, \"everything\" is already what an untouched filter shows."
|
|
36908
|
+
},
|
|
36909
|
+
{
|
|
36910
|
+
"key": "filterMatch",
|
|
36911
|
+
"value": "all",
|
|
36912
|
+
"types": [
|
|
36913
|
+
"filter-checkbox",
|
|
36914
|
+
"filter-radio",
|
|
36915
|
+
"filter-color",
|
|
36916
|
+
"filter-tag"
|
|
36917
|
+
],
|
|
36918
|
+
"requires": [
|
|
36919
|
+
{
|
|
36920
|
+
"key": "filterSource",
|
|
36921
|
+
"anyOf": [
|
|
36922
|
+
"category",
|
|
36923
|
+
"attribute",
|
|
36924
|
+
"tag",
|
|
36925
|
+
"blog_category",
|
|
36926
|
+
"course_tag"
|
|
36927
|
+
]
|
|
36928
|
+
},
|
|
36929
|
+
{
|
|
36930
|
+
"key": "filterBehavior",
|
|
36931
|
+
"anyOf": [
|
|
36932
|
+
"filter",
|
|
36933
|
+
"navigate",
|
|
36934
|
+
"",
|
|
36935
|
+
null
|
|
36936
|
+
]
|
|
36937
|
+
}
|
|
36938
|
+
],
|
|
36939
|
+
"otherwise": "the filter combines ticked values with OR, as it always did. Both predicates IGNORE the mode outside these cases rather than honouring it — a product holds ONE brand, one price and one stock state, so \"match all\" on two ticked values could only return an empty shelf, and a stale URL degrades to the ordinary OR instead of to a blank page."
|
|
36908
36940
|
}
|
|
36909
36941
|
];
|
|
36910
36942
|
export const HOVER_HOMES = {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// GENERATED by scripts/gen-catalog.ts — do not edit by hand.
|
|
2
|
+
// The platform commit every other generated file here describes.
|
|
3
|
+
export const PLATFORM_SOURCE = {
|
|
4
|
+
"commit": "d6a68b77a7a00a6a9ca66da5161d57e845aae628",
|
|
5
|
+
"committedAt": "2026-09-17T00:01:02+07:00",
|
|
6
|
+
"dirty": false
|
|
7
|
+
};
|
|
@@ -414,6 +414,27 @@ export function deadKeyNote(namespace, key) {
|
|
|
414
414
|
* not have, and a newer deployment may honour a combination this catalog was
|
|
415
415
|
* generated before. The sentence says what the PAGE does, never "invalid".
|
|
416
416
|
*/
|
|
417
|
+
/**
|
|
418
|
+
* One clause against one stored value — a MIRROR of `holds` in
|
|
419
|
+
* schema/src/filters/preconditions.ts, and the two must agree.
|
|
420
|
+
*
|
|
421
|
+
* `null` in a clause means NOT SET, which is a real state rather than a hole:
|
|
422
|
+
* a node written before a key existed does not carry it, and both renderers
|
|
423
|
+
* then read their own fallback. Without this mapping the two copies disagreed
|
|
424
|
+
* within an hour of being written — this one called a perfectly good tag filter
|
|
425
|
+
* broken, which is a FALSE POSITIVE and worse than the silence the whole
|
|
426
|
+
* mechanism replaced, because a caller warned about correct work stops reading
|
|
427
|
+
* the warnings.
|
|
428
|
+
*
|
|
429
|
+
* It is a copy because this package has no runtime dependency on the schema —
|
|
430
|
+
* the table arrives as generated data. The cases that separate the two are
|
|
431
|
+
* asserted on both sides (`test/write-preconditions.test.ts` here,
|
|
432
|
+
* `schema/test/filter-preconditions.test.ts` there) rather than left to the
|
|
433
|
+
* reading.
|
|
434
|
+
*/
|
|
435
|
+
function holdsClause(c, stored) {
|
|
436
|
+
return c.anyOf.includes(stored === undefined ? null : stored);
|
|
437
|
+
}
|
|
417
438
|
export function preconditionNotes(type, specials) {
|
|
418
439
|
const out = [];
|
|
419
440
|
for (const p of WRITE_PRECONDITIONS) {
|
|
@@ -421,13 +442,15 @@ export function preconditionNotes(type, specials) {
|
|
|
421
442
|
continue;
|
|
422
443
|
if (specials[p.key] !== p.value)
|
|
423
444
|
continue;
|
|
424
|
-
const missing = p.requires.filter((c) => !c
|
|
445
|
+
const missing = p.requires.filter((c) => !holdsClause(c, specials[c.key]));
|
|
425
446
|
if (missing.length === 0)
|
|
426
447
|
continue;
|
|
427
448
|
const held = missing
|
|
428
449
|
.map((c) => {
|
|
429
450
|
const now = specials[c.key];
|
|
430
|
-
const want = c.anyOf
|
|
451
|
+
const want = c.anyOf
|
|
452
|
+
.map((v) => (v === null ? 'to be left unset' : JSON.stringify(v)))
|
|
453
|
+
.join(' or ');
|
|
431
454
|
// An ABSENT key is not `null`, and saying so would send a caller looking
|
|
432
455
|
// for a null they never wrote. The platform's own default applies here,
|
|
433
456
|
// which is a different thing to fix than a wrong value.
|
package/dist/tools/session.js
CHANGED
|
@@ -2,6 +2,26 @@ import { z } from 'zod';
|
|
|
2
2
|
import { request } from '../transport/http.js';
|
|
3
3
|
import { text } from '../mcp/response.js';
|
|
4
4
|
import { API_OPERATIONS } from '../catalog/api.generated.js';
|
|
5
|
+
import { PLATFORM_SOURCE } from '../catalog/source.generated.js';
|
|
6
|
+
/**
|
|
7
|
+
* The provenance line, or '' when the generator had no git to ask.
|
|
8
|
+
*
|
|
9
|
+
* Reads as an AGE rather than a commit id, because the age is the actionable
|
|
10
|
+
* half — nobody can tell whether `d6a68b7` is recent, and everybody can tell
|
|
11
|
+
* whether four months is. The id rides along for the person who then has to
|
|
12
|
+
* find it.
|
|
13
|
+
*/
|
|
14
|
+
export function catalogAge(now = new Date()) {
|
|
15
|
+
const { commit, committedAt, dirty } = PLATFORM_SOURCE;
|
|
16
|
+
if (!commit || !committedAt)
|
|
17
|
+
return '';
|
|
18
|
+
const days = Math.floor((now.getTime() - new Date(committedAt).getTime()) / 86_400_000);
|
|
19
|
+
const age = days <= 1 ? 'today' : days < 45 ? `${days} days old` : `${Math.floor(days / 30)} months old`;
|
|
20
|
+
return (`Built from web_builder ${commit.slice(0, 9)} (${committedAt.slice(0, 10)}, ${age})` +
|
|
21
|
+
(dirty ? ', from an UNCOMMITTED tree — it may describe work no deployment has' : '') +
|
|
22
|
+
`. The platform reports no version of its own, so nothing here can compare the two: if an ` +
|
|
23
|
+
`element or key you expect is missing, regenerate rather than working around it.`);
|
|
24
|
+
}
|
|
5
25
|
/**
|
|
6
26
|
* Log in and report what this server can actually reach.
|
|
7
27
|
*
|
|
@@ -26,6 +46,7 @@ export async function connect(ctx, args) {
|
|
|
26
46
|
...(ctx.siteId ? { site: ctx.siteId } : {}),
|
|
27
47
|
...(ctx.siteId && ctx.siteName ? { site_name: ctx.siteName } : {}),
|
|
28
48
|
operations: API_OPERATIONS.length,
|
|
49
|
+
...(catalogAge() ? { catalog: catalogAge() } : {}),
|
|
29
50
|
note: ctx.siteId
|
|
30
51
|
? `Connected with an API key alone, on site ${ctx.siteId} (SB_SITE)${ctx.siteName ? `, the store called ${JSON.stringify(ctx.siteName)}` : ''}. Every tool ` +
|
|
31
52
|
'defaults to it, so site_id is optional. Set SB_EMAIL and SB_PASSWORD as well if ' +
|
|
@@ -71,6 +92,7 @@ export async function connect(ctx, args) {
|
|
|
71
92
|
sites: (listed?.sites ?? []).map((s) => ({ id: s.id, name: s.name })),
|
|
72
93
|
api_key: ctx.apiKey ? 'present' : 'missing',
|
|
73
94
|
operations: API_OPERATIONS.length,
|
|
95
|
+
...(catalogAge() ? { catalog: catalogAge() } : {}),
|
|
74
96
|
};
|
|
75
97
|
if (!ctx.apiKey) {
|
|
76
98
|
result.note =
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.61.
|
|
3
|
+
"version": "0.61.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",
|