@abraca/mcp 1.0.18 → 1.0.21
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 +157 -64
- package/dist/abracadabra-mcp.cjs.map +1 -1
- package/dist/abracadabra-mcp.esm.js +157 -64
- package/dist/abracadabra-mcp.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +9 -8
- package/src/server.ts +2 -0
- package/src/tools/meta.ts +22 -4
- package/src/tools/tree.ts +61 -10
package/dist/abracadabra-mcp.cjs
CHANGED
|
@@ -19690,6 +19690,7 @@ var AbracadabraMCPServer = class {
|
|
|
19690
19690
|
method: "notifications/claude/channel",
|
|
19691
19691
|
params: {
|
|
19692
19692
|
content: task.text,
|
|
19693
|
+
instructions: `You MUST use the reply tool with doc_id="${task.docId ?? ""}" and task_id="${task.id}" for your final response. The user CANNOT see plain text output; they only see replies sent via MCP tools. For progress updates during multi-step work, use send_chat_message with channel="group:${task.docId ?? ""}" (e.g. "Looking into that..." or "Found it, writing up results..."). Never output plain text as a substitute for MCP tools.`,
|
|
19693
19694
|
meta: {
|
|
19694
19695
|
source: "abracadabra",
|
|
19695
19696
|
type: "ai_task",
|
|
@@ -19752,6 +19753,7 @@ var AbracadabraMCPServer = class {
|
|
|
19752
19753
|
method: "notifications/claude/channel",
|
|
19753
19754
|
params: {
|
|
19754
19755
|
content: data.content ?? "",
|
|
19756
|
+
instructions: `You MUST use send_chat_message with channel="${channel ?? ""}" for ALL responses — both progress updates and final answers. The user CANNOT see plain text output; they only see messages sent via send_chat_message. When doing multi-step work, send brief status updates via send_chat_message (e.g. "Looking into that..." or "Found it, writing up results...") so the user knows you're working. Never output plain text as a substitute for send_chat_message.`,
|
|
19755
19757
|
meta: {
|
|
19756
19758
|
source: "abracadabra",
|
|
19757
19759
|
type: "chat_message",
|
|
@@ -19908,13 +19910,19 @@ function buildTree$1(entries, rootId, maxDepth, currentDepth = 0, visited = /* @
|
|
|
19908
19910
|
}
|
|
19909
19911
|
function registerTreeTools(mcp, server) {
|
|
19910
19912
|
mcp.tool("list_documents", "List direct children of a document (defaults to root). Returns id, label, type, meta, order. NOTE: Only returns ONE level. Use find_document to search by name across the full tree, or get_document_tree to see the complete hierarchy.", { parentId: zod.z.string().optional().describe("Parent document ID. Omit for root-level documents.") }, async ({ parentId }) => {
|
|
19913
|
+
server.setAutoStatus("reading");
|
|
19914
|
+
server.setActiveToolCall({ name: "list_documents" });
|
|
19911
19915
|
const treeMap = server.getTreeMap();
|
|
19912
|
-
if (!treeMap)
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
+
if (!treeMap) {
|
|
19917
|
+
server.setActiveToolCall(null);
|
|
19918
|
+
return { content: [{
|
|
19919
|
+
type: "text",
|
|
19920
|
+
text: "Not connected"
|
|
19921
|
+
}] };
|
|
19922
|
+
}
|
|
19916
19923
|
const targetId = normalizeRootId(parentId, server);
|
|
19917
19924
|
const children = childrenOf$1(readEntries$1(treeMap), targetId);
|
|
19925
|
+
server.setActiveToolCall(null);
|
|
19918
19926
|
return { content: [{
|
|
19919
19927
|
type: "text",
|
|
19920
19928
|
text: JSON.stringify(children, null, 2)
|
|
@@ -19924,14 +19932,20 @@ function registerTreeTools(mcp, server) {
|
|
|
19924
19932
|
rootId: zod.z.string().optional().describe("Root document ID to start from. Omit for the entire tree."),
|
|
19925
19933
|
depth: zod.z.number().optional().describe("Maximum depth to traverse. Default 3. Use -1 for unlimited.")
|
|
19926
19934
|
}, async ({ rootId, depth }) => {
|
|
19935
|
+
server.setAutoStatus("reading");
|
|
19936
|
+
server.setActiveToolCall({ name: "get_document_tree" });
|
|
19927
19937
|
const treeMap = server.getTreeMap();
|
|
19928
|
-
if (!treeMap)
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19938
|
+
if (!treeMap) {
|
|
19939
|
+
server.setActiveToolCall(null);
|
|
19940
|
+
return { content: [{
|
|
19941
|
+
type: "text",
|
|
19942
|
+
text: "Not connected"
|
|
19943
|
+
}] };
|
|
19944
|
+
}
|
|
19932
19945
|
const targetId = normalizeRootId(rootId, server);
|
|
19933
19946
|
const maxDepth = depth ?? 3;
|
|
19934
19947
|
const tree = buildTree$1(readEntries$1(treeMap), targetId, maxDepth);
|
|
19948
|
+
server.setActiveToolCall(null);
|
|
19935
19949
|
return { content: [{
|
|
19936
19950
|
type: "text",
|
|
19937
19951
|
text: JSON.stringify(tree, null, 2)
|
|
@@ -19941,11 +19955,19 @@ function registerTreeTools(mcp, server) {
|
|
|
19941
19955
|
query: zod.z.string().describe("Search query — matched case-insensitively against document labels (substring match)."),
|
|
19942
19956
|
rootId: zod.z.string().optional().describe("Restrict search to descendants of this document. Omit to search the entire tree.")
|
|
19943
19957
|
}, async ({ query, rootId }) => {
|
|
19958
|
+
server.setAutoStatus("searching");
|
|
19959
|
+
server.setActiveToolCall({
|
|
19960
|
+
name: "find_document",
|
|
19961
|
+
target: query
|
|
19962
|
+
});
|
|
19944
19963
|
const treeMap = server.getTreeMap();
|
|
19945
|
-
if (!treeMap)
|
|
19946
|
-
|
|
19947
|
-
|
|
19948
|
-
|
|
19964
|
+
if (!treeMap) {
|
|
19965
|
+
server.setActiveToolCall(null);
|
|
19966
|
+
return { content: [{
|
|
19967
|
+
type: "text",
|
|
19968
|
+
text: "Not connected"
|
|
19969
|
+
}] };
|
|
19970
|
+
}
|
|
19949
19971
|
const entries = readEntries$1(treeMap);
|
|
19950
19972
|
const lowerQuery = query.toLowerCase();
|
|
19951
19973
|
const normalizedRoot = normalizeRootId(rootId, server);
|
|
@@ -19970,6 +19992,7 @@ function registerTreeTools(mcp, server) {
|
|
|
19970
19992
|
path
|
|
19971
19993
|
};
|
|
19972
19994
|
});
|
|
19995
|
+
server.setActiveToolCall(null);
|
|
19973
19996
|
if (results.length === 0) return { content: [{
|
|
19974
19997
|
type: "text",
|
|
19975
19998
|
text: `No documents found matching "${query}". Try get_document_tree to see the full hierarchy.`
|
|
@@ -20029,21 +20052,33 @@ function registerTreeTools(mcp, server) {
|
|
|
20029
20052
|
id: zod.z.string().describe("Document ID to rename."),
|
|
20030
20053
|
label: zod.z.string().describe("New display name.")
|
|
20031
20054
|
}, async ({ id, label }) => {
|
|
20055
|
+
server.setAutoStatus("writing");
|
|
20056
|
+
server.setActiveToolCall({
|
|
20057
|
+
name: "rename_document",
|
|
20058
|
+
target: id
|
|
20059
|
+
});
|
|
20032
20060
|
const treeMap = server.getTreeMap();
|
|
20033
|
-
if (!treeMap)
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20061
|
+
if (!treeMap) {
|
|
20062
|
+
server.setActiveToolCall(null);
|
|
20063
|
+
return { content: [{
|
|
20064
|
+
type: "text",
|
|
20065
|
+
text: "Not connected"
|
|
20066
|
+
}] };
|
|
20067
|
+
}
|
|
20037
20068
|
const entry = treeMap.get(id);
|
|
20038
|
-
if (!entry)
|
|
20039
|
-
|
|
20040
|
-
|
|
20041
|
-
|
|
20069
|
+
if (!entry) {
|
|
20070
|
+
server.setActiveToolCall(null);
|
|
20071
|
+
return { content: [{
|
|
20072
|
+
type: "text",
|
|
20073
|
+
text: `Document ${id} not found`
|
|
20074
|
+
}] };
|
|
20075
|
+
}
|
|
20042
20076
|
treeMap.set(id, {
|
|
20043
20077
|
...entry,
|
|
20044
20078
|
label,
|
|
20045
20079
|
updatedAt: Date.now()
|
|
20046
20080
|
});
|
|
20081
|
+
server.setActiveToolCall(null);
|
|
20047
20082
|
return { content: [{
|
|
20048
20083
|
type: "text",
|
|
20049
20084
|
text: `Renamed to "${label}"`
|
|
@@ -20054,35 +20089,55 @@ function registerTreeTools(mcp, server) {
|
|
|
20054
20089
|
newParentId: zod.z.string().optional().describe("New parent document ID. Omit to move to top level."),
|
|
20055
20090
|
order: zod.z.number().optional().describe("New sort order. Defaults to Date.now() (append to end).")
|
|
20056
20091
|
}, async ({ id, newParentId, order }) => {
|
|
20092
|
+
server.setAutoStatus("writing");
|
|
20093
|
+
server.setActiveToolCall({
|
|
20094
|
+
name: "move_document",
|
|
20095
|
+
target: id
|
|
20096
|
+
});
|
|
20057
20097
|
const treeMap = server.getTreeMap();
|
|
20058
|
-
if (!treeMap)
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20098
|
+
if (!treeMap) {
|
|
20099
|
+
server.setActiveToolCall(null);
|
|
20100
|
+
return { content: [{
|
|
20101
|
+
type: "text",
|
|
20102
|
+
text: "Not connected"
|
|
20103
|
+
}] };
|
|
20104
|
+
}
|
|
20062
20105
|
const entry = treeMap.get(id);
|
|
20063
|
-
if (!entry)
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20106
|
+
if (!entry) {
|
|
20107
|
+
server.setActiveToolCall(null);
|
|
20108
|
+
return { content: [{
|
|
20109
|
+
type: "text",
|
|
20110
|
+
text: `Document ${id} not found`
|
|
20111
|
+
}] };
|
|
20112
|
+
}
|
|
20067
20113
|
treeMap.set(id, {
|
|
20068
20114
|
...entry,
|
|
20069
20115
|
parentId: normalizeRootId(newParentId, server),
|
|
20070
20116
|
order: order ?? Date.now(),
|
|
20071
20117
|
updatedAt: Date.now()
|
|
20072
20118
|
});
|
|
20119
|
+
server.setActiveToolCall(null);
|
|
20073
20120
|
return { content: [{
|
|
20074
20121
|
type: "text",
|
|
20075
20122
|
text: `Moved ${id} to parent ${newParentId}`
|
|
20076
20123
|
}] };
|
|
20077
20124
|
});
|
|
20078
20125
|
mcp.tool("delete_document", "Soft-delete a document and its descendants (moves to trash).", { id: zod.z.string().describe("Document ID to delete.") }, async ({ id }) => {
|
|
20126
|
+
server.setAutoStatus("writing");
|
|
20127
|
+
server.setActiveToolCall({
|
|
20128
|
+
name: "delete_document",
|
|
20129
|
+
target: id
|
|
20130
|
+
});
|
|
20079
20131
|
const treeMap = server.getTreeMap();
|
|
20080
20132
|
const trashMap = server.getTrashMap();
|
|
20081
20133
|
const rootDoc = server.rootDocument;
|
|
20082
|
-
if (!treeMap || !trashMap || !rootDoc)
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20134
|
+
if (!treeMap || !trashMap || !rootDoc) {
|
|
20135
|
+
server.setActiveToolCall(null);
|
|
20136
|
+
return { content: [{
|
|
20137
|
+
type: "text",
|
|
20138
|
+
text: "Not connected"
|
|
20139
|
+
}] };
|
|
20140
|
+
}
|
|
20086
20141
|
const toDelete = [id, ...descendantsOf(readEntries$1(treeMap), id).map((e) => e.id)];
|
|
20087
20142
|
const now = Date.now();
|
|
20088
20143
|
rootDoc.transact(() => {
|
|
@@ -20100,6 +20155,7 @@ function registerTreeTools(mcp, server) {
|
|
|
20100
20155
|
treeMap.delete(nid);
|
|
20101
20156
|
}
|
|
20102
20157
|
});
|
|
20158
|
+
server.setActiveToolCall(null);
|
|
20103
20159
|
return { content: [{
|
|
20104
20160
|
type: "text",
|
|
20105
20161
|
text: `Deleted ${toDelete.length} document(s)`
|
|
@@ -20109,21 +20165,33 @@ function registerTreeTools(mcp, server) {
|
|
|
20109
20165
|
id: zod.z.string().describe("Document ID."),
|
|
20110
20166
|
type: zod.z.string().describe("New page type (e.g. \"doc\", \"kanban\", \"table\", \"calendar\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"dashboard\", \"mindmap\", \"graph\").")
|
|
20111
20167
|
}, async ({ id, type }) => {
|
|
20168
|
+
server.setAutoStatus("writing");
|
|
20169
|
+
server.setActiveToolCall({
|
|
20170
|
+
name: "change_document_type",
|
|
20171
|
+
target: id
|
|
20172
|
+
});
|
|
20112
20173
|
const treeMap = server.getTreeMap();
|
|
20113
|
-
if (!treeMap)
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20174
|
+
if (!treeMap) {
|
|
20175
|
+
server.setActiveToolCall(null);
|
|
20176
|
+
return { content: [{
|
|
20177
|
+
type: "text",
|
|
20178
|
+
text: "Not connected"
|
|
20179
|
+
}] };
|
|
20180
|
+
}
|
|
20117
20181
|
const entry = treeMap.get(id);
|
|
20118
|
-
if (!entry)
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20182
|
+
if (!entry) {
|
|
20183
|
+
server.setActiveToolCall(null);
|
|
20184
|
+
return { content: [{
|
|
20185
|
+
type: "text",
|
|
20186
|
+
text: `Document ${id} not found`
|
|
20187
|
+
}] };
|
|
20188
|
+
}
|
|
20122
20189
|
treeMap.set(id, {
|
|
20123
20190
|
...entry,
|
|
20124
20191
|
type,
|
|
20125
20192
|
updatedAt: Date.now()
|
|
20126
20193
|
});
|
|
20194
|
+
server.setActiveToolCall(null);
|
|
20127
20195
|
return { content: [{
|
|
20128
20196
|
type: "text",
|
|
20129
20197
|
text: `Changed type to "${type}"`
|
|
@@ -21423,16 +21491,28 @@ function registerContentTools(mcp, server) {
|
|
|
21423
21491
|
//#region packages/mcp/src/tools/meta.ts
|
|
21424
21492
|
function registerMetaTools(mcp, server) {
|
|
21425
21493
|
mcp.tool("get_metadata", "Read the metadata (PageMeta) of a document from the tree.", { docId: zod.z.string().describe("Document ID.") }, async ({ docId }) => {
|
|
21494
|
+
server.setAutoStatus("reading", docId);
|
|
21495
|
+
server.setActiveToolCall({
|
|
21496
|
+
name: "get_metadata",
|
|
21497
|
+
target: docId
|
|
21498
|
+
});
|
|
21426
21499
|
const treeMap = server.getTreeMap();
|
|
21427
|
-
if (!treeMap)
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21500
|
+
if (!treeMap) {
|
|
21501
|
+
server.setActiveToolCall(null);
|
|
21502
|
+
return { content: [{
|
|
21503
|
+
type: "text",
|
|
21504
|
+
text: "Not connected"
|
|
21505
|
+
}] };
|
|
21506
|
+
}
|
|
21431
21507
|
const entry = treeMap.get(docId);
|
|
21432
|
-
if (!entry)
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21508
|
+
if (!entry) {
|
|
21509
|
+
server.setActiveToolCall(null);
|
|
21510
|
+
return { content: [{
|
|
21511
|
+
type: "text",
|
|
21512
|
+
text: `Document ${docId} not found`
|
|
21513
|
+
}] };
|
|
21514
|
+
}
|
|
21515
|
+
server.setActiveToolCall(null);
|
|
21436
21516
|
return { content: [{
|
|
21437
21517
|
type: "text",
|
|
21438
21518
|
text: JSON.stringify({
|
|
@@ -21447,16 +21527,27 @@ function registerMetaTools(mcp, server) {
|
|
|
21447
21527
|
docId: zod.z.string().describe("Document ID."),
|
|
21448
21528
|
meta: zod.z.record(zod.z.unknown()).describe("Metadata fields to update (merged with existing). Universal keys: color (hex), icon (Lucide kebab-case — NEVER emoji), dateStart/dateEnd, datetimeStart/datetimeEnd, allDay, tags (string[]), checked (bool), priority (0=none,1=low,2=med,3=high,4=urgent), status, rating (0-5), url, email, phone, number, unit, subtitle, note, taskProgress (0-100), members ({id,label}[]), coverUploadId. Geo/Map: geoType (\"marker\"|\"line\"|\"measure\"), geoLat, geoLng, geoDescription. Spatial 3D: spShape (\"box\"|\"sphere\"|\"cylinder\"|\"cone\"|\"plane\"|\"torus\"|\"glb\"), spX/spY/spZ, spRX/spRY/spRZ, spSX/spSY/spSZ, spColor, spOpacity (0-100). Dashboard: deskX, deskY, deskZ, deskMode (\"icon\"|\"widget-sm\"|\"widget-lg\"). Renderer config (on the page doc itself): kanbanColumnWidth, galleryColumns, galleryAspect, calendarView, calendarWeekStart, tableMode, showRefEdges. Set a key to null to clear it.")
|
|
21449
21529
|
}, async ({ docId, meta }) => {
|
|
21530
|
+
server.setAutoStatus("writing", docId);
|
|
21531
|
+
server.setActiveToolCall({
|
|
21532
|
+
name: "update_metadata",
|
|
21533
|
+
target: docId
|
|
21534
|
+
});
|
|
21450
21535
|
const treeMap = server.getTreeMap();
|
|
21451
|
-
if (!treeMap)
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21536
|
+
if (!treeMap) {
|
|
21537
|
+
server.setActiveToolCall(null);
|
|
21538
|
+
return { content: [{
|
|
21539
|
+
type: "text",
|
|
21540
|
+
text: "Not connected"
|
|
21541
|
+
}] };
|
|
21542
|
+
}
|
|
21455
21543
|
const entry = treeMap.get(docId);
|
|
21456
|
-
if (!entry)
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21544
|
+
if (!entry) {
|
|
21545
|
+
server.setActiveToolCall(null);
|
|
21546
|
+
return { content: [{
|
|
21547
|
+
type: "text",
|
|
21548
|
+
text: `Document ${docId} not found`
|
|
21549
|
+
}] };
|
|
21550
|
+
}
|
|
21460
21551
|
treeMap.set(docId, {
|
|
21461
21552
|
...entry,
|
|
21462
21553
|
meta: {
|
|
@@ -21465,6 +21556,7 @@ function registerMetaTools(mcp, server) {
|
|
|
21465
21556
|
},
|
|
21466
21557
|
updatedAt: Date.now()
|
|
21467
21558
|
});
|
|
21559
|
+
server.setActiveToolCall(null);
|
|
21468
21560
|
return { content: [{
|
|
21469
21561
|
type: "text",
|
|
21470
21562
|
text: `Metadata updated for ${docId}`
|
|
@@ -22530,6 +22622,13 @@ async function main() {
|
|
|
22530
22622
|
capabilities: { experimental: { "claude/channel": {} } },
|
|
22531
22623
|
instructions: `Abracadabra is a CRDT collaboration platform where everything is a document in a tree.
|
|
22532
22624
|
|
|
22625
|
+
## CRITICAL: Responding to Channel Events
|
|
22626
|
+
The user CANNOT see your plain text output — they only see messages sent via MCP tools. When you receive a channel event:
|
|
22627
|
+
- **Chat messages**: Use send_chat_message with the SAME channel for ALL replies AND progress updates.
|
|
22628
|
+
- **AI tasks**: Use reply tool for the final response; use send_chat_message for progress updates.
|
|
22629
|
+
- For multi-step work, send brief status updates via send_chat_message so the user sees progress.
|
|
22630
|
+
- NEVER output plain text as a response or status update — it is invisible to the user.
|
|
22631
|
+
|
|
22533
22632
|
## Quick Start Workflow
|
|
22534
22633
|
1. list_spaces → note the active space's doc_id (this is the hub/root document)
|
|
22535
22634
|
2. get_document_tree(rootId: hubDocId) → see the FULL hierarchy before doing anything
|
|
@@ -22539,12 +22638,12 @@ async function main() {
|
|
|
22539
22638
|
## Key Concepts
|
|
22540
22639
|
- Documents form a tree. A kanban board's columns are child documents; cards are grandchildren.
|
|
22541
22640
|
- A document's label IS its display name everywhere. Children ARE the content (not just the body text).
|
|
22542
|
-
- Page types
|
|
22641
|
+
- Page types are views over the SAME tree — switching types preserves data.
|
|
22543
22642
|
- An empty markdown body does NOT mean empty content — always check the children array.
|
|
22544
22643
|
- Use ![[docId]] in content to embed another document, or [[docId|label]] for inline links.
|
|
22545
22644
|
|
|
22546
22645
|
## Finding Documents
|
|
22547
|
-
- list_documents only shows ONE level of children.
|
|
22646
|
+
- list_documents only shows ONE level of children. Use find_document to search by name, or get_document_tree to see the full hierarchy.
|
|
22548
22647
|
- NEVER conclude a document doesn't exist after only checking root-level documents.
|
|
22549
22648
|
|
|
22550
22649
|
## Rules
|
|
@@ -22554,12 +22653,6 @@ async function main() {
|
|
|
22554
22653
|
- Never rename or write content to the hub document itself.
|
|
22555
22654
|
- Use universal meta keys (color, icon, dateStart) — never prefix with page type names.
|
|
22556
22655
|
|
|
22557
|
-
## Channel Events
|
|
22558
|
-
Events from the abracadabra channel arrive as <channel source="abracadabra" ...>. They may be:
|
|
22559
|
-
- ai:task events from human users (includes sender, doc_id context)
|
|
22560
|
-
- chat messages from the platform chat system (includes channel, sender, sender_id)
|
|
22561
|
-
When you receive a channel event, read it, do the work using your tools, and reply using the reply tool (for document responses) or send_chat_message (for chat responses).
|
|
22562
|
-
|
|
22563
22656
|
## Full Reference
|
|
22564
22657
|
Read the resource at abracadabra://agent-guide for the complete guide covering page type schemas, metadata reference, awareness/presence, content structure, and detailed examples.`
|
|
22565
22658
|
});
|