@abraca/mcp 1.0.19 → 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.
@@ -19686,6 +19686,7 @@ var AbracadabraMCPServer = class {
19686
19686
  method: "notifications/claude/channel",
19687
19687
  params: {
19688
19688
  content: task.text,
19689
+ 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.`,
19689
19690
  meta: {
19690
19691
  source: "abracadabra",
19691
19692
  type: "ai_task",
@@ -19748,6 +19749,7 @@ var AbracadabraMCPServer = class {
19748
19749
  method: "notifications/claude/channel",
19749
19750
  params: {
19750
19751
  content: data.content ?? "",
19752
+ 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.`,
19751
19753
  meta: {
19752
19754
  source: "abracadabra",
19753
19755
  type: "chat_message",
@@ -19904,13 +19906,19 @@ function buildTree$1(entries, rootId, maxDepth, currentDepth = 0, visited = /* @
19904
19906
  }
19905
19907
  function registerTreeTools(mcp, server) {
19906
19908
  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: z.string().optional().describe("Parent document ID. Omit for root-level documents.") }, async ({ parentId }) => {
19909
+ server.setAutoStatus("reading");
19910
+ server.setActiveToolCall({ name: "list_documents" });
19907
19911
  const treeMap = server.getTreeMap();
19908
- if (!treeMap) return { content: [{
19909
- type: "text",
19910
- text: "Not connected"
19911
- }] };
19912
+ if (!treeMap) {
19913
+ server.setActiveToolCall(null);
19914
+ return { content: [{
19915
+ type: "text",
19916
+ text: "Not connected"
19917
+ }] };
19918
+ }
19912
19919
  const targetId = normalizeRootId(parentId, server);
19913
19920
  const children = childrenOf$1(readEntries$1(treeMap), targetId);
19921
+ server.setActiveToolCall(null);
19914
19922
  return { content: [{
19915
19923
  type: "text",
19916
19924
  text: JSON.stringify(children, null, 2)
@@ -19920,14 +19928,20 @@ function registerTreeTools(mcp, server) {
19920
19928
  rootId: z.string().optional().describe("Root document ID to start from. Omit for the entire tree."),
19921
19929
  depth: z.number().optional().describe("Maximum depth to traverse. Default 3. Use -1 for unlimited.")
19922
19930
  }, async ({ rootId, depth }) => {
19931
+ server.setAutoStatus("reading");
19932
+ server.setActiveToolCall({ name: "get_document_tree" });
19923
19933
  const treeMap = server.getTreeMap();
19924
- if (!treeMap) return { content: [{
19925
- type: "text",
19926
- text: "Not connected"
19927
- }] };
19934
+ if (!treeMap) {
19935
+ server.setActiveToolCall(null);
19936
+ return { content: [{
19937
+ type: "text",
19938
+ text: "Not connected"
19939
+ }] };
19940
+ }
19928
19941
  const targetId = normalizeRootId(rootId, server);
19929
19942
  const maxDepth = depth ?? 3;
19930
19943
  const tree = buildTree$1(readEntries$1(treeMap), targetId, maxDepth);
19944
+ server.setActiveToolCall(null);
19931
19945
  return { content: [{
19932
19946
  type: "text",
19933
19947
  text: JSON.stringify(tree, null, 2)
@@ -19937,11 +19951,19 @@ function registerTreeTools(mcp, server) {
19937
19951
  query: z.string().describe("Search query — matched case-insensitively against document labels (substring match)."),
19938
19952
  rootId: z.string().optional().describe("Restrict search to descendants of this document. Omit to search the entire tree.")
19939
19953
  }, async ({ query, rootId }) => {
19954
+ server.setAutoStatus("searching");
19955
+ server.setActiveToolCall({
19956
+ name: "find_document",
19957
+ target: query
19958
+ });
19940
19959
  const treeMap = server.getTreeMap();
19941
- if (!treeMap) return { content: [{
19942
- type: "text",
19943
- text: "Not connected"
19944
- }] };
19960
+ if (!treeMap) {
19961
+ server.setActiveToolCall(null);
19962
+ return { content: [{
19963
+ type: "text",
19964
+ text: "Not connected"
19965
+ }] };
19966
+ }
19945
19967
  const entries = readEntries$1(treeMap);
19946
19968
  const lowerQuery = query.toLowerCase();
19947
19969
  const normalizedRoot = normalizeRootId(rootId, server);
@@ -19966,6 +19988,7 @@ function registerTreeTools(mcp, server) {
19966
19988
  path
19967
19989
  };
19968
19990
  });
19991
+ server.setActiveToolCall(null);
19969
19992
  if (results.length === 0) return { content: [{
19970
19993
  type: "text",
19971
19994
  text: `No documents found matching "${query}". Try get_document_tree to see the full hierarchy.`
@@ -20025,21 +20048,33 @@ function registerTreeTools(mcp, server) {
20025
20048
  id: z.string().describe("Document ID to rename."),
20026
20049
  label: z.string().describe("New display name.")
20027
20050
  }, async ({ id, label }) => {
20051
+ server.setAutoStatus("writing");
20052
+ server.setActiveToolCall({
20053
+ name: "rename_document",
20054
+ target: id
20055
+ });
20028
20056
  const treeMap = server.getTreeMap();
20029
- if (!treeMap) return { content: [{
20030
- type: "text",
20031
- text: "Not connected"
20032
- }] };
20057
+ if (!treeMap) {
20058
+ server.setActiveToolCall(null);
20059
+ return { content: [{
20060
+ type: "text",
20061
+ text: "Not connected"
20062
+ }] };
20063
+ }
20033
20064
  const entry = treeMap.get(id);
20034
- if (!entry) return { content: [{
20035
- type: "text",
20036
- text: `Document ${id} not found`
20037
- }] };
20065
+ if (!entry) {
20066
+ server.setActiveToolCall(null);
20067
+ return { content: [{
20068
+ type: "text",
20069
+ text: `Document ${id} not found`
20070
+ }] };
20071
+ }
20038
20072
  treeMap.set(id, {
20039
20073
  ...entry,
20040
20074
  label,
20041
20075
  updatedAt: Date.now()
20042
20076
  });
20077
+ server.setActiveToolCall(null);
20043
20078
  return { content: [{
20044
20079
  type: "text",
20045
20080
  text: `Renamed to "${label}"`
@@ -20050,35 +20085,55 @@ function registerTreeTools(mcp, server) {
20050
20085
  newParentId: z.string().optional().describe("New parent document ID. Omit to move to top level."),
20051
20086
  order: z.number().optional().describe("New sort order. Defaults to Date.now() (append to end).")
20052
20087
  }, async ({ id, newParentId, order }) => {
20088
+ server.setAutoStatus("writing");
20089
+ server.setActiveToolCall({
20090
+ name: "move_document",
20091
+ target: id
20092
+ });
20053
20093
  const treeMap = server.getTreeMap();
20054
- if (!treeMap) return { content: [{
20055
- type: "text",
20056
- text: "Not connected"
20057
- }] };
20094
+ if (!treeMap) {
20095
+ server.setActiveToolCall(null);
20096
+ return { content: [{
20097
+ type: "text",
20098
+ text: "Not connected"
20099
+ }] };
20100
+ }
20058
20101
  const entry = treeMap.get(id);
20059
- if (!entry) return { content: [{
20060
- type: "text",
20061
- text: `Document ${id} not found`
20062
- }] };
20102
+ if (!entry) {
20103
+ server.setActiveToolCall(null);
20104
+ return { content: [{
20105
+ type: "text",
20106
+ text: `Document ${id} not found`
20107
+ }] };
20108
+ }
20063
20109
  treeMap.set(id, {
20064
20110
  ...entry,
20065
20111
  parentId: normalizeRootId(newParentId, server),
20066
20112
  order: order ?? Date.now(),
20067
20113
  updatedAt: Date.now()
20068
20114
  });
20115
+ server.setActiveToolCall(null);
20069
20116
  return { content: [{
20070
20117
  type: "text",
20071
20118
  text: `Moved ${id} to parent ${newParentId}`
20072
20119
  }] };
20073
20120
  });
20074
20121
  mcp.tool("delete_document", "Soft-delete a document and its descendants (moves to trash).", { id: z.string().describe("Document ID to delete.") }, async ({ id }) => {
20122
+ server.setAutoStatus("writing");
20123
+ server.setActiveToolCall({
20124
+ name: "delete_document",
20125
+ target: id
20126
+ });
20075
20127
  const treeMap = server.getTreeMap();
20076
20128
  const trashMap = server.getTrashMap();
20077
20129
  const rootDoc = server.rootDocument;
20078
- if (!treeMap || !trashMap || !rootDoc) return { content: [{
20079
- type: "text",
20080
- text: "Not connected"
20081
- }] };
20130
+ if (!treeMap || !trashMap || !rootDoc) {
20131
+ server.setActiveToolCall(null);
20132
+ return { content: [{
20133
+ type: "text",
20134
+ text: "Not connected"
20135
+ }] };
20136
+ }
20082
20137
  const toDelete = [id, ...descendantsOf(readEntries$1(treeMap), id).map((e) => e.id)];
20083
20138
  const now = Date.now();
20084
20139
  rootDoc.transact(() => {
@@ -20096,6 +20151,7 @@ function registerTreeTools(mcp, server) {
20096
20151
  treeMap.delete(nid);
20097
20152
  }
20098
20153
  });
20154
+ server.setActiveToolCall(null);
20099
20155
  return { content: [{
20100
20156
  type: "text",
20101
20157
  text: `Deleted ${toDelete.length} document(s)`
@@ -20105,21 +20161,33 @@ function registerTreeTools(mcp, server) {
20105
20161
  id: z.string().describe("Document ID."),
20106
20162
  type: z.string().describe("New page type (e.g. \"doc\", \"kanban\", \"table\", \"calendar\", \"outline\", \"gallery\", \"slides\", \"timeline\", \"whiteboard\", \"map\", \"dashboard\", \"mindmap\", \"graph\").")
20107
20163
  }, async ({ id, type }) => {
20164
+ server.setAutoStatus("writing");
20165
+ server.setActiveToolCall({
20166
+ name: "change_document_type",
20167
+ target: id
20168
+ });
20108
20169
  const treeMap = server.getTreeMap();
20109
- if (!treeMap) return { content: [{
20110
- type: "text",
20111
- text: "Not connected"
20112
- }] };
20170
+ if (!treeMap) {
20171
+ server.setActiveToolCall(null);
20172
+ return { content: [{
20173
+ type: "text",
20174
+ text: "Not connected"
20175
+ }] };
20176
+ }
20113
20177
  const entry = treeMap.get(id);
20114
- if (!entry) return { content: [{
20115
- type: "text",
20116
- text: `Document ${id} not found`
20117
- }] };
20178
+ if (!entry) {
20179
+ server.setActiveToolCall(null);
20180
+ return { content: [{
20181
+ type: "text",
20182
+ text: `Document ${id} not found`
20183
+ }] };
20184
+ }
20118
20185
  treeMap.set(id, {
20119
20186
  ...entry,
20120
20187
  type,
20121
20188
  updatedAt: Date.now()
20122
20189
  });
20190
+ server.setActiveToolCall(null);
20123
20191
  return { content: [{
20124
20192
  type: "text",
20125
20193
  text: `Changed type to "${type}"`
@@ -21416,16 +21484,28 @@ function registerContentTools(mcp, server) {
21416
21484
  //#region packages/mcp/src/tools/meta.ts
21417
21485
  function registerMetaTools(mcp, server) {
21418
21486
  mcp.tool("get_metadata", "Read the metadata (PageMeta) of a document from the tree.", { docId: z.string().describe("Document ID.") }, async ({ docId }) => {
21487
+ server.setAutoStatus("reading", docId);
21488
+ server.setActiveToolCall({
21489
+ name: "get_metadata",
21490
+ target: docId
21491
+ });
21419
21492
  const treeMap = server.getTreeMap();
21420
- if (!treeMap) return { content: [{
21421
- type: "text",
21422
- text: "Not connected"
21423
- }] };
21493
+ if (!treeMap) {
21494
+ server.setActiveToolCall(null);
21495
+ return { content: [{
21496
+ type: "text",
21497
+ text: "Not connected"
21498
+ }] };
21499
+ }
21424
21500
  const entry = treeMap.get(docId);
21425
- if (!entry) return { content: [{
21426
- type: "text",
21427
- text: `Document ${docId} not found`
21428
- }] };
21501
+ if (!entry) {
21502
+ server.setActiveToolCall(null);
21503
+ return { content: [{
21504
+ type: "text",
21505
+ text: `Document ${docId} not found`
21506
+ }] };
21507
+ }
21508
+ server.setActiveToolCall(null);
21429
21509
  return { content: [{
21430
21510
  type: "text",
21431
21511
  text: JSON.stringify({
@@ -21440,16 +21520,27 @@ function registerMetaTools(mcp, server) {
21440
21520
  docId: z.string().describe("Document ID."),
21441
21521
  meta: z.record(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.")
21442
21522
  }, async ({ docId, meta }) => {
21523
+ server.setAutoStatus("writing", docId);
21524
+ server.setActiveToolCall({
21525
+ name: "update_metadata",
21526
+ target: docId
21527
+ });
21443
21528
  const treeMap = server.getTreeMap();
21444
- if (!treeMap) return { content: [{
21445
- type: "text",
21446
- text: "Not connected"
21447
- }] };
21529
+ if (!treeMap) {
21530
+ server.setActiveToolCall(null);
21531
+ return { content: [{
21532
+ type: "text",
21533
+ text: "Not connected"
21534
+ }] };
21535
+ }
21448
21536
  const entry = treeMap.get(docId);
21449
- if (!entry) return { content: [{
21450
- type: "text",
21451
- text: `Document ${docId} not found`
21452
- }] };
21537
+ if (!entry) {
21538
+ server.setActiveToolCall(null);
21539
+ return { content: [{
21540
+ type: "text",
21541
+ text: `Document ${docId} not found`
21542
+ }] };
21543
+ }
21453
21544
  treeMap.set(docId, {
21454
21545
  ...entry,
21455
21546
  meta: {
@@ -21458,6 +21549,7 @@ function registerMetaTools(mcp, server) {
21458
21549
  },
21459
21550
  updatedAt: Date.now()
21460
21551
  });
21552
+ server.setActiveToolCall(null);
21461
21553
  return { content: [{
21462
21554
  type: "text",
21463
21555
  text: `Metadata updated for ${docId}`
@@ -22523,6 +22615,13 @@ async function main() {
22523
22615
  capabilities: { experimental: { "claude/channel": {} } },
22524
22616
  instructions: `Abracadabra is a CRDT collaboration platform where everything is a document in a tree.
22525
22617
 
22618
+ ## CRITICAL: Responding to Channel Events
22619
+ The user CANNOT see your plain text output — they only see messages sent via MCP tools. When you receive a channel event:
22620
+ - **Chat messages**: Use send_chat_message with the SAME channel for ALL replies AND progress updates.
22621
+ - **AI tasks**: Use reply tool for the final response; use send_chat_message for progress updates.
22622
+ - For multi-step work, send brief status updates via send_chat_message so the user sees progress.
22623
+ - NEVER output plain text as a response or status update — it is invisible to the user.
22624
+
22526
22625
  ## Quick Start Workflow
22527
22626
  1. list_spaces → note the active space's doc_id (this is the hub/root document)
22528
22627
  2. get_document_tree(rootId: hubDocId) → see the FULL hierarchy before doing anything
@@ -22532,12 +22631,12 @@ async function main() {
22532
22631
  ## Key Concepts
22533
22632
  - Documents form a tree. A kanban board's columns are child documents; cards are grandchildren.
22534
22633
  - A document's label IS its display name everywhere. Children ARE the content (not just the body text).
22535
- - Page types (doc, kanban, table, calendar, timeline, checklist, outline, gallery, map, graph, dashboard, spatial, media, mindmap, etc.) are views over the SAME tree — switching types preserves data.
22634
+ - Page types are views over the SAME tree — switching types preserves data.
22536
22635
  - An empty markdown body does NOT mean empty content — always check the children array.
22537
22636
  - Use ![[docId]] in content to embed another document, or [[docId|label]] for inline links.
22538
22637
 
22539
22638
  ## Finding Documents
22540
- - list_documents only shows ONE level of children. If you don't find what you need, use find_document to search the entire tree by name, or get_document_tree to see the full hierarchy.
22639
+ - list_documents only shows ONE level of children. Use find_document to search by name, or get_document_tree to see the full hierarchy.
22541
22640
  - NEVER conclude a document doesn't exist after only checking root-level documents.
22542
22641
 
22543
22642
  ## Rules
@@ -22547,12 +22646,6 @@ async function main() {
22547
22646
  - Never rename or write content to the hub document itself.
22548
22647
  - Use universal meta keys (color, icon, dateStart) — never prefix with page type names.
22549
22648
 
22550
- ## Channel Events
22551
- Events from the abracadabra channel arrive as <channel source="abracadabra" ...>. They may be:
22552
- - ai:task events from human users (includes sender, doc_id context)
22553
- - chat messages from the platform chat system (includes channel, sender, sender_id)
22554
- 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).
22555
-
22556
22649
  ## Full Reference
22557
22650
  Read the resource at abracadabra://agent-guide for the complete guide covering page type schemas, metadata reference, awareness/presence, content structure, and detailed examples.`
22558
22651
  });