@abraca/mcp 1.5.0 → 1.8.0

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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Static resource: AI agent guide for working with Abracadabra documents.
3
3
  */
4
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
 
6
6
  const AGENT_GUIDE = `# Abracadabra AI Agent Guide
7
7
 
@@ -47,24 +47,38 @@ If you are adding content to an existing space, call \`get_document_tree(rootId:
47
47
 
48
48
  ## Page Types Reference
49
49
 
50
+ ### Core Types (always available)
51
+
50
52
  | Type | Children Are | Grandchildren Are | Depth | Key Meta on Children |
51
53
  |------|-------------|-------------------|-------|---------------------|
52
54
  | **doc** | Sub-documents | Sub-sub-documents | ∞ | — |
53
55
  | **kanban** | Columns | Cards | 2 | color, icon on cards |
54
56
  | **table** | Columns | Cells (positional rows) | 2 | — |
55
- | **calendar** | Events | — | 1 | datetimeStart, datetimeEnd, allDay, color |
57
+ | **calendar** | Events | — | 1 | datetimeStart, datetimeEnd, allDay, color, icon |
56
58
  | **timeline** | Epics | Tasks | 2 | dateStart, dateEnd, taskProgress, color |
57
59
  | **checklist** | Tasks | Sub-tasks | ∞ | checked, priority, dateEnd |
58
60
  | **outline** | Items | Sub-items | ∞ | — |
59
- | **mindmap** | Central nodes | Branches | ∞ | mmX, mmY |
60
61
  | **graph** | Nodes | — | 1 | graphX, graphY, graphPinned, color |
61
- | **gallery** | Items | — | 1 | geoLat, geoLng, datetimeStart, tags |
62
+ | **gallery** | Items | — | 1 | geoLat, geoLng, datetimeStart, tags, rating, icon, color |
62
63
  | **map** | Markers/Lines | Points (for lines) | 2 | geoType, geoLat, geoLng, icon, color |
63
- | **dashboard** | Items | | 1 | deskX, deskY, deskMode |
64
- | **spatial** | Objects | Sub-parts | | spShape, spX/Y/Z, spColor, spOpacity |
65
- | **media** | Tracks | | 1 | tags |
66
- | **slides** | Slides | | 1 | |
67
- | **whiteboard** | Objects | — | 1 | wbX, wbY, wbW, wbH |
64
+ | **slides** | Slides | Sub-slides | 2 | slidesTransition, color |
65
+ | **dashboard** | Items | | 1 | deskX, deskY, deskZ, deskMode |
66
+ | **chart** | Data points | Data points | 2 | number (value), color, tags |
67
+ | **sheets** | Columns | Cells | 2 | formula, bold, italic, textColor, bgColor, align |
68
+ | **overview** | Pages | — | 1 | |
69
+ | **call** | — (no children) | — | 0 | — |
70
+
71
+ Alias: \`desktop\` → \`dashboard\`.
72
+
73
+ ### Plugin Types (require plugin enabled on the server)
74
+
75
+ | Type | Plugin | Children Are | Grandchildren Are | Depth | Key Meta on Children |
76
+ |------|--------|-------------|-------------------|-------|---------------------|
77
+ | **spatial** | spatial | Objects | Sub-parts | ∞ | spShape, spX/Y/Z, spRX/RY/RZ, spSX/SY/SZ, color, spOpacity |
78
+ | **media** | media | Tracks | — | ∞ | tags |
79
+ | **coder** | coder | Files/Folders | — | ∞ | fileType, entry |
80
+
81
+ > Spatial uses the universal \`color\` key for object color — there is no \`spColor\`.
68
82
 
69
83
  ---
70
84
 
@@ -137,18 +151,65 @@ If you are adding content to an existing space, call \`get_document_tree(rootId:
137
151
  4. Modes: \`"icon"\` (small), \`"widget-sm"\` (240×180), \`"widget-lg"\` (400×320)
138
152
  5. Grid uses 80px cells
139
153
 
140
- **Spatial (3D scene):**
154
+ **Spatial (3D scene)** *(requires spatial plugin):*
141
155
  1. \`create_document(parentId, "3D Scene", "spatial")\`
142
156
  2. Create objects: \`create_document(sceneId, "Red Cube")\`
143
- 3. Set 3D properties: \`update_metadata(objId, { spShape: "box", spColor: "#ef4444", spX: 0, spY: 1, spZ: 0, spSX: 2, spSY: 2, spSZ: 2 })\`
144
- 4. Shapes: \`"box"\`, \`"sphere"\`, \`"cylinder"\`, \`"cone"\`, \`"plane"\`, \`"torus"\`, \`"glb"\` (uploaded 3D model)
157
+ 3. Set 3D properties: \`update_metadata(objId, { spShape: "box", color: "#ef4444", spX: 0, spY: 1, spZ: 0, spSX: 2, spSY: 2, spSZ: 2 })\`
158
+ 4. Shapes: \`"box"\`, \`"sphere"\`, \`"cylinder"\`, \`"cone"\`, \`"plane"\`, \`"torus"\`, \`"glb"\` (uploaded 3D model — set \`spModelUploadId\` and \`spModelDocId\`)
145
159
  5. Rotation (degrees): \`spRX\`, \`spRY\`, \`spRZ\`. Scale: \`spSX\`, \`spSY\`, \`spSZ\` (default 1). Opacity: \`spOpacity\` (0–100)
160
+ 6. Use the universal \`color\` key — **never \`spColor\`**
146
161
 
147
162
  **Outline (nested items):**
148
163
  1. \`create_document(parentId, "Meeting Notes", "outline")\`
149
164
  2. Create items: \`create_document(outlineId, "Agenda Item 1")\`
150
165
  3. Create sub-items (unlimited depth): \`create_document(itemId, "Sub-point")\`
151
166
 
167
+ **Slides (presentation with two-axis nav):**
168
+ 1. \`create_document(parentId, "Q1 Review", "slides")\`
169
+ 2. Create slides as direct children: \`create_document(deckId, "Intro")\`
170
+ 3. Create sub-slides (vertical navigation) as grandchildren: \`create_document(slideId, "Deep dive")\`
171
+ 4. Per-slide transition: \`update_metadata(slideId, { slidesTransition: "fade", color: "#6366f1" })\`
172
+ 5. Deck-level theme: \`update_metadata(deckId, { slidesTheme: "dark" })\`
173
+
174
+ **Chart (data viz — manual or aggregation):**
175
+ 1. \`create_document(parentId, "Sales", "chart")\`
176
+ 2. Configure: \`update_metadata(chartId, { chartType: "bar", chartMetric: "value", chartShowLegend: true, chartLimit: 10 })\`
177
+ 3. Modes:
178
+ - **Manual data points**: create children with \`number\` (value) and optional \`tags\`/\`color\`
179
+ \`create_document(chartId, "Q1")\` then \`update_metadata(dpId, { number: 42500, color: "#6366f1" })\`
180
+ - **Aggregation**: set \`chartMetric\` to \`"type"\`/\`"tag"\`/\`"status"\`/\`"priority"\`/\`"activity"\`/\`"completion"\` and point the chart at a subtree — it aggregates the descendants' meta automatically
181
+ 4. Chart types: \`"bar"\`, \`"stacked bar"\`, \`"line"\`, \`"donut"\`, \`"treemap"\`
182
+ 5. Color schemes: \`"default"\`, \`"warm"\`, \`"cool"\`, \`"mono"\`
183
+
184
+ **Sheets (spreadsheet with formulas):**
185
+ 1. \`create_document(parentId, "Budget", "sheets")\`
186
+ 2. Create columns: \`create_document(sheetId, "A")\`, \`create_document(sheetId, "B")\`
187
+ 3. Create cells under columns (positional rows, like \`table\`)
188
+ 4. Formulas on cells: \`update_metadata(cellId, { formula: "=A1+B1" })\`
189
+ 5. Cell formatting: \`update_metadata(cellId, { bold: true, bgColor: "#fef3c7", align: "right" })\`
190
+ 6. Deck config: \`update_metadata(sheetId, { sheetsDefaultColWidth: 120, sheetsDefaultRowHeight: 28, sheetsShowGridlines: true, sheetsFreezeRows: 1, sheetsFreezeCols: 1 })\`
191
+
192
+ **Overview (space home):**
193
+ 1. \`create_document(parentId, "Home", "overview")\`
194
+ 2. No children required — renders activity, people, stats from the surrounding space
195
+ 3. Children (if any) show as linked pages
196
+
197
+ **Call (video room):**
198
+ 1. \`create_document(parentId, "Daily Standup", "call")\`
199
+ 2. Video rooms have **no children** — do not add documents underneath
200
+
201
+ **Coder (multi-file collaborative editor)** *(requires coder plugin):*
202
+ 1. \`create_document(parentId, "My App", "coder")\`
203
+ 2. Create files/folders: \`create_document(projectId, "App.vue")\`, \`create_document(projectId, "src")\`
204
+ 3. Set file type: \`update_metadata(fileId, { fileType: "vue", entry: true })\`
205
+ 4. \`fileType\` options: \`"vue"\`, \`"ts"\`, \`"js"\`, \`"css"\`, \`"json"\`, \`"folder"\`
206
+ 5. Mark the entry file with \`entry: true\` — the renderer uses it as the preview root
207
+
208
+ **Media (audio/video playlist)** *(requires media plugin):*
209
+ 1. \`create_document(parentId, "Focus Mix", "media")\`
210
+ 2. Create tracks: \`create_document(playlistId, "Track 1")\` — attach audio/video file via upload tool
211
+ 3. Playlist config: \`update_metadata(playlistId, { mediaRepeat: "all", mediaShuffle: false })\`
212
+
152
213
  ---
153
214
 
154
215
  ## Document References
@@ -223,22 +284,55 @@ In the **graph** page type, document references (embeds and links) create visibl
223
284
  |-----|------|-----------|--------|
224
285
  | \`kanbanColumnWidth\` | string | kanban | "narrow", "default", "wide" |
225
286
  | \`galleryColumns\` | number | gallery | 1–6 |
226
- | \`galleryAspect\` | string | gallery | "square", "4:3", "16:9" |
287
+ | \`galleryAspect\` | string | gallery | "square", "4:3", "3:2", "16:9", "free" |
288
+ | \`galleryCardStyle\` | string | gallery | "default", "compact", "detailed" |
289
+ | \`galleryShowLabels\` | boolean | gallery | show item labels |
290
+ | \`gallerySortBy\` | string | gallery | "manual", "date", "name", "rating" |
227
291
  | \`calendarView\` | string | calendar | "month", "week", "day" |
228
292
  | \`calendarWeekStart\` | string | calendar | "sun", "mon" |
293
+ | \`calendarShowWeekNumbers\` | boolean | calendar | — |
229
294
  | \`tableMode\` | string | table | "hierarchy", "flat" |
295
+ | \`tableSortKey\` | string | table | meta key to sort by |
296
+ | \`tableSortDir\` | string | table | "asc", "desc" |
297
+ | \`timelineZoom\` | string | timeline | "week", "month", "quarter" |
298
+ | \`timelinePixelsPerDay\` | number | timeline | zoom granularity |
299
+ | \`timelineCenterDate\` | string | timeline | ISO date to center view |
300
+ | \`checklistFilter\` | string | checklist | "all", "active", "completed" |
301
+ | \`checklistSort\` | string | checklist | "manual", "priority", "due" |
302
+ | \`mapShowLabels\` | boolean | map | — |
303
+ | \`graphSpacing\` | string | graph | "compact", "default", "spacious" |
304
+ | \`graphShowLabels\` | boolean | graph | — |
305
+ | \`graphEdgeThickness\` | string | graph | "thin", "normal", "thick" |
230
306
  | \`showRefEdges\` | boolean | graph | show doc-reference edges |
231
-
232
- ### Spatial 3D Keys (for spatial children)
307
+ | \`mmSpacing\` | string | mindmap-layout | spacing between branches |
308
+ | \`spatialGridVisible\` | boolean | spatial | show ground grid |
309
+ | \`slidesTheme\` | string | slides | "dark", "light" |
310
+ | \`chartType\` | string | chart | "bar", "stacked bar", "line", "donut", "treemap" |
311
+ | \`chartMetric\` | string | chart | "value", "type", "tag", "status", "priority", "activity", "completion" |
312
+ | \`chartColorScheme\` | string | chart | "default", "warm", "cool", "mono" |
313
+ | \`chartLimit\` | number | chart | 3–30 (max items) |
314
+ | \`chartShowLegend\` | boolean | chart | — |
315
+ | \`chartShowValues\` | boolean | chart | — |
316
+ | \`sheetsDefaultColWidth\` | number | sheets | 40–500 |
317
+ | \`sheetsDefaultRowHeight\` | number | sheets | 20–100 |
318
+ | \`sheetsShowGridlines\` | boolean | sheets | — |
319
+ | \`sheetsFreezeRows\` | number | sheets | frozen rows count |
320
+ | \`sheetsFreezeCols\` | number | sheets | frozen cols count |
321
+ | \`mediaRepeat\` | string | media | "off", "all", "one" (plugin) |
322
+ | \`mediaShuffle\` | boolean | media | plugin |
323
+
324
+ ### Spatial 3D Keys (for spatial children — requires spatial plugin)
233
325
 
234
326
  | Key | Type | Default | Meaning |
235
327
  |-----|------|---------|---------|
236
328
  | \`spShape\` | string | — | "box", "sphere", "cylinder", "cone", "plane", "torus", "glb" |
237
- | \`spColor\` | string | — | CSS color |
329
+ | \`color\` | string | — | CSS color — universal key, **not** \`spColor\` |
238
330
  | \`spOpacity\` | number | 100 | 0–100 |
239
331
  | \`spX\`, \`spY\`, \`spZ\` | number | 0 | Position |
240
332
  | \`spRX\`, \`spRY\`, \`spRZ\` | number | 0 | Rotation (degrees) |
241
333
  | \`spSX\`, \`spSY\`, \`spSZ\` | number | 1 | Scale |
334
+ | \`spModelUploadId\` | string | — | GLB upload ID (when \`spShape: "glb"\`) |
335
+ | \`spModelDocId\` | string | — | Doc ID that owns the GLB upload |
242
336
 
243
337
  ### Dashboard Keys (for dashboard children)
244
338
 
@@ -248,6 +342,28 @@ In the **graph** page type, document references (embeds and links) create visibl
248
342
  | \`deskZ\` | number | Z-index (layering) |
249
343
  | \`deskMode\` | string | "icon", "widget-sm" (240×180), "widget-lg" (400×320) |
250
344
 
345
+ ### Sheets Cell Formatting (per-cell meta)
346
+
347
+ | Key | Type | Meaning |
348
+ |-----|------|---------|
349
+ | \`formula\` | string | Cell formula (e.g. "=A1+B1") |
350
+ | \`bold\` | boolean | — |
351
+ | \`italic\` | boolean | — |
352
+ | \`textColor\` | string | CSS color |
353
+ | \`bgColor\` | string | CSS color |
354
+ | \`align\` | string | "left", "center", "right" |
355
+
356
+ ### Coder Keys (for coder children — plugin)
357
+
358
+ | Key | Type | Meaning |
359
+ |-----|------|---------|
360
+ | \`fileType\` | string | "vue", "ts", "js", "css", "json", "folder" |
361
+ | \`entry\` | boolean | Mark this file as the preview entry point |
362
+
363
+ ### Discovering What Metadata Applies
364
+
365
+ Use the \`list_page_types\` tool to enumerate all known page types and their declared \`metaSchema\` (fields that apply to children/descendants) and \`defaultMetaFields\` (renderer config fields on the page itself). It's the authoritative list of what meta keys make sense for any given page type.
366
+
251
367
  ---
252
368
 
253
369
  ## Content Structure
@@ -330,8 +446,11 @@ Always clear fields when done by setting them to \`null\`.
330
446
  | **Outline** | \`outline:editing\` | nodeId | Editing an outline node |
331
447
  | **Gallery** | \`gallery:focused\` | itemId | Item hovered/selected |
332
448
  | **Timeline** | \`timeline:focused\` | taskId | Task selected |
333
- | **Mindmap** | \`mindmap:focused\` | nodeId | Node selected/edited |
334
449
  | **Graph** | \`graph:focused\` | nodeId | Node hovered/selected |
450
+ | **Slides** | \`slides:current\` | slideId | Slide being presented |
451
+ | **Spatial** | \`spatial:selected\`, \`spatial:camera\` | objectId / camera state | plugin |
452
+ | **Media** | \`media:playing\`, \`media:position\` | trackId / 0–1 | plugin |
453
+ | **Coder** | \`coder:activeFile\` | fileId | plugin |
335
454
  | **Map** | \`map:focused\` | markerId | Marker hovered/selected |
336
455
  | **Doc** | \`doc:scroll\` | 0–1 number | Scroll position in document |
337
456
 
@@ -414,22 +533,25 @@ Always check and traverse the \`children\` array returned by \`read_document\`.*
414
533
  - Create top-level documents without first confirming the correct hub doc ID
415
534
  - Rename or write content to the space hub document itself
416
535
  - Set \`type\` on child items (cards, cells, events) — only set type on the parent page
417
- `
536
+ `;
418
537
 
419
538
  export function registerAgentGuide(mcp: McpServer) {
420
539
  mcp.resource(
421
- 'agent-guide',
422
- 'abracadabra://agent-guide',
540
+ "agent-guide",
541
+ "abracadabra://agent-guide",
423
542
  {
424
- description: 'Comprehensive guide for AI agents working with Abracadabra documents. Covers page types, tree operations, metadata, content structure, and best practices.',
425
- mimeType: 'text/markdown',
543
+ description:
544
+ "Comprehensive guide for AI agents working with Abracadabra documents. Covers page types, tree operations, metadata, content structure, and best practices.",
545
+ mimeType: "text/markdown",
426
546
  },
427
547
  async () => ({
428
- contents: [{
429
- uri: 'abracadabra://agent-guide',
430
- text: AGENT_GUIDE,
431
- mimeType: 'text/markdown',
432
- }],
433
- })
434
- )
548
+ contents: [
549
+ {
550
+ uri: "abracadabra://agent-guide",
551
+ text: AGENT_GUIDE,
552
+ mimeType: "text/markdown",
553
+ },
554
+ ],
555
+ }),
556
+ );
435
557
  }
package/src/server.ts CHANGED
@@ -249,6 +249,19 @@ export class AbracadabraMCPServer {
249
249
  return this._activeConnection?.doc.getMap('doc-trash') ?? null
250
250
  }
251
251
 
252
+ /** Get plugin names enabled in the active space via space-plugins Y.Map. */
253
+ getEnabledPluginNames(): string[] {
254
+ const doc = this._activeConnection?.doc
255
+ if (!doc) return []
256
+ const pluginsMap = doc.getMap('space-plugins')
257
+ const names: string[] = []
258
+ pluginsMap.forEach((value: any, key: string) => {
259
+ const entry = value?.toJSON ? value.toJSON() : value
260
+ if (entry?.enabled) names.push(key)
261
+ })
262
+ return names
263
+ }
264
+
252
265
  /**
253
266
  * Get or create a child provider for a given document ID.
254
267
  * Caches providers and waits for sync before returning.
package/src/tools/meta.ts CHANGED
@@ -47,7 +47,7 @@ export function registerMetaTools(mcp: McpServer, server: AbracadabraMCPServer)
47
47
  'Update metadata fields on a document. Merges the provided fields into existing metadata.',
48
48
  {
49
49
  docId: z.string().describe('Document ID.'),
50
- 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"). Slides: slidesTransition ("none"|"fade"|"slide"), slidesTheme ("dark"|"light"). Chart: chartType ("bar"|"stacked bar"|"line"|"donut"|"treemap"), chartMetric, chartColorScheme, chartLimit, chartShowLegend, chartShowValues. Sheets: sheetsDefaultColWidth, sheetsDefaultRowHeight, sheetsShowGridlines, sheetsFreezeRows, sheetsFreezeCols. Cell formatting: bold, italic, textColor, bgColor, align, formula. Renderer config (on the page doc itself): kanbanColumnWidth, galleryColumns, galleryAspect, galleryCardStyle, galleryShowLabels, gallerySortBy, calendarView, calendarWeekStart, calendarShowWeekNumbers, tableMode, tableSortDir, checklistFilter, checklistSort, mapShowLabels, spatialGridVisible, showRefEdges, mediaRepeat, mediaShuffle. Set a key to null to clear it.'),
50
+ 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, timeStart/timeEnd, 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, coverDocId, dateTaken. Geo/Map (children): geoType ("marker"|"line"|"measure"), geoLat, geoLng, geoDescription. Spatial 3D (children, plugin: spatial): spShape ("box"|"sphere"|"cylinder"|"cone"|"plane"|"torus"|"glb"), spX/spY/spZ, spRX/spRY/spRZ (deg), spSX/spSY/spSZ (scale), spOpacity (0-100), spModelUploadId, spModelDocId — spatial uses the universal `color` key, NOT spColor. Dashboard (children): deskX, deskY, deskZ, deskMode ("icon"|"widget-sm"|"widget-lg"). Mindmap-layout (children): mmX, mmY. Graph-layout (children): graphX, graphY, graphPinned. Slides (children): slidesTransition ("none"|"fade"|"slide"). Coder (children, plugin: coder): fileType ("vue"|"ts"|"js"|"css"|"json"|"folder"), entry (bool). Cell formatting (sheets cells): bold, italic, textColor, bgColor, align ("left"|"center"|"right"), formula. Renderer config (on the PAGE doc itself, not children): kanbanColumnWidth ("narrow"|"default"|"wide"), galleryColumns (1-6), galleryAspect ("square"|"4:3"|"3:2"|"16:9"|"free"), galleryCardStyle ("default"|"compact"|"detailed"), galleryShowLabels, gallerySortBy ("manual"|"date"|"name"|"rating"), calendarView ("month"|"week"|"day"), calendarWeekStart ("sun"|"mon"), calendarShowWeekNumbers, tableMode ("hierarchy"|"flat"), tableSortKey, tableSortDir ("asc"|"desc"), timelineZoom ("week"|"month"|"quarter"), timelinePixelsPerDay, timelineCenterDate (ISO date), checklistFilter ("all"|"active"|"completed"), checklistSort ("manual"|"priority"|"due"), mapShowLabels, graphSpacing ("compact"|"default"|"spacious"), graphShowLabels, graphEdgeThickness ("thin"|"normal"|"thick"), showRefEdges, mmSpacing, spatialGridVisible, slidesTheme ("dark"|"light"), chartType ("bar"|"stacked bar"|"line"|"donut"|"treemap"), chartMetric ("value"|"type"|"tag"|"status"|"priority"|"activity"|"completion"), chartColorScheme ("default"|"warm"|"cool"|"mono"), chartLimit (3-30), chartShowLegend, chartShowValues, sheetsDefaultColWidth (40-500), sheetsDefaultRowHeight (20-100), sheetsShowGridlines, sheetsFreezeRows, sheetsFreezeCols, mediaRepeat ("off"|"all"|"one"), mediaShuffle. Set a key to null to clear it.'),
51
51
  },
52
52
  async ({ docId, meta }) => {
53
53
  server.setAutoStatus('writing', docId)
package/src/tools/tree.ts CHANGED
@@ -6,6 +6,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
6
6
  import { z } from 'zod'
7
7
  import type { AbracadabraMCPServer } from '../server.ts'
8
8
  import type { TreeEntry, PageMeta } from '../converters/types.ts'
9
+ import { PAGE_TYPES, TYPE_ALIASES, resolvePageType } from '../converters/page-types.ts'
9
10
 
10
11
  /**
11
12
  * Normalize a document ID so the hub/root doc ID is treated as the tree root (null).
@@ -207,7 +208,7 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
207
208
  {
208
209
  parentId: z.string().optional().describe('Parent document ID. Omit for top-level pages. Use a document ID for nested/child pages.'),
209
210
  label: z.string().describe('Display name / title for the document.'),
210
- type: z.string().optional().describe('Page type — sets how this document renders. "doc" (rich text), "kanban" (columns → cards), "table" (columns → rows with custom fields), "calendar" (events with datetimeStart/End), "timeline" (epics → tasks with dateStart/End + taskProgress), "checklist" (tasks with checked/priority, unlimited nesting), "outline" (nested items, unlimited depth), "gallery" (visual grid with covers/ratings), "map" (markers/lines with geoLat/geoLng), "graph" (force-directed knowledge graph), "dashboard" (positioned widgets with deskX/deskY/deskMode), "spatial" (3D scene with spShape/spX/spY/spZ), "media" (audio/video player with playlists), "slides" (presentation with transitions), "chart" (bar/line/donut/treemap from data points or aggregation), "sheets" (spreadsheet with formulas and formatting), "overview" (space home — activity and stats), "call" (video call room, no children). Omit to inherit parent view. Only set on the parent page, NEVER on child items.'),
211
+ type: z.string().optional().describe('Page type — sets how this document renders. Core types (always available): "doc" (rich text), "kanban" (columns → cards), "table" (columns → rows, positional), "calendar" (events with datetimeStart/End), "timeline" (epics → tasks with dateStart/End + taskProgress), "checklist" (tasks with checked/priority, unlimited nesting), "outline" (nested items, unlimited depth), "gallery" (visual grid with covers/ratings), "map" (markers/lines with geoLat/geoLng), "graph" (force-directed knowledge graph), "dashboard" (positioned widgets with deskX/deskY/deskMode), "slides" (slides sub-slides with transitions), "chart" (bar/stacked bar/line/donut/treemap from data points or aggregation), "sheets" (spreadsheet with formulas and formatting), "overview" (space home — activity and stats), "call" (video call room, no children). Plugin types (require plugin enabled on the server): "spatial" (3D scene with spShape/spX/spY/spZ + universal color, plugin: spatial), "media" (audio/video player with playlists, plugin: media), "coder" (collaborative multi-file coding env with fileType/entry, plugin: coder). Alias: "desktop" → "dashboard". Omit to inherit parent view. Only set on the parent page, NEVER on child items.'),
211
212
  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.'),
212
213
  },
213
214
  async ({ parentId, label, type, meta }) => {
@@ -360,7 +361,7 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
360
361
  'Change the page type view of a document (data is preserved).',
361
362
  {
362
363
  id: z.string().describe('Document ID.'),
363
- type: z.string().describe('New page type: "doc", "kanban", "table", "calendar", "timeline", "checklist", "outline", "gallery", "map", "graph", "dashboard", "spatial", "media", "slides", "chart", "sheets", "overview", "call".'),
364
+ type: z.string().describe('New page type. Core: "doc", "kanban", "table", "calendar", "timeline", "checklist", "outline", "gallery", "map", "graph", "dashboard", "slides", "chart", "sheets", "overview", "call". Plugin (require plugin enabled): "spatial", "media", "coder". Switching preserves the tree — children, labels, and meta are all retained; only the view changes.'),
364
365
  },
365
366
  async ({ id, type }) => {
366
367
  server.setAutoStatus('writing')
@@ -442,4 +443,28 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
442
443
  }
443
444
  }
444
445
  )
446
+
447
+ mcp.tool(
448
+ 'list_page_types',
449
+ 'Enumerate all known Abracadabra page types with their metadata schemas. Returns an array of { key, label, icon, description, core, plugin, supportsChildren, childLabel, grandchildLabel, defaultDepth, metaSchema, defaultMetaFields }. `metaSchema` describes fields that apply to DESCENDANTS (children, grandchildren, ...) of a page of this type. `defaultMetaFields` are view-config fields on the page doc itself. Plugin types (core: false) require the named plugin to be enabled on the server. Use this to discover what meta keys a given renderer supports instead of guessing.',
450
+ {
451
+ key: z.string().optional().describe('Filter to a single type by key (e.g. "kanban", "calendar"). Aliases are resolved (e.g. "desktop" → "dashboard"). Omit to list all types.'),
452
+ },
453
+ async ({ key }) => {
454
+ if (key) {
455
+ const resolved = resolvePageType(key)
456
+ if (!resolved) {
457
+ return { content: [{ type: 'text', text: `Unknown page type "${key}". Call list_page_types with no args to see all types.` }] }
458
+ }
459
+ return { content: [{ type: 'text', text: JSON.stringify(resolved, null, 2) }] }
460
+ }
461
+ const all = Object.values(PAGE_TYPES)
462
+ return {
463
+ content: [{
464
+ type: 'text',
465
+ text: JSON.stringify({ types: all, aliases: TYPE_ALIASES }, null, 2),
466
+ }],
467
+ }
468
+ }
469
+ )
445
470
  }