@abraca/mcp 2.16.0 → 2.17.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/dist/abracadabra-mcp.cjs +35 -22
- package/dist/abracadabra-mcp.cjs.map +1 -1
- package/dist/abracadabra-mcp.esm.js +36 -23
- package/dist/abracadabra-mcp.esm.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/package.json +2 -2
- package/src/server.ts +66 -42
- package/src/tools/content.ts +8 -1
|
@@ -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, WebSocketStatus, awarenessStatesToArray, foldRecords, isEncryptedContent, makeEntryMap, patchEntry, recordFromYAny, toPlain } from "@abraca/dabra";
|
|
13
|
+
import { AbracadabraClient, AbracadabraProvider, Kind, SERVER_ROOT_ID, WebSocketStatus, awarenessStatesToArray, foldRecords, isEncryptedContent, makeEntryMap, patchEntry, reconcileDocCover, 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";
|
|
@@ -21461,6 +21461,7 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
|
|
|
21461
21461
|
this._spaceConnections = /* @__PURE__ */ new Map();
|
|
21462
21462
|
this.childCache = /* @__PURE__ */ new Map();
|
|
21463
21463
|
this.evictionTimer = null;
|
|
21464
|
+
this.heartbeatTimer = null;
|
|
21464
21465
|
this._mcpServerRef = null;
|
|
21465
21466
|
this._serverRef = null;
|
|
21466
21467
|
this._handledTaskIds = /* @__PURE__ */ new Set();
|
|
@@ -21558,6 +21559,9 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
|
|
|
21558
21559
|
await this._connectToSpace(initialDocId);
|
|
21559
21560
|
console.error("[abracadabra-mcp] Space doc synced");
|
|
21560
21561
|
this.evictionTimer = setInterval(() => this.evictIdle(), 6e4);
|
|
21562
|
+
this.heartbeatTimer = setInterval(() => {
|
|
21563
|
+
this.ensureConnected();
|
|
21564
|
+
}, 3e4);
|
|
21561
21565
|
}
|
|
21562
21566
|
/** Connect to a space's root doc and cache it. Sets it as the active connection. */
|
|
21563
21567
|
async _connectToSpace(docId) {
|
|
@@ -21666,34 +21670,37 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
|
|
|
21666
21670
|
console.error("[abracadabra-mcp] Re-auth during heal failed:", e);
|
|
21667
21671
|
}
|
|
21668
21672
|
const conn = this._activeConnection;
|
|
21669
|
-
if (
|
|
21670
|
-
if (this.
|
|
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
|
-
}
|
|
21673
|
+
if (conn) await this._healProvider(conn.provider, "space");
|
|
21674
|
+
if (this._inboxProvider) await this._healProvider(this._inboxProvider, "inbox");
|
|
21691
21675
|
} finally {
|
|
21692
21676
|
this._reconnecting = null;
|
|
21693
21677
|
}
|
|
21694
21678
|
})();
|
|
21695
21679
|
return this._reconnecting;
|
|
21696
21680
|
}
|
|
21681
|
+
/**
|
|
21682
|
+
* Bring one provider's socket back to a synced state WITHOUT swapping its
|
|
21683
|
+
* Y.Doc. First let the SDK's own auto-reconnect finish if it's mid-flight;
|
|
21684
|
+
* if the socket is still dead, force a reconnect on the same doc. Because the
|
|
21685
|
+
* document is never replaced, every observer attached to it (root awareness,
|
|
21686
|
+
* tree reads, the inbox `entries` array, the stateless chat listener) keeps
|
|
21687
|
+
* firing — no re-attach needed. Best-effort: logs but never throws.
|
|
21688
|
+
*/
|
|
21689
|
+
async _healProvider(provider, label) {
|
|
21690
|
+
if (this._wsConnected(provider)) return;
|
|
21691
|
+
try {
|
|
21692
|
+
await waitForSync(provider, 6e3);
|
|
21693
|
+
} catch {}
|
|
21694
|
+
if (this._wsConnected(provider)) return;
|
|
21695
|
+
console.error(`[abracadabra-mcp] ${label} socket dead — forcing reconnect on the same doc…`);
|
|
21696
|
+
provider.reconnect();
|
|
21697
|
+
try {
|
|
21698
|
+
await waitForSync(provider, 1e4);
|
|
21699
|
+
console.error(`[abracadabra-mcp] ${label} reconnected + re-synced`);
|
|
21700
|
+
} catch (e) {
|
|
21701
|
+
console.error(`[abracadabra-mcp] ${label} reconnect did not re-sync in time:`, e);
|
|
21702
|
+
}
|
|
21703
|
+
}
|
|
21697
21704
|
async ensureSpaceActive(targetId) {
|
|
21698
21705
|
await this.ensureConnected();
|
|
21699
21706
|
if (!targetId || targetId === this._rootDocId) return true;
|
|
@@ -22300,6 +22307,10 @@ var AbracadabraMCPServer = class AbracadabraMCPServer {
|
|
|
22300
22307
|
clearInterval(this.evictionTimer);
|
|
22301
22308
|
this.evictionTimer = null;
|
|
22302
22309
|
}
|
|
22310
|
+
if (this.heartbeatTimer) {
|
|
22311
|
+
clearInterval(this.heartbeatTimer);
|
|
22312
|
+
this.heartbeatTimer = null;
|
|
22313
|
+
}
|
|
22303
22314
|
for (const dispose of this._inboxDisposers) try {
|
|
22304
22315
|
dispose();
|
|
22305
22316
|
} catch {}
|
|
@@ -22767,6 +22778,8 @@ function registerContentTools(mcp, server) {
|
|
|
22767
22778
|
const contentToWrite = body || markdown;
|
|
22768
22779
|
const existingLabel = server.getDocSummary(docId)?.label;
|
|
22769
22780
|
populateYDocFromMarkdown(fragment, contentToWrite, title || existingLabel || "Untitled");
|
|
22781
|
+
const coverTree = server.getTreeMap();
|
|
22782
|
+
if (coverTree) reconcileDocCover(coverTree, docId, fragment);
|
|
22770
22783
|
server.setFocusedDoc(docId);
|
|
22771
22784
|
server.setDocCursor(docId, fragment.length);
|
|
22772
22785
|
return { content: [{
|