@abraca/mcp 1.0.2 → 1.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "MCP server for Abracadabra — AI agent collaboration on CRDT documents",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/tools/tree.ts CHANGED
@@ -32,7 +32,10 @@ function childrenOf(entries: TreeEntry[], parentId: string | null): TreeEntry[]
32
32
 
33
33
  function descendantsOf(entries: TreeEntry[], id: string | null): TreeEntry[] {
34
34
  const result: TreeEntry[] = []
35
+ const visited = new Set<string>()
35
36
  function collect(pid: string) {
37
+ if (visited.has(pid)) return
38
+ visited.add(pid)
36
39
  for (const child of childrenOf(entries, pid)) {
37
40
  result.push(child)
38
41
  collect(child.id)
@@ -42,20 +45,37 @@ function descendantsOf(entries: TreeEntry[], id: string | null): TreeEntry[] {
42
45
  return result
43
46
  }
44
47
 
45
- function buildTree(entries: TreeEntry[], rootId: string | null, maxDepth: number, currentDepth = 0): any[] {
48
+ function buildTree(entries: TreeEntry[], rootId: string | null, maxDepth: number, currentDepth = 0, visited = new Set<string>()): any[] {
46
49
  if (maxDepth >= 0 && currentDepth >= maxDepth) return []
47
50
  const children = childrenOf(entries, rootId)
48
- return children.map(entry => ({
49
- id: entry.id,
50
- label: entry.label,
51
- type: entry.type,
52
- meta: entry.meta,
53
- order: entry.order,
54
- children: buildTree(entries, entry.id, maxDepth, currentDepth + 1),
55
- }))
51
+ return children.filter(e => !visited.has(e.id)).map(entry => {
52
+ const next = new Set(visited)
53
+ next.add(entry.id)
54
+ return {
55
+ id: entry.id,
56
+ label: entry.label,
57
+ type: entry.type,
58
+ meta: entry.meta,
59
+ order: entry.order,
60
+ children: buildTree(entries, entry.id, maxDepth, currentDepth + 1, next),
61
+ }
62
+ })
56
63
  }
57
64
 
58
65
  export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer) {
66
+ // Temporary diagnostic: list ALL entries with their parentId (no recursion)
67
+ mcp.tool(
68
+ '_debug_list_all',
69
+ 'List ALL tree entries with their raw parentId. For debugging circular references.',
70
+ {},
71
+ async () => {
72
+ const treeMap = server.getTreeMap()
73
+ if (!treeMap) return { content: [{ type: 'text', text: 'Not connected' }] }
74
+ const entries = readEntries(treeMap)
75
+ return { content: [{ type: 'text', text: JSON.stringify(entries.map(e => ({ id: e.id, label: e.label, parentId: e.parentId, type: e.type })), null, 2) }] }
76
+ }
77
+ )
78
+
59
79
  mcp.tool(
60
80
  'list_documents',
61
81
  'List direct children of a document (defaults to root). Returns id, label, type, meta, order.',
@@ -108,7 +128,7 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
108
128
  {
109
129
  parentId: z.string().optional().describe('Parent document ID. Omit for top-level pages. Use a document ID for nested/child pages.'),
110
130
  label: z.string().describe('Display name / title for the document.'),
111
- type: z.string().optional().describe('Page type: "doc", "kanban", "calendar", "table", "outline", "gallery", "slides", "timeline", "whiteboard", "map", "desktop", "mindmap", "graph". Omit to inherit parent view.'),
131
+ type: z.string().optional().describe('Page type: "doc", "kanban", "calendar", "table", "outline", "gallery", "slides", "timeline", "whiteboard", "map", "dashboard", "mindmap", "graph". Omit to inherit parent view.'),
112
132
  meta: z.record(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.'),
113
133
  },
114
134
  async ({ parentId, label, type, meta }) => {
@@ -224,7 +244,7 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
224
244
  'Change the page type view of a document (data is preserved).',
225
245
  {
226
246
  id: z.string().describe('Document ID.'),
227
- type: z.string().describe('New page type (e.g. "doc", "kanban", "table", "calendar", "outline", "gallery", "slides", "timeline", "whiteboard", "map", "desktop", "mindmap", "graph").'),
247
+ type: z.string().describe('New page type (e.g. "doc", "kanban", "table", "calendar", "outline", "gallery", "slides", "timeline", "whiteboard", "map", "dashboard", "mindmap", "graph").'),
228
248
  },
229
249
  async ({ id, type }) => {
230
250
  const treeMap = server.getTreeMap()