sbuilder-mcp 0.63.0 → 0.63.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 CHANGED
@@ -6,6 +6,14 @@ 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.63.1] - 2026-09-24
10
+
11
+ ### Fixed
12
+ - Live-edit frames now carry the open page's id from the moment a room is joined and whenever a new page is opened afterward, so the editor's own per-page filter no longer drops every ops, cursor, and select frame this server sends.
13
+ - A second `sb_live_join`, or opening a page on a different site while already in a room, now leaves the previous room instead of leaving its socket connected and reconnecting for the rest of the process.
14
+ - Opening a page on a site other than the one the current live room belongs to now leaves that room first, so a failed join can no longer strand the session in the wrong site's room.
15
+ - The server process now exits when the MCP client closes stdin, instead of being kept alive indefinitely by the live-room socket and its reconnect timer.
16
+
9
17
  ## [0.63.0] - 2026-09-21
10
18
 
11
19
  ### Added
package/CHANGELOG.vi.md CHANGED
@@ -6,6 +6,14 @@ 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.63.1] - 2026-09-24
10
+
11
+ ### Fixed
12
+ - Các frame live-edit giờ mang theo id của trang đang mở ngay từ lúc vào phòng và mỗi khi một trang mới được mở sau đó, để bộ lọc theo từng trang của chính editor không còn loại bỏ mọi frame ops, cursor và select mà server này gửi đi.
13
+ - Gọi `sb_live_join` lần thứ hai, hoặc mở một trang thuộc site khác trong khi đang ở trong một phòng, giờ sẽ rời phòng cũ thay vì để socket của nó tiếp tục kết nối và tự reconnect trong suốt phần đời còn lại của tiến trình.
14
+ - Mở một trang thuộc site khác với site của phòng live hiện tại giờ sẽ rời phòng đó trước, để một lần join thất bại không còn có thể khiến session bị mắc kẹt trong phòng của site sai.
15
+ - Tiến trình server giờ thoát khi client MCP đóng stdin, thay vì bị giữ sống vô thời hạn bởi socket của phòng live và timer reconnect của nó.
16
+
9
17
  ## [0.63.0] - 2026-09-21
10
18
 
11
19
  ### Added
package/dist/index.js CHANGED
@@ -11,6 +11,14 @@ async function main() {
11
11
  }
12
12
  const server = createServer();
13
13
  await server.connect(new StdioServerTransport());
14
+ // THE CLIENT CLOSED STDIN, SO THIS PROCESS IS DONE. The SDK does not exit on
15
+ // its own, and the live-room socket (plus its reconnect timer) keeps the event
16
+ // loop alive — an orphaned process that answers the server's pings, so the
17
+ // agent's avatar stays in the room indefinitely. Exiting closes the socket and
18
+ // the server publishes the leave.
19
+ const done = () => process.exit(0);
20
+ process.stdin.once('end', done);
21
+ process.stdin.once('close', done);
14
22
  // stdout is the MCP channel. Every log line in this repo is console.error.
15
23
  console.error('[sbuilder-mcp] ready on stdio');
16
24
  }
@@ -65,6 +65,10 @@ export class LiveSession {
65
65
  if (this.selfId)
66
66
  this.announcePage();
67
67
  }
68
+ /** Leave the room: the socket closes, the server publishes our `leave`. */
69
+ close() {
70
+ this.socket.close();
71
+ }
68
72
  announcePage() {
69
73
  if (!this.pageId)
70
74
  return;
@@ -261,7 +261,7 @@ export function joinRoom(ctx, session, siteId) {
261
261
  onDesync: (reason) => session.markStale(reason),
262
262
  });
263
263
  socket.connect();
264
- session.attachLive(live);
264
+ session.attachLive(live, siteId);
265
265
  }
266
266
  export function registerLiveTools(server, ctx, session) {
267
267
  // The page session joins on its own at the first sb_page_open. Registered
@@ -61,6 +61,8 @@ export class PageSession {
61
61
  siteId = '';
62
62
  pageId = '';
63
63
  live = null;
64
+ /** Which site's room `live` is in — a page on another site needs another room. */
65
+ liveSite = '';
64
66
  stale = null;
65
67
  /**
66
68
  * The document revision this session last stored, so an unchanged document is
@@ -79,8 +81,28 @@ export class PageSession {
79
81
  constructor(ctx) {
80
82
  this.ctx = ctx;
81
83
  }
82
- attachLive(live) {
84
+ /**
85
+ * ONE ROOM AT A TIME. A second join (sb_live_join again, or a page on another
86
+ * site) used to overwrite `live` and leave the old socket reconnecting for the
87
+ * life of the process — a second robot in the old site's room, forever.
88
+ *
89
+ * And the page is announced HERE as well as on open: `start` was never called
90
+ * anywhere, so every ops/cursor/select frame went out with `pageId: ''` and
91
+ * the editor, which filters by page, dropped all of them.
92
+ */
93
+ attachLive(live, siteId) {
94
+ if (this.live !== live)
95
+ this.live?.close();
83
96
  this.live = live;
97
+ this.liveSite = siteId;
98
+ if (this.pageId && this.siteId === siteId)
99
+ live.start(this.pageId);
100
+ }
101
+ /** Leave the room, if in one. */
102
+ leaveLive() {
103
+ this.live?.close();
104
+ this.live = null;
105
+ this.liveSite = '';
84
106
  }
85
107
  /**
86
108
  * How to open the live-edit room, handed over by `registerLiveTools`.
@@ -113,10 +135,13 @@ export class PageSession {
113
135
  * rather than a new one.
114
136
  */
115
137
  async ensureLive(siteId) {
116
- if (this.live)
138
+ if (this.live && this.liveSite === siteId)
117
139
  return 'already';
118
140
  if (!this.liveJoiner)
119
141
  return 'no live transport is registered';
142
+ // Out of the old site's room first, so a failed join below cannot leave
143
+ // this process sitting in a room for a site it is no longer editing.
144
+ this.leaveLive();
120
145
  try {
121
146
  this.liveJoiner(siteId);
122
147
  return this.live ? 'joined' : 'the live transport did not attach';
@@ -314,6 +339,11 @@ export class PageSession {
314
339
  // `sb_template_use` and `shareChrome` all open pages too, and a join wired to
315
340
  // sb_page_open alone leaves every one of those editing unseen.
316
341
  this.liveState = await this.ensureLive(siteId);
342
+ // Tell the room this page. A fresh join already did (attachLive); an
343
+ // existing one has only heard of the previous page. Without it the editor
344
+ // drops every frame this session sends.
345
+ if (this.liveState === 'already')
346
+ this.live?.start(pageId);
317
347
  return this.doc.outline();
318
348
  }
319
349
  /** What the last open's join attempt did, for the tool that reports it. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.63.0",
3
+ "version": "0.63.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",