sbuilder-mcp 0.2.1 → 0.2.2
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 +6 -0
- package/CHANGELOG.vi.md +6 -0
- package/dist/tools/live.js +29 -14
- package/dist/transport/media.js +19 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ 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.2.2] - 2026-09-07
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- sb_live_join now accepts an API key (SB_TOKEN) to join the live-edit room, instead of refusing it and requiring a session; the platform now gates the socket on the key's delegated member permission and its own site, so a key-only install can join like every other tool.
|
|
13
|
+
- sb_media_upload now accepts an API key, instead of refusing it and requiring a session; the platform's upload endpoint moved behind the same gate as sb_sites, and a 401 with a key present now reports that the key lacks the media permission or belongs to another site, rather than telling the caller to switch credentials.
|
|
14
|
+
|
|
9
15
|
## [0.2.1] - 2026-09-07
|
|
10
16
|
|
|
11
17
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,12 @@ 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.2.2] - 2026-09-07
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- sb_live_join giờ chấp nhận API key (SB_TOKEN) để vào phòng live-edit, thay vì từ chối và bắt buộc dùng session; nền tảng giờ kiểm soát socket dựa trên quyền member được ủy quyền qua key và site của chính key đó, nên một cài đặt chỉ dùng key có thể tham gia như mọi tool khác.
|
|
13
|
+
- sb_media_upload giờ chấp nhận API key, thay vì từ chối và bắt buộc dùng session; endpoint upload của nền tảng giờ nằm sau cùng một cổng chặn với sb_sites, và lỗi 401 khi có key giờ báo rằng key thiếu quyền media hoặc thuộc site khác, thay vì bảo caller đổi sang loại credential khác.
|
|
14
|
+
|
|
9
15
|
## [0.2.1] - 2026-09-07
|
|
10
16
|
|
|
11
17
|
### Added
|
package/dist/tools/live.js
CHANGED
|
@@ -60,30 +60,45 @@ export function bindNode(doc, id, source, field) {
|
|
|
60
60
|
];
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* The
|
|
63
|
+
* The credential that opens the live-edit room.
|
|
64
64
|
*
|
|
65
|
-
* `server/internal/server/realtime.go:
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* AN AGENT KEY NOW WORKS. `server/internal/server/realtime.go:44` gives a `wbk_`
|
|
66
|
+
* bearer "the same door as a session", decided from the token's own shape, and
|
|
67
|
+
* gates it on member.read through the key's DELEGATED principal — so the key
|
|
68
|
+
* sees the room only if the member who minted it may, and only on the site the
|
|
69
|
+
* key belongs to. This function used to refuse a key-only context outright,
|
|
70
|
+
* which was true before agent keys landed and now turns away a working setup.
|
|
71
|
+
*
|
|
72
|
+
* The peer that appears on the canvas is then the KEY, not a person: the
|
|
73
|
+
* platform returns `key.ID` and `key.Name` rather than the minter's name,
|
|
74
|
+
* deliberately — an avatar borrowing a human's name would tell the room a person
|
|
75
|
+
* is editing when a machine is. So the merchant sees the label they chose for
|
|
76
|
+
* the key moving around the page.
|
|
77
|
+
*
|
|
78
|
+
* Still a GETTER, read per attempt: a session access token lives ~15 minutes and
|
|
79
|
+
* rotates, and a captured string replays an expired token forever on every
|
|
80
|
+
* reconnect — silently, because a rejected socket auth still fires `onopen`.
|
|
69
81
|
*/
|
|
70
|
-
export function
|
|
71
|
-
if (!ctx.session.loggedIn()) {
|
|
72
|
-
throw new Error('sbuilder: the live-edit room
|
|
73
|
-
'
|
|
74
|
-
'works with the key alone.');
|
|
82
|
+
export function liveTokenFor(ctx) {
|
|
83
|
+
if (!ctx.session.loggedIn() && !ctx.apiKey) {
|
|
84
|
+
throw new Error('sbuilder: the live-edit room needs a credential. Set SB_TOKEN, or SB_EMAIL and ' +
|
|
85
|
+
'SB_PASSWORD, and call sb_connect before sb_live_join.');
|
|
75
86
|
}
|
|
76
|
-
|
|
87
|
+
// Prefer the key, for the reason `tokenFor` prefers it everywhere: a key is
|
|
88
|
+
// narrower — one site, its own scopes, revocable on its own — while a session
|
|
89
|
+
// carries the whole account.
|
|
90
|
+
return () => (ctx.apiKey ? ctx.apiKey : ctx.session.token());
|
|
77
91
|
}
|
|
78
92
|
export function registerLiveTools(server, ctx, session) {
|
|
79
93
|
server.registerTool('sb_live_join', {
|
|
80
94
|
description: "Join the site's live-edit room as a visible peer: every write then appears in any open " +
|
|
81
|
-
'editor as it happens
|
|
82
|
-
'
|
|
95
|
+
'editor as it happens, with the agent shown by the API key\'s own name rather than a ' +
|
|
96
|
+
"person's. Always yields, so it is safe beside a human. Works with SB_TOKEN or with " +
|
|
97
|
+
'SB_EMAIL / SB_PASSWORD.',
|
|
83
98
|
inputSchema: { site_id: z.string() },
|
|
84
99
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
|
85
100
|
}, async ({ site_id }) => {
|
|
86
|
-
const tokenFn =
|
|
101
|
+
const tokenFn = liveTokenFor(ctx);
|
|
87
102
|
const wsBase = ctx.base.replace(/^http/, 'ws').replace(/\/$/, '');
|
|
88
103
|
const socket = new RealtimeSocket(`${wsBase}/api/realtime/ws?site=${encodeURIComponent(site_id)}`, tokenFn);
|
|
89
104
|
const live = new LiveSession(socket, {
|
package/dist/transport/media.js
CHANGED
|
@@ -66,18 +66,27 @@ export async function uploadMedia(ctx, siteId, source) {
|
|
|
66
66
|
throw new ApiError(res.status, 'non_json_response', raw.slice(0, 400));
|
|
67
67
|
}
|
|
68
68
|
if (!res.ok) {
|
|
69
|
-
//
|
|
69
|
+
// AN API KEY CAN UPLOAD — THE PLATFORM WIDENED THIS.
|
|
70
70
|
//
|
|
71
|
-
// `/api/media
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
71
|
+
// `/api/media` is now mounted behind `RequireAuthOrDefer`
|
|
72
|
+
// (server/internal/server/router.go:2821), the same gate `/api/sites` uses,
|
|
73
|
+
// and `upload_agentkey_test.go` pins the three answers that make it safe:
|
|
74
|
+
// the key's own site only, the same permission the session path checks, and
|
|
75
|
+
// no key at all still meaning 401. The platform's own comment gives the
|
|
76
|
+
// reason it changed — the media LIBRARY already took a key while the UPLOAD
|
|
77
|
+
// refused one, so a merchant could hand an agent a key that manages every
|
|
78
|
+
// image the store has and cannot add one.
|
|
79
|
+
//
|
|
80
|
+
// This message used to say "an API key cannot upload" and send the caller to
|
|
81
|
+
// set SB_EMAIL. That is now the wrong instruction: with a key present, a 401
|
|
82
|
+
// here means the KEY is wrong for this call, not that the wrong KIND of
|
|
83
|
+
// credential was used, and the old text sent people to fix something that
|
|
84
|
+
// was never broken.
|
|
77
85
|
if ((res.status === 401 || res.status === 403) && ctx.apiKey && !ctx.session.loggedIn()) {
|
|
78
|
-
throw new ApiError(res.status, '
|
|
79
|
-
'
|
|
80
|
-
'
|
|
86
|
+
throw new ApiError(res.status, 'media_key_refused', 'sbuilder: the platform refused this API key for the upload. It accepts a key, so the ' +
|
|
87
|
+
'cause is the key itself: it needs the media permission, and it must belong to THIS ' +
|
|
88
|
+
'site — a key minted for another site is refused before the upload is read. Check the ' +
|
|
89
|
+
"key's scopes and its site, or set SB_EMAIL / SB_PASSWORD to upload as a person.");
|
|
81
90
|
}
|
|
82
91
|
const env = (parsed ?? {});
|
|
83
92
|
const fieldText = env.fields
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|