@abraca/mcp 2.10.0 → 2.13.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.
@@ -10,7 +10,7 @@ import * as os from "node:os";
10
10
  import { homedir } from "node:os";
11
11
  import * as path from "node:path";
12
12
  import { dirname, join } from "node:path";
13
- import { AbracadabraClient, AbracadabraProvider, Kind, SERVER_ROOT_ID, awarenessStatesToArray, foldRecords, isEncryptedContent, makeEntryMap, patchEntry, recordFromYAny, toPlain } from "@abraca/dabra";
13
+ import { AbracadabraClient, AbracadabraProvider, Kind, SERVER_ROOT_ID, WebSocketStatus, awarenessStatesToArray, foldRecords, isEncryptedContent, makeEntryMap, patchEntry, recordFromYAny, toPlain } from "@abraca/dabra";
14
14
  import * as Y from "yjs";
15
15
  import { mkdir, readFile, writeFile } from "node:fs/promises";
16
16
  import { parseFrontmatter, populateYDocFromMarkdown, yjsToMarkdown } from "@abraca/convert";
@@ -21471,6 +21471,7 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
21471
21471
  this._activeTurnChannel = null;
21472
21472
  this._signFn = null;
21473
21473
  this._toolHistory = [];
21474
+ this._reconnecting = null;
21474
21475
  this._inboxStarted = false;
21475
21476
  this._inboxDocId = null;
21476
21477
  this._inboxDoc = null;
@@ -21641,7 +21642,60 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
21641
21642
  * child-content provider cache survives — content docs are keyed by global
21642
21643
  * guid, independent of which space is active.
21643
21644
  */
21645
+ /**
21646
+ * Heal the active connection before a tool op so a dropped/idle WebSocket or
21647
+ * an expired JWT doesn't surface as a 15s sync timeout ("MCP not responding").
21648
+ * Refreshes the token, lets the SDK's own auto-reconnect finish if it's mid-
21649
+ * flight, and rebuilds the space connection from scratch if the socket is
21650
+ * truly dead. De-duped so parallel tool calls heal once. Best-effort: never
21651
+ * throws — a failed heal falls through to the normal (possibly erroring) op.
21652
+ */
21653
+ /** Whether a provider's shared WebSocket is currently connected. Isolated in
21654
+ * its own scope so repeated checks don't trip TS control-flow narrowing on
21655
+ * the live `connectionStatus` getter. */
21656
+ _wsConnected(provider) {
21657
+ return provider.connectionStatus === WebSocketStatus.Connected;
21658
+ }
21659
+ async ensureConnected() {
21660
+ if (this._reconnecting) return this._reconnecting;
21661
+ this._reconnecting = (async () => {
21662
+ try {
21663
+ if (!this.client.isTokenValid() && this._signFn && this._userId) try {
21664
+ await this.client.loginWithKey(this._userId, this._signFn);
21665
+ } catch (e) {
21666
+ console.error("[abracadabra-mcp] Re-auth during heal failed:", e);
21667
+ }
21668
+ const conn = this._activeConnection;
21669
+ if (!conn) return;
21670
+ if (this._wsConnected(conn.provider)) return;
21671
+ try {
21672
+ await waitForSync(conn.provider, 6e3);
21673
+ } catch {}
21674
+ if (this._wsConnected(conn.provider)) return;
21675
+ console.error("[abracadabra-mcp] Active connection dead — rebuilding space provider…");
21676
+ const docId = conn.docId;
21677
+ this._spaceConnections.delete(docId);
21678
+ try {
21679
+ conn.provider.destroy();
21680
+ } catch {}
21681
+ for (const [, cached] of this.childCache) try {
21682
+ cached.provider.destroy();
21683
+ } catch {}
21684
+ this.childCache.clear();
21685
+ try {
21686
+ await this._connectToSpace(docId);
21687
+ console.error("[abracadabra-mcp] Space provider rebuilt + synced");
21688
+ } catch (e) {
21689
+ console.error("[abracadabra-mcp] Connection rebuild failed:", e);
21690
+ }
21691
+ } finally {
21692
+ this._reconnecting = null;
21693
+ }
21694
+ })();
21695
+ return this._reconnecting;
21696
+ }
21644
21697
  async ensureSpaceActive(targetId) {
21698
+ await this.ensureConnected();
21645
21699
  if (!targetId || targetId === this._rootDocId) return true;
21646
21700
  if (this._spaces.some((s) => s.id === targetId)) {
21647
21701
  await this._connectToSpace(targetId);
@@ -21698,11 +21752,18 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
21698
21752
  * Caches providers and waits for sync before returning.
21699
21753
  */
21700
21754
  async getChildProvider(docId) {
21755
+ await this.ensureConnected();
21701
21756
  const cached = this.childCache.get(docId);
21702
- if (cached) {
21757
+ if (cached && cached.provider.connectionStatus !== WebSocketStatus.Disconnected) {
21703
21758
  cached.lastAccessed = Date.now();
21704
21759
  return cached.provider;
21705
21760
  }
21761
+ if (cached) {
21762
+ try {
21763
+ cached.provider.destroy();
21764
+ } catch {}
21765
+ this.childCache.delete(docId);
21766
+ }
21706
21767
  const activeProvider = this._activeConnection?.provider;
21707
21768
  if (!activeProvider) throw new Error("Not connected. Call connect() first.");
21708
21769
  if (!this.client.isTokenValid() && this._signFn && this._userId) {