@abraca/mcp 1.0.12 → 1.0.15
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 +123 -13
- package/dist/abracadabra-mcp.cjs.map +1 -1
- package/dist/abracadabra-mcp.esm.js +123 -13
- package/dist/abracadabra-mcp.esm.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/package.json +1 -1
- package/src/server.ts +99 -0
- package/src/tools/channel.ts +11 -0
- package/src/tools/content.ts +12 -0
- package/src/tools/tree.ts +9 -1
package/dist/abracadabra-mcp.cjs
CHANGED
|
@@ -19446,6 +19446,9 @@ var AbracadabraMCPServer = class {
|
|
|
19446
19446
|
this._serverRef = null;
|
|
19447
19447
|
this._handledTaskIds = /* @__PURE__ */ new Set();
|
|
19448
19448
|
this._userId = null;
|
|
19449
|
+
this._statusClearTimer = null;
|
|
19450
|
+
this._typingInterval = null;
|
|
19451
|
+
this._lastChatChannel = null;
|
|
19449
19452
|
this.config = config;
|
|
19450
19453
|
this.client = new _abraca_dabra.AbracadabraClient({
|
|
19451
19454
|
url: config.url,
|
|
@@ -19540,7 +19543,8 @@ var AbracadabraMCPServer = class {
|
|
|
19540
19543
|
provider.awareness.setLocalStateField("user", {
|
|
19541
19544
|
name: this.agentName,
|
|
19542
19545
|
color: this.agentColor,
|
|
19543
|
-
publicKey: this._userId
|
|
19546
|
+
publicKey: this._userId,
|
|
19547
|
+
isAgent: true
|
|
19544
19548
|
});
|
|
19545
19549
|
const conn = {
|
|
19546
19550
|
doc,
|
|
@@ -19593,7 +19597,8 @@ var AbracadabraMCPServer = class {
|
|
|
19593
19597
|
childProvider.awareness.setLocalStateField("user", {
|
|
19594
19598
|
name: this.agentName,
|
|
19595
19599
|
color: this.agentColor,
|
|
19596
|
-
publicKey: this._userId
|
|
19600
|
+
publicKey: this._userId,
|
|
19601
|
+
isAgent: true
|
|
19597
19602
|
});
|
|
19598
19603
|
this.childCache.set(docId, {
|
|
19599
19604
|
provider: childProvider,
|
|
@@ -19660,6 +19665,7 @@ var AbracadabraMCPServer = class {
|
|
|
19660
19665
|
const user = state["user"];
|
|
19661
19666
|
const senderName = user && typeof user === "object" && typeof user.name === "string" ? user.name : "Unknown";
|
|
19662
19667
|
console.error(`[abracadabra-mcp] Handling ai:task id=${id} from ${senderName}: ${text.slice(0, 80)}`);
|
|
19668
|
+
this.setAutoStatus("thinking");
|
|
19663
19669
|
this._dispatchAiTask({
|
|
19664
19670
|
id,
|
|
19665
19671
|
text,
|
|
@@ -19724,6 +19730,18 @@ var AbracadabraMCPServer = class {
|
|
|
19724
19730
|
const parts = channel.split(":");
|
|
19725
19731
|
if (parts.length === 3 && parts[1] !== this._userId && parts[2] !== this._userId) return;
|
|
19726
19732
|
}
|
|
19733
|
+
if (channel) {
|
|
19734
|
+
const rootProvider = this._activeConnection?.provider;
|
|
19735
|
+
if (rootProvider) rootProvider.sendStateless(JSON.stringify({
|
|
19736
|
+
type: "chat:mark_read",
|
|
19737
|
+
channel,
|
|
19738
|
+
timestamp: Math.floor(Date.now() / 1e3)
|
|
19739
|
+
}));
|
|
19740
|
+
this._lastChatChannel = channel;
|
|
19741
|
+
this.sendTypingIndicator(channel);
|
|
19742
|
+
this._startTypingInterval(channel);
|
|
19743
|
+
}
|
|
19744
|
+
this.setAutoStatus("thinking");
|
|
19727
19745
|
await this._serverRef.notification({
|
|
19728
19746
|
method: "notifications/claude/channel",
|
|
19729
19747
|
params: {
|
|
@@ -19741,8 +19759,65 @@ var AbracadabraMCPServer = class {
|
|
|
19741
19759
|
console.error(`[abracadabra-mcp] Chat message from ${data.sender_name ?? "unknown"} on ${channel}`);
|
|
19742
19760
|
} catch {}
|
|
19743
19761
|
}
|
|
19762
|
+
/**
|
|
19763
|
+
* Set the agent's status in root awareness with auto-clear after idle.
|
|
19764
|
+
*/
|
|
19765
|
+
setAutoStatus(status, docId) {
|
|
19766
|
+
const provider = this._activeConnection?.provider;
|
|
19767
|
+
if (!provider) return;
|
|
19768
|
+
if (this._statusClearTimer) {
|
|
19769
|
+
clearTimeout(this._statusClearTimer);
|
|
19770
|
+
this._statusClearTimer = null;
|
|
19771
|
+
}
|
|
19772
|
+
provider.awareness.setLocalStateField("status", status);
|
|
19773
|
+
if (docId !== void 0) provider.awareness.setLocalStateField("docId", docId);
|
|
19774
|
+
if (!status) this._stopTypingInterval();
|
|
19775
|
+
if (status) this._statusClearTimer = setTimeout(() => {
|
|
19776
|
+
provider.awareness.setLocalStateField("status", null);
|
|
19777
|
+
provider.awareness.setLocalStateField("activeToolCall", null);
|
|
19778
|
+
this._stopTypingInterval();
|
|
19779
|
+
}, 3e4);
|
|
19780
|
+
}
|
|
19781
|
+
/** Re-send typing indicator every 2s so dashboard keeps showing it (expires at 3s). */
|
|
19782
|
+
_startTypingInterval(channel) {
|
|
19783
|
+
this._stopTypingInterval();
|
|
19784
|
+
this._typingInterval = setInterval(() => {
|
|
19785
|
+
this.sendTypingIndicator(channel);
|
|
19786
|
+
}, 2e3);
|
|
19787
|
+
}
|
|
19788
|
+
_stopTypingInterval() {
|
|
19789
|
+
if (this._typingInterval) {
|
|
19790
|
+
clearInterval(this._typingInterval);
|
|
19791
|
+
this._typingInterval = null;
|
|
19792
|
+
}
|
|
19793
|
+
this._lastChatChannel = null;
|
|
19794
|
+
}
|
|
19795
|
+
/**
|
|
19796
|
+
* Broadcast which tool the agent is currently executing.
|
|
19797
|
+
* Dashboard renders this as a ChatTool indicator.
|
|
19798
|
+
*/
|
|
19799
|
+
setActiveToolCall(toolCall) {
|
|
19800
|
+
this._activeConnection?.provider?.awareness.setLocalStateField("activeToolCall", toolCall);
|
|
19801
|
+
}
|
|
19802
|
+
/**
|
|
19803
|
+
* Send a typing indicator to a chat channel.
|
|
19804
|
+
*/
|
|
19805
|
+
sendTypingIndicator(channel) {
|
|
19806
|
+
const rootProvider = this._activeConnection?.provider;
|
|
19807
|
+
if (!rootProvider) return;
|
|
19808
|
+
rootProvider.sendStateless(JSON.stringify({
|
|
19809
|
+
type: "chat:typing",
|
|
19810
|
+
channel,
|
|
19811
|
+
sender_name: this.agentName
|
|
19812
|
+
}));
|
|
19813
|
+
}
|
|
19744
19814
|
/** Graceful shutdown. */
|
|
19745
19815
|
async destroy() {
|
|
19816
|
+
this._stopTypingInterval();
|
|
19817
|
+
if (this._statusClearTimer) {
|
|
19818
|
+
clearTimeout(this._statusClearTimer);
|
|
19819
|
+
this._statusClearTimer = null;
|
|
19820
|
+
}
|
|
19746
19821
|
if (this.evictionTimer) {
|
|
19747
19822
|
clearInterval(this.evictionTimer);
|
|
19748
19823
|
this.evictionTimer = null;
|
|
@@ -19894,12 +19969,20 @@ function registerTreeTools(mcp, server) {
|
|
|
19894
19969
|
type: zod.z.string().optional().describe("Page type: \"doc\", \"kanban\", \"calendar\", \"table\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"dashboard\", \"mindmap\", \"graph\". Omit to inherit parent view."),
|
|
19895
19970
|
meta: zod.z.record(zod.z.unknown()).optional().describe("Initial metadata (PageMeta fields: color as hex string, icon as Lucide kebab-case name like \"star\"/\"code-2\"/\"users\" — never emoji, dateStart, dateEnd, priority 0-4, tags array, etc). Omit icon entirely to use page type default.")
|
|
19896
19971
|
}, async ({ parentId, label, type, meta }) => {
|
|
19972
|
+
server.setAutoStatus("creating");
|
|
19973
|
+
server.setActiveToolCall({
|
|
19974
|
+
name: "create_document",
|
|
19975
|
+
target: label
|
|
19976
|
+
});
|
|
19897
19977
|
const treeMap = server.getTreeMap();
|
|
19898
19978
|
const rootDoc = server.rootDocument;
|
|
19899
|
-
if (!treeMap || !rootDoc)
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19979
|
+
if (!treeMap || !rootDoc) {
|
|
19980
|
+
server.setActiveToolCall(null);
|
|
19981
|
+
return { content: [{
|
|
19982
|
+
type: "text",
|
|
19983
|
+
text: "Not connected"
|
|
19984
|
+
}] };
|
|
19985
|
+
}
|
|
19903
19986
|
const id = crypto.randomUUID();
|
|
19904
19987
|
const normalizedParent = normalizeRootId(parentId, server);
|
|
19905
19988
|
const now = Date.now();
|
|
@@ -19914,6 +19997,7 @@ function registerTreeTools(mcp, server) {
|
|
|
19914
19997
|
updatedAt: now
|
|
19915
19998
|
});
|
|
19916
19999
|
});
|
|
20000
|
+
server.setActiveToolCall(null);
|
|
19917
20001
|
return { content: [{
|
|
19918
20002
|
type: "text",
|
|
19919
20003
|
text: JSON.stringify({
|
|
@@ -21151,6 +21235,11 @@ function yjsToMarkdown(fragment) {
|
|
|
21151
21235
|
function registerContentTools(mcp, server) {
|
|
21152
21236
|
mcp.tool("read_document", "Read a document's content as markdown, along with its immediate children. IMPORTANT: A document's full content is its body text PLUS all its children. An empty body does not mean empty content — the children ARE the content (sub-docs, kanban columns, table columns, calendar events, etc.). Always check the returned \"children\" array and read child documents too.", { docId: zod.z.string().describe("Document ID to read.") }, async ({ docId }) => {
|
|
21153
21237
|
try {
|
|
21238
|
+
server.setAutoStatus("reading", docId);
|
|
21239
|
+
server.setActiveToolCall({
|
|
21240
|
+
name: "read_document",
|
|
21241
|
+
target: docId
|
|
21242
|
+
});
|
|
21154
21243
|
const { title, markdown } = yjsToMarkdown((await server.getChildProvider(docId)).document.getXmlFragment("default"));
|
|
21155
21244
|
server.setFocusedDoc(docId);
|
|
21156
21245
|
server.setDocCursor(docId, 0);
|
|
@@ -21176,6 +21265,7 @@ function registerContentTools(mcp, server) {
|
|
|
21176
21265
|
});
|
|
21177
21266
|
children.sort((a, b) => (treeMap.get(a.id)?.order ?? 0) - (treeMap.get(b.id)?.order ?? 0));
|
|
21178
21267
|
}
|
|
21268
|
+
server.setActiveToolCall(null);
|
|
21179
21269
|
const result = {
|
|
21180
21270
|
label,
|
|
21181
21271
|
type,
|
|
@@ -21188,6 +21278,7 @@ function registerContentTools(mcp, server) {
|
|
|
21188
21278
|
text: JSON.stringify(result, null, 2)
|
|
21189
21279
|
}] };
|
|
21190
21280
|
} catch (error) {
|
|
21281
|
+
server.setActiveToolCall(null);
|
|
21191
21282
|
return {
|
|
21192
21283
|
content: [{
|
|
21193
21284
|
type: "text",
|
|
@@ -21203,6 +21294,11 @@ function registerContentTools(mcp, server) {
|
|
|
21203
21294
|
mode: zod.z.enum(["replace", "append"]).optional().describe("Write mode. \"replace\" clears existing content first (default). \"append\" adds to the end.")
|
|
21204
21295
|
}, async ({ docId, markdown, mode }) => {
|
|
21205
21296
|
try {
|
|
21297
|
+
server.setAutoStatus("writing", docId);
|
|
21298
|
+
server.setActiveToolCall({
|
|
21299
|
+
name: "write_document",
|
|
21300
|
+
target: docId
|
|
21301
|
+
});
|
|
21206
21302
|
const writeMode = mode ?? "replace";
|
|
21207
21303
|
const doc = (await server.getChildProvider(docId)).document;
|
|
21208
21304
|
const fragment = doc.getXmlFragment("default");
|
|
@@ -21232,11 +21328,13 @@ function registerContentTools(mcp, server) {
|
|
|
21232
21328
|
populateYDocFromMarkdown(fragment, body || markdown, title || "Untitled");
|
|
21233
21329
|
server.setFocusedDoc(docId);
|
|
21234
21330
|
server.setDocCursor(docId, fragment.length);
|
|
21331
|
+
server.setActiveToolCall(null);
|
|
21235
21332
|
return { content: [{
|
|
21236
21333
|
type: "text",
|
|
21237
21334
|
text: `Document ${docId} updated (${writeMode} mode)`
|
|
21238
21335
|
}] };
|
|
21239
21336
|
} catch (error) {
|
|
21337
|
+
server.setActiveToolCall(null);
|
|
21240
21338
|
return {
|
|
21241
21339
|
content: [{
|
|
21242
21340
|
type: "text",
|
|
@@ -21519,15 +21617,23 @@ function registerChannelTools(mcp, server) {
|
|
|
21519
21617
|
task_id: zod.z.string().optional().describe("If replying to an ai:task, the task ID to clear from awareness.")
|
|
21520
21618
|
}, async ({ doc_id, text, task_id }) => {
|
|
21521
21619
|
try {
|
|
21620
|
+
server.setAutoStatus("writing", doc_id);
|
|
21621
|
+
server.setActiveToolCall({
|
|
21622
|
+
name: "reply",
|
|
21623
|
+
target: doc_id
|
|
21624
|
+
});
|
|
21522
21625
|
const treeMap = server.getTreeMap();
|
|
21523
21626
|
const rootDoc = server.rootDocument;
|
|
21524
|
-
if (!treeMap || !rootDoc)
|
|
21525
|
-
|
|
21526
|
-
|
|
21527
|
-
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
|
|
21627
|
+
if (!treeMap || !rootDoc) {
|
|
21628
|
+
server.setActiveToolCall(null);
|
|
21629
|
+
return {
|
|
21630
|
+
content: [{
|
|
21631
|
+
type: "text",
|
|
21632
|
+
text: "Not connected"
|
|
21633
|
+
}],
|
|
21634
|
+
isError: true
|
|
21635
|
+
};
|
|
21636
|
+
}
|
|
21531
21637
|
const label = `AI Reply — ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)}: ${text.slice(0, 40).replace(/\n/g, " ")}`;
|
|
21532
21638
|
const replyId = crypto.randomUUID();
|
|
21533
21639
|
const now = Date.now();
|
|
@@ -21543,6 +21649,7 @@ function registerChannelTools(mcp, server) {
|
|
|
21543
21649
|
});
|
|
21544
21650
|
populateYDocFromMarkdown((await server.getChildProvider(replyId)).document, text);
|
|
21545
21651
|
if (task_id) server.clearAiTask(task_id);
|
|
21652
|
+
server.setActiveToolCall(null);
|
|
21546
21653
|
return { content: [{
|
|
21547
21654
|
type: "text",
|
|
21548
21655
|
text: JSON.stringify({
|
|
@@ -21551,6 +21658,7 @@ function registerChannelTools(mcp, server) {
|
|
|
21551
21658
|
})
|
|
21552
21659
|
}] };
|
|
21553
21660
|
} catch (error) {
|
|
21661
|
+
server.setActiveToolCall(null);
|
|
21554
21662
|
return {
|
|
21555
21663
|
content: [{
|
|
21556
21664
|
type: "text",
|
|
@@ -21579,6 +21687,8 @@ function registerChannelTools(mcp, server) {
|
|
|
21579
21687
|
content: text,
|
|
21580
21688
|
sender_name: server.agentName
|
|
21581
21689
|
}));
|
|
21690
|
+
server.setAutoStatus(null);
|
|
21691
|
+
server.setActiveToolCall(null);
|
|
21582
21692
|
return { content: [{
|
|
21583
21693
|
type: "text",
|
|
21584
21694
|
text: `Sent to ${channel}`
|