remnote-mcp-server 0.15.0 → 0.16.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +11 -9
  3. package/dist/index.js +2 -7
  4. package/dist/index.js.map +1 -1
  5. package/dist/remnote-cli/cli.js +5 -2
  6. package/dist/remnote-cli/cli.js.map +1 -1
  7. package/dist/remnote-cli/client/mcp-server-client.d.ts +1 -0
  8. package/dist/remnote-cli/client/mcp-server-client.js +7 -4
  9. package/dist/remnote-cli/client/mcp-server-client.js.map +1 -1
  10. package/dist/remnote-cli/commands/create.js +3 -0
  11. package/dist/remnote-cli/commands/create.js.map +1 -1
  12. package/dist/remnote-cli/commands/read.js +17 -3
  13. package/dist/remnote-cli/commands/read.js.map +1 -1
  14. package/dist/remnote-cli/commands/search.d.ts +1 -0
  15. package/dist/remnote-cli/commands/search.js +88 -7
  16. package/dist/remnote-cli/commands/search.js.map +1 -1
  17. package/dist/remnote-cli/commands/write-actions.d.ts +2 -0
  18. package/dist/remnote-cli/commands/write-actions.js +106 -0
  19. package/dist/remnote-cli/commands/write-actions.js.map +1 -1
  20. package/dist/schemas/remnote-schemas.d.ts +73 -9
  21. package/dist/schemas/remnote-schemas.js +125 -48
  22. package/dist/schemas/remnote-schemas.js.map +1 -1
  23. package/dist/shutdown.d.ts +15 -0
  24. package/dist/shutdown.js +59 -0
  25. package/dist/shutdown.js.map +1 -0
  26. package/dist/tools/index.d.ts +2915 -618
  27. package/dist/tools/index.js +442 -23
  28. package/dist/tools/index.js.map +1 -1
  29. package/dist/websocket-server.d.ts +2 -1
  30. package/dist/websocket-server.js +6 -2
  31. package/dist/websocket-server.js.map +1 -1
  32. package/mcpb/remnote-local/manifest.json +16 -4
  33. package/mcpb/remnote-local/package.json +1 -1
  34. package/mcpb/remnote-local/remnote-local.mcpb +0 -0
  35. package/mcpb/remnote-local/server/fallback-tools.generated.js +160 -6
  36. package/mcpb/remnote-local/server/index.js +4 -2
  37. package/package.json +1 -1
@@ -4,6 +4,9 @@ import { SearchSchema } from '../schemas/remnote-schemas.js';
4
4
  import { SearchByTagSchema } from '../schemas/remnote-schemas.js';
5
5
  import { ReadNoteSchema } from '../schemas/remnote-schemas.js';
6
6
  import { UpdateNoteSchema } from '../schemas/remnote-schemas.js';
7
+ import { SetDocumentStatusSchema } from '../schemas/remnote-schemas.js';
8
+ import { ListChildrenSchema } from '../schemas/remnote-schemas.js';
9
+ import { MoveNoteSchema } from '../schemas/remnote-schemas.js';
7
10
  import { InsertChildrenSchema } from '../schemas/remnote-schemas.js';
8
11
  import { ReplaceChildrenSchema } from '../schemas/remnote-schemas.js';
9
12
  import { UpdateTagsSchema } from '../schemas/remnote-schemas.js';
@@ -11,10 +14,24 @@ import { AppendJournalSchema } from '../schemas/remnote-schemas.js';
11
14
  import { ReadTableSchema } from '../schemas/remnote-schemas.js';
12
15
  import { checkVersionCompatibility } from '../version-compat.js';
13
16
  const NAVIGATION_PRESET = {
14
- includeContent: 'structured',
17
+ contentMode: 'structured',
18
+ view: 'compact',
15
19
  depth: 1,
16
20
  childLimit: 500,
17
21
  };
22
+ const ANCESTOR_SCHEMA = {
23
+ type: 'object',
24
+ properties: {
25
+ remId: { type: 'string', description: 'Ancestor Rem ID' },
26
+ title: { type: 'string', description: 'Rendered ancestor title' },
27
+ remType: {
28
+ type: 'string',
29
+ description: 'Ancestor Rem classification when available',
30
+ },
31
+ },
32
+ required: ['remId', 'title'],
33
+ additionalProperties: false,
34
+ };
18
35
  const TAG_INFO_SCHEMA = {
19
36
  type: 'object',
20
37
  properties: {
@@ -24,6 +41,57 @@ const TAG_INFO_SCHEMA = {
24
41
  required: ['tagRemId', 'name'],
25
42
  additionalProperties: false,
26
43
  };
44
+ const INLINE_REF_SCHEMA = {
45
+ type: 'object',
46
+ properties: {
47
+ text: { type: 'string', description: 'Rendered target text used in output' },
48
+ targetRemId: { type: 'string', description: 'Exact target Rem ID' },
49
+ kind: { type: 'string', enum: ['rem'], description: 'Inline reference kind' },
50
+ },
51
+ required: ['text', 'targetRemId', 'kind'],
52
+ additionalProperties: false,
53
+ };
54
+ const MATCHED_REM_SCHEMA = {
55
+ type: 'object',
56
+ properties: {
57
+ remId: { type: 'string', description: 'Directly tagged Rem ID' },
58
+ title: { type: 'string', description: 'Rendered title with markdown formatting' },
59
+ headline: { type: 'string', description: 'Display-oriented full line' },
60
+ inlineRefs: {
61
+ type: 'array',
62
+ items: INLINE_REF_SCHEMA,
63
+ description: 'Resolvable inline Rem references from the matched Rem title/headline',
64
+ },
65
+ remType: {
66
+ type: 'string',
67
+ description: 'Rem classification: document, dailyDocument, concept, descriptor, portal, or text',
68
+ },
69
+ parentRemId: {
70
+ type: 'string',
71
+ description: 'Direct parent Rem ID (omitted for top-level Rems)',
72
+ },
73
+ parentTitle: {
74
+ type: 'string',
75
+ description: 'Direct parent title/front text (omitted for top-level Rems)',
76
+ },
77
+ ancestors: {
78
+ type: 'array',
79
+ items: ANCESTOR_SCHEMA,
80
+ description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
81
+ },
82
+ ancestorsTruncated: {
83
+ type: 'boolean',
84
+ description: 'Whether the ancestor chain was capped by ancestorDepth',
85
+ },
86
+ tags: {
87
+ type: 'array',
88
+ items: TAG_INFO_SCHEMA,
89
+ description: 'Direct tags applied to the matched Rem as exact tag Rem IDs plus names',
90
+ },
91
+ },
92
+ required: ['remId', 'title', 'headline', 'remType'],
93
+ additionalProperties: false,
94
+ };
27
95
  export const CREATE_NOTE_TOOL = {
28
96
  name: 'remnote_create_note',
29
97
  description: 'Create a new note in RemNote with optional content, parent, and exact tag Rem IDs. Supports hierarchical markdown in content and flashcard syntax (e.g. "- Term :: Definition"). At least one of title or content must be provided. Recommended preflight once per session: remnote_status.',
@@ -44,6 +112,10 @@ export const CREATE_NOTE_TOOL = {
44
112
  items: { type: 'string' },
45
113
  description: 'Exact tag Rem IDs to apply',
46
114
  },
115
+ asDocument: {
116
+ type: 'boolean',
117
+ description: 'Mark the created title/root Rem as a document while preserving any concept/card status',
118
+ },
47
119
  },
48
120
  required: [],
49
121
  additionalProperties: false,
@@ -67,17 +139,30 @@ export const CREATE_NOTE_TOOL = {
67
139
  };
68
140
  export const SEARCH_TOOL = {
69
141
  name: 'remnote_search',
70
- description: 'Search the RemNote knowledge base. For whole-KB orientation, prefer includeContent="structured" with depth=1 and childLimit=500, then follow remIds with remnote_read_note. Use includeContent="markdown" for human-readable summaries.',
142
+ description: 'Search the RemNote knowledge base. Supports cursor paging through hasMore/nextCursor. For whole-KB orientation, prefer contentMode="structured" with view="compact", depth=1, and childLimit=500. Request ancestorDepth when hierarchy context matters.',
71
143
  inputSchema: {
72
144
  type: 'object',
73
145
  properties: {
74
146
  query: { type: 'string', description: 'Search query text' },
75
147
  limit: { type: 'number', description: 'Maximum results (1-150, default: 50)' },
76
- includeContent: {
148
+ cursor: {
149
+ type: 'string',
150
+ description: 'Opaque cursor returned by a previous remnote_search response',
151
+ },
152
+ contentMode: {
77
153
  type: 'string',
78
154
  enum: ['none', 'markdown', 'structured'],
79
155
  description: 'Content rendering mode: "none" omits content (default), "markdown" renders child subtree as indented markdown, "structured" returns nested child objects with remIds',
80
156
  },
157
+ view: {
158
+ type: 'string',
159
+ enum: ['compact', 'standard', 'full'],
160
+ description: 'Output detail level: compact, standard, or full',
161
+ },
162
+ ancestorDepth: {
163
+ type: 'number',
164
+ description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
165
+ },
81
166
  depth: {
82
167
  type: 'number',
83
168
  description: 'Depth of child hierarchy to render for markdown/structured content (0-10, default: 1)',
@@ -108,6 +193,11 @@ export const SEARCH_TOOL = {
108
193
  type: 'string',
109
194
  description: 'Display-oriented full line: title + type-aware delimiter + detail (e.g. "Term :: Definition")',
110
195
  },
196
+ inlineRefs: {
197
+ type: 'array',
198
+ items: INLINE_REF_SCHEMA,
199
+ description: 'Resolvable inline Rem references from the rendered title/headline. Markdown text preserves these as [[Target Title]].',
200
+ },
111
201
  parentRemId: {
112
202
  type: 'string',
113
203
  description: 'Direct parent Rem ID (omitted for top-level Rems)',
@@ -116,6 +206,15 @@ export const SEARCH_TOOL = {
116
206
  type: 'string',
117
207
  description: 'Direct parent title/front text (omitted for top-level Rems)',
118
208
  },
209
+ ancestors: {
210
+ type: 'array',
211
+ items: ANCESTOR_SCHEMA,
212
+ description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
213
+ },
214
+ ancestorsTruncated: {
215
+ type: 'boolean',
216
+ description: 'Whether the ancestor chain was capped by ancestorDepth',
217
+ },
119
218
  aliases: {
120
219
  type: 'array',
121
220
  items: { type: 'string' },
@@ -130,17 +229,35 @@ export const SEARCH_TOOL = {
130
229
  type: 'string',
131
230
  description: 'Rem classification: document, dailyDocument, concept, descriptor, portal, or text',
132
231
  },
232
+ matchedRems: {
233
+ type: 'array',
234
+ items: MATCHED_REM_SCHEMA,
235
+ description: 'For remnote_search_by_tag context mode, direct Rems carrying the requested tag that produced this context result',
236
+ },
237
+ contextRemId: {
238
+ type: 'string',
239
+ description: 'For remnote_search_by_tag tagged mode, resolved ancestor/context Rem ID for this direct match',
240
+ },
241
+ contextTitle: {
242
+ type: 'string',
243
+ description: 'For remnote_search_by_tag tagged mode, resolved ancestor/context title for this direct match',
244
+ },
245
+ contextReason: {
246
+ type: 'string',
247
+ enum: ['ancestor-document', 'ancestor-concept', 'ancestor-context', 'self'],
248
+ description: 'For remnote_search_by_tag tagged mode, why the context Rem was selected',
249
+ },
133
250
  cardDirection: {
134
251
  type: 'string',
135
252
  description: 'Flashcard direction: forward, reverse, or bidirectional (omitted if not a flashcard)',
136
253
  },
137
254
  content: {
138
255
  type: 'string',
139
- description: 'Rendered markdown content of child subtree (only when includeContent="markdown")',
256
+ description: 'Rendered markdown content of child subtree (only when contentMode="markdown")',
140
257
  },
141
258
  contentStructured: {
142
259
  type: 'array',
143
- description: 'Structured child subtree with nested remIds and metadata (only when includeContent="structured")',
260
+ description: 'Structured child subtree with nested remIds and metadata (only when contentMode="structured")',
144
261
  items: {
145
262
  type: 'object',
146
263
  properties: {
@@ -150,6 +267,11 @@ export const SEARCH_TOOL = {
150
267
  type: 'string',
151
268
  description: 'Display-oriented child line with type-aware delimiter',
152
269
  },
270
+ inlineRefs: {
271
+ type: 'array',
272
+ items: INLINE_REF_SCHEMA,
273
+ description: 'Resolvable inline Rem references from the child title/headline',
274
+ },
153
275
  aliases: {
154
276
  type: 'array',
155
277
  items: { type: 'string' },
@@ -198,12 +320,29 @@ export const SEARCH_TOOL = {
198
320
  },
199
321
  },
200
322
  },
323
+ hasMore: {
324
+ type: 'boolean',
325
+ description: 'Whether more results are available through nextCursor',
326
+ },
327
+ nextCursor: {
328
+ type: 'string',
329
+ description: 'Opaque cursor for the next page when hasMore is true',
330
+ },
331
+ truncated: {
332
+ type: 'boolean',
333
+ description: 'Whether search results may be incomplete because the bridge hit its snapshot cap',
334
+ },
335
+ truncationReason: {
336
+ type: 'string',
337
+ enum: ['cursor_snapshot_limit'],
338
+ description: 'Reason search results may be incomplete when truncated is true',
339
+ },
201
340
  },
202
341
  },
203
342
  };
204
343
  export const SEARCH_BY_TAG_TOOL = {
205
344
  name: 'remnote_search_by_tag',
206
- description: 'Find notes by exact tag Rem ID and return resolved ancestor context targets (nearest document/daily document when available, otherwise nearest non-document ancestor). Does not look up tags by name or alias. Supports the same includeContent modes as remnote_search.',
345
+ description: 'Find notes by exact tag Rem ID. Supports cursor paging through hasMore/nextCursor. Default resultMode="context" returns resolved ancestor context targets with matchedRems; resultMode="tagged" returns directly tagged Rems with context metadata. Request ancestorDepth to include parent-first ancestors on both context results and matchedRems.',
207
346
  inputSchema: {
208
347
  type: 'object',
209
348
  properties: {
@@ -211,12 +350,26 @@ export const SEARCH_BY_TAG_TOOL = {
211
350
  type: 'string',
212
351
  description: 'Exact tag Rem ID to search',
213
352
  },
353
+ resultMode: {
354
+ type: 'string',
355
+ enum: ['context', 'tagged'],
356
+ description: '"context" returns resolved ancestor context targets with matchedRems (default); "tagged" returns directly tagged Rems with context metadata',
357
+ },
214
358
  limit: { type: 'number', description: 'Maximum results (1-150, default: 50)' },
215
- includeContent: {
359
+ contentMode: {
216
360
  type: 'string',
217
361
  enum: ['none', 'markdown', 'structured'],
218
362
  description: 'Content rendering mode: "none" omits content (default), "markdown" renders child subtree as indented markdown, "structured" returns nested child objects with remIds',
219
363
  },
364
+ view: {
365
+ type: 'string',
366
+ enum: ['compact', 'standard', 'full'],
367
+ description: 'Output detail level: compact, standard, or full',
368
+ },
369
+ ancestorDepth: {
370
+ type: 'number',
371
+ description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
372
+ },
220
373
  depth: {
221
374
  type: 'number',
222
375
  description: 'Depth of child hierarchy to render for markdown/structured content (0-10, default: 1)',
@@ -229,14 +382,31 @@ export const SEARCH_BY_TAG_TOOL = {
229
382
  type: 'number',
230
383
  description: 'Maximum character length for rendered content (default: 3000)',
231
384
  },
385
+ cursor: {
386
+ type: 'string',
387
+ description: 'Opaque cursor returned by a previous remnote_search_by_tag response',
388
+ },
389
+ timeoutMs: {
390
+ type: 'number',
391
+ description: 'Per-call bridge wait timeout in milliseconds (1-60000, default: 15000). Does not cancel plugin-side work.',
392
+ },
232
393
  },
233
394
  required: ['tagRemId'],
234
395
  },
235
- outputSchema: SEARCH_TOOL.outputSchema,
396
+ outputSchema: {
397
+ type: 'object',
398
+ properties: {
399
+ results: SEARCH_TOOL.outputSchema.properties.results,
400
+ hasMore: SEARCH_TOOL.outputSchema.properties.hasMore,
401
+ nextCursor: SEARCH_TOOL.outputSchema.properties.nextCursor,
402
+ truncated: SEARCH_TOOL.outputSchema.properties.truncated,
403
+ truncationReason: SEARCH_TOOL.outputSchema.properties.truncationReason,
404
+ },
405
+ },
236
406
  };
237
407
  export const READ_NOTE_TOOL = {
238
408
  name: 'remnote_read_note',
239
- description: 'Read a specific note from RemNote by its Rem ID. For hierarchy traversal, prefer includeContent="structured" and start shallow (depth=1, childLimit=500), then deepen only selected branches. Use includeContent="markdown" for summarization.',
409
+ description: 'Read a specific note from RemNote by its Rem ID. For hierarchy traversal, prefer contentMode="structured" and start shallow (depth=1, childLimit=500), then deepen only selected branches. Request ancestorDepth when placement context matters.',
240
410
  inputSchema: {
241
411
  type: 'object',
242
412
  properties: {
@@ -245,11 +415,20 @@ export const READ_NOTE_TOOL = {
245
415
  type: 'number',
246
416
  description: 'Depth of child hierarchy to render (0-10, default: 5)',
247
417
  },
248
- includeContent: {
418
+ contentMode: {
249
419
  type: 'string',
250
420
  enum: ['none', 'markdown', 'structured'],
251
421
  description: 'Content rendering mode: "markdown" renders child subtree (default), "structured" returns nested child objects with remIds, "none" omits content',
252
422
  },
423
+ view: {
424
+ type: 'string',
425
+ enum: ['compact', 'standard', 'full'],
426
+ description: 'Output detail level: compact, standard, or full',
427
+ },
428
+ ancestorDepth: {
429
+ type: 'number',
430
+ description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
431
+ },
253
432
  childLimit: {
254
433
  type: 'number',
255
434
  description: 'Maximum children per level (1-500, default: 100)',
@@ -270,6 +449,11 @@ export const READ_NOTE_TOOL = {
270
449
  type: 'string',
271
450
  description: 'Display-oriented full line: title + type-aware delimiter + detail (e.g. "Term :: Definition")',
272
451
  },
452
+ inlineRefs: {
453
+ type: 'array',
454
+ items: INLINE_REF_SCHEMA,
455
+ description: 'Resolvable inline Rem references from the rendered title/headline. Markdown text preserves these as [[Target Title]].',
456
+ },
273
457
  parentRemId: {
274
458
  type: 'string',
275
459
  description: 'Direct parent Rem ID (omitted for top-level Rems)',
@@ -278,6 +462,15 @@ export const READ_NOTE_TOOL = {
278
462
  type: 'string',
279
463
  description: 'Direct parent title/front text (omitted for top-level Rems)',
280
464
  },
465
+ ancestors: {
466
+ type: 'array',
467
+ items: ANCESTOR_SCHEMA,
468
+ description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
469
+ },
470
+ ancestorsTruncated: {
471
+ type: 'boolean',
472
+ description: 'Whether the ancestor chain was capped by ancestorDepth',
473
+ },
281
474
  aliases: {
282
475
  type: 'array',
283
476
  items: { type: 'string' },
@@ -298,11 +491,11 @@ export const READ_NOTE_TOOL = {
298
491
  },
299
492
  content: {
300
493
  type: 'string',
301
- description: 'Rendered markdown content of child subtree (when includeContent="markdown", which is the default)',
494
+ description: 'Rendered markdown content of child subtree (when contentMode="markdown", which is the default)',
302
495
  },
303
496
  contentStructured: {
304
497
  type: 'array',
305
- description: 'Structured child subtree with nested remIds and metadata (only when includeContent="structured")',
498
+ description: 'Structured child subtree with nested remIds and metadata (only when contentMode="structured")',
306
499
  items: {
307
500
  type: 'object',
308
501
  properties: {
@@ -312,6 +505,11 @@ export const READ_NOTE_TOOL = {
312
505
  type: 'string',
313
506
  description: 'Display-oriented child line with type-aware delimiter',
314
507
  },
508
+ inlineRefs: {
509
+ type: 'array',
510
+ items: INLINE_REF_SCHEMA,
511
+ description: 'Resolvable inline Rem references from the child title/headline',
512
+ },
315
513
  aliases: {
316
514
  type: 'array',
317
515
  items: { type: 'string' },
@@ -360,6 +558,98 @@ export const READ_NOTE_TOOL = {
360
558
  },
361
559
  },
362
560
  };
561
+ export const LIST_CHILDREN_TOOL = {
562
+ name: 'remnote_list_children',
563
+ description: 'List direct child Rems under a parent without rendering a subtree. Use for cheap hierarchy traversal; page with nextCursor when hasMore is true.',
564
+ inputSchema: {
565
+ type: 'object',
566
+ properties: {
567
+ parentRemId: { type: 'string', description: 'Parent Rem ID whose direct children to list' },
568
+ limit: { type: 'number', description: 'Maximum direct children (1-150, default: 50)' },
569
+ cursor: {
570
+ type: 'string',
571
+ description: 'Opaque cursor returned by a previous remnote_list_children response',
572
+ },
573
+ view: {
574
+ type: 'string',
575
+ enum: ['compact', 'standard', 'full'],
576
+ description: 'Output detail level for child metadata: compact, standard, or full',
577
+ },
578
+ ancestorDepth: {
579
+ type: 'number',
580
+ description: 'Number of parent Rems to include for each child, direct parent first',
581
+ },
582
+ },
583
+ required: ['parentRemId'],
584
+ },
585
+ outputSchema: {
586
+ type: 'object',
587
+ properties: {
588
+ children: {
589
+ type: 'array',
590
+ items: SEARCH_TOOL.outputSchema.properties.results.items,
591
+ },
592
+ hasMore: SEARCH_TOOL.outputSchema.properties.hasMore,
593
+ nextCursor: SEARCH_TOOL.outputSchema.properties.nextCursor,
594
+ totalChildren: {
595
+ type: 'number',
596
+ description: 'Total direct children observed when listing this parent',
597
+ },
598
+ },
599
+ },
600
+ };
601
+ export const MOVE_NOTE_TOOL = {
602
+ name: 'remnote_move_note',
603
+ description: 'Safely move an existing Rem and its subtree under a new parent. Defaults to dryRun=true; pass dryRun=false only after user approval. expectedOldParentRemId rejects stale move proposals.',
604
+ inputSchema: {
605
+ type: 'object',
606
+ properties: {
607
+ remId: { type: 'string', description: 'Rem ID to move' },
608
+ newParentRemId: { type: 'string', description: 'New parent Rem ID' },
609
+ position: {
610
+ type: 'string',
611
+ enum: ['first', 'last', 'before', 'after'],
612
+ description: 'Where to place the moved Rem under the new parent (default: last)',
613
+ },
614
+ siblingRemId: {
615
+ type: 'string',
616
+ description: 'Sibling Rem ID required for before/after positioning',
617
+ },
618
+ dryRun: {
619
+ type: 'boolean',
620
+ description: 'Preview without mutating RemNote (default: true)',
621
+ },
622
+ expectedOldParentRemId: {
623
+ type: 'string',
624
+ description: 'Reject if the current direct parent differs from this Rem ID',
625
+ },
626
+ ancestorDepth: {
627
+ type: 'number',
628
+ description: 'Number of parent Rems to include before/after the move',
629
+ },
630
+ },
631
+ required: ['remId', 'newParentRemId'],
632
+ },
633
+ outputSchema: {
634
+ type: 'object',
635
+ properties: {
636
+ remId: { type: 'string' },
637
+ title: { type: 'string' },
638
+ dryRun: { type: 'boolean' },
639
+ oldParentRemId: { type: 'string' },
640
+ oldParentTitle: { type: 'string' },
641
+ newParentRemId: { type: 'string' },
642
+ newParentTitle: { type: 'string' },
643
+ position: { type: 'string', enum: ['first', 'last', 'before', 'after'] },
644
+ siblingRemId: { type: 'string' },
645
+ ancestorsBefore: { type: 'array', items: ANCESTOR_SCHEMA },
646
+ ancestorsBeforeTruncated: { type: 'boolean' },
647
+ ancestorsAfter: { type: 'array', items: ANCESTOR_SCHEMA },
648
+ ancestorsAfterTruncated: { type: 'boolean' },
649
+ },
650
+ required: ['remId', 'title', 'dryRun', 'newParentRemId', 'newParentTitle', 'position'],
651
+ },
652
+ };
363
653
  export const UPDATE_NOTE_TOOL = {
364
654
  name: 'remnote_update_note',
365
655
  description: 'Update note metadata in RemNote. Use this tool for title changes only.',
@@ -389,6 +679,100 @@ export const UPDATE_NOTE_TOOL = {
389
679
  required: ['remIds', 'titles'],
390
680
  },
391
681
  };
682
+ export const SET_DOCUMENT_STATUS_TOOL = {
683
+ name: 'remnote_set_document_status',
684
+ description: 'Preview or set whether an existing Rem is marked as a document. Uses dryRun=true by default, preserves Rem ID, children, parent, tags, and concept/card status, and requires write operations to be enabled.',
685
+ inputSchema: {
686
+ type: 'object',
687
+ properties: {
688
+ remId: { type: 'string', description: 'The Rem ID whose document status should change' },
689
+ isDocument: {
690
+ type: 'boolean',
691
+ description: 'Whether the Rem should be marked as a document',
692
+ },
693
+ dryRun: {
694
+ type: 'boolean',
695
+ description: 'Preview the document-status change without mutating RemNote (default: true)',
696
+ },
697
+ expectedOldRemType: {
698
+ type: 'string',
699
+ enum: ['document', 'dailyDocument', 'concept', 'descriptor', 'portal', 'text'],
700
+ description: 'Optional stale-context guard; reject if current remType differs from this value',
701
+ },
702
+ },
703
+ required: ['remId', 'isDocument'],
704
+ additionalProperties: false,
705
+ },
706
+ outputSchema: {
707
+ type: 'object',
708
+ properties: {
709
+ remId: { type: 'string', description: 'The Rem ID that was checked or changed' },
710
+ title: { type: 'string', description: 'Rendered title/front text' },
711
+ oldRemType: {
712
+ type: 'string',
713
+ description: 'Bridge remType before the requested document-status change',
714
+ },
715
+ newRemType: {
716
+ type: 'string',
717
+ description: 'Bridge remType after the change, or previewed remType for dry runs',
718
+ },
719
+ oldIsDocument: {
720
+ type: 'boolean',
721
+ description: 'Document status before the requested change',
722
+ },
723
+ newIsDocument: {
724
+ type: 'boolean',
725
+ description: 'Document status after the change, or previewed status for dry runs',
726
+ },
727
+ requestedIsDocument: {
728
+ type: 'boolean',
729
+ description: 'Requested document status',
730
+ },
731
+ dryRun: {
732
+ type: 'boolean',
733
+ description: 'Whether the request was only a preview',
734
+ },
735
+ changed: {
736
+ type: 'boolean',
737
+ description: 'Whether RemNote was actually mutated by this call',
738
+ },
739
+ wouldChange: {
740
+ type: 'boolean',
741
+ description: 'Whether the requested status differs from the current status',
742
+ },
743
+ sdkSupportsDocumentStatus: {
744
+ type: 'boolean',
745
+ description: 'Whether the bridge runtime exposes a safe SDK document-status setter',
746
+ },
747
+ warnings: {
748
+ type: 'array',
749
+ items: { type: 'string' },
750
+ description: 'Important side-effect notes, especially around preserved card status',
751
+ },
752
+ cardDirectionBefore: {
753
+ type: 'string',
754
+ description: 'Card direction before the change when available',
755
+ },
756
+ cardDirectionAfter: {
757
+ type: 'string',
758
+ description: 'Card direction after the change when available',
759
+ },
760
+ },
761
+ required: [
762
+ 'remId',
763
+ 'title',
764
+ 'oldRemType',
765
+ 'newRemType',
766
+ 'oldIsDocument',
767
+ 'newIsDocument',
768
+ 'requestedIsDocument',
769
+ 'dryRun',
770
+ 'changed',
771
+ 'wouldChange',
772
+ 'sdkSupportsDocumentStatus',
773
+ ],
774
+ },
775
+ };
392
776
  export const INSERT_CHILDREN_TOOL = {
393
777
  name: 'remnote_insert_children',
394
778
  description: 'Insert new child Rems under a parent at a deterministic position without replacing existing children. Use this for tag description nodes and hierarchy maintenance.',
@@ -634,7 +1018,8 @@ export const PLAYBOOK_TOOL = {
634
1018
  orientation: {
635
1019
  type: 'object',
636
1020
  properties: {
637
- includeContent: { type: 'string' },
1021
+ contentMode: { type: 'string' },
1022
+ view: { type: 'string' },
638
1023
  depth: { type: 'number' },
639
1024
  childLimit: { type: 'number' },
640
1025
  },
@@ -670,7 +1055,10 @@ export const ALL_TOOLS = [
670
1055
  SEARCH_TOOL,
671
1056
  SEARCH_BY_TAG_TOOL,
672
1057
  READ_NOTE_TOOL,
1058
+ LIST_CHILDREN_TOOL,
673
1059
  UPDATE_NOTE_TOOL,
1060
+ SET_DOCUMENT_STATUS_TOOL,
1061
+ MOVE_NOTE_TOOL,
674
1062
  INSERT_CHILDREN_TOOL,
675
1063
  REPLACE_CHILDREN_TOOL,
676
1064
  UPDATE_TAGS_TOOL,
@@ -724,7 +1112,8 @@ export function registerAllTools(server, wsServer, logger) {
724
1112
  }
725
1113
  case 'remnote_search_by_tag': {
726
1114
  const args = SearchByTagSchema.parse(request.params.arguments);
727
- result = await wsServer.sendRequest('search_by_tag', args);
1115
+ const { timeoutMs, ...payload } = args;
1116
+ result = await wsServer.sendRequest('search_by_tag', payload, timeoutMs);
728
1117
  break;
729
1118
  }
730
1119
  case 'remnote_read_note': {
@@ -732,16 +1121,31 @@ export function registerAllTools(server, wsServer, logger) {
732
1121
  result = await wsServer.sendRequest('read_note', args);
733
1122
  break;
734
1123
  }
1124
+ case 'remnote_list_children': {
1125
+ const args = ListChildrenSchema.parse(request.params.arguments);
1126
+ result = await wsServer.sendRequest('list_children', args);
1127
+ break;
1128
+ }
735
1129
  case 'remnote_update_note': {
736
1130
  const args = UpdateNoteSchema.parse(request.params.arguments);
737
1131
  result = await wsServer.sendRequest('update_note', args);
738
1132
  break;
739
1133
  }
1134
+ case 'remnote_set_document_status': {
1135
+ const args = SetDocumentStatusSchema.parse(request.params.arguments);
1136
+ result = await wsServer.sendRequest('set_document_status', args);
1137
+ break;
1138
+ }
740
1139
  case 'remnote_insert_children': {
741
1140
  const args = InsertChildrenSchema.parse(request.params.arguments);
742
1141
  result = await wsServer.sendRequest('insert_children', args);
743
1142
  break;
744
1143
  }
1144
+ case 'remnote_move_note': {
1145
+ const args = MoveNoteSchema.parse(request.params.arguments);
1146
+ result = await wsServer.sendRequest('move_note', args);
1147
+ break;
1148
+ }
745
1149
  case 'remnote_replace_children': {
746
1150
  const args = ReplaceChildrenSchema.parse(request.params.arguments);
747
1151
  result = await wsServer.sendRequest('replace_children', args);
@@ -769,8 +1173,8 @@ export function registerAllTools(server, wsServer, logger) {
769
1173
  };
770
1174
  }
771
1175
  result = {
772
- playbookVersion: '1.1.0',
773
- summary: 'Use this playbook to navigate RemNote efficiently via remIds, and to apply write-safety checks before mutations.',
1176
+ playbookVersion: '1.5.0',
1177
+ summary: 'Use this playbook to check RemNote connection and write gates, navigate by remId with paged search/read/list workflows, request nearby ancestors when hierarchy context matters, choose compact/full output views, and apply safe exact-ID writes including dry-run-first document status changes.',
774
1178
  recommendedStatusCheck: {
775
1179
  tool: 'remnote_status',
776
1180
  cadence: 'recommended once per session and before risky writes',
@@ -778,15 +1182,23 @@ export function registerAllTools(server, wsServer, logger) {
778
1182
  },
779
1183
  decisionTree: [
780
1184
  'Need connection/capability context? Call remnote_status first.',
781
- 'Need to orient across the KB? Use remnote_search with includeContent="structured", depth=1, childLimit=500.',
782
- 'Need tagged-note context? Use remnote_search_by_tag with tagRemId; it does not resolve tag names or aliases.',
783
- 'Need to traverse a specific branch? Use remnote_read_note on a chosen remId with includeContent="structured", depth=1, childLimit=500, then recurse by child remIds.',
1185
+ 'Need to orient across the KB? Use remnote_search with contentMode="structured", view="compact", depth=1, childLimit=500.',
1186
+ 'Need broad search enumeration? Continue remnote_search or remnote_search_by_tag with nextCursor while hasMore is true.',
1187
+ 'Need tagged-note context/navigation? Use remnote_search_by_tag with tagRemId and default resultMode="context"; inspect matchedRems to see the direct tagged Rems behind each context result.',
1188
+ 'Need hierarchy placement context? Add ancestorDepth, typically 5, to search/read/search_by_tag/list_children; ancestors are direct-parent first.',
1189
+ 'Need strict tag verification? Use remnote_search_by_tag with resultMode="tagged", or verify the exact Rem in matchedRems from context mode.',
1190
+ 'Need a large tag search to finish? Prefer cursor paging first; use remnote_search_by_tag.timeoutMs only as a bounded wait-time escape hatch.',
1191
+ 'Need to traverse a specific branch cheaply? Use remnote_list_children on the parentRemId and page through direct children.',
1192
+ 'Need to read a selected subtree? Use remnote_read_note on a chosen remId with contentMode="structured", depth=1, childLimit=500, then deepen selected branches.',
1193
+ 'Need to follow inline graph references? Inspect inlineRefs on search/read results and structured child nodes for exact target Rem IDs.',
784
1194
  'Need tabular/structured data from an Advanced Table? Use remnote_read_table with either tableTitle or tableRemId. Use propertyFilter to limit columns for large tables.',
785
- 'Need a human-readable summary? Switch to includeContent="markdown" on search/read results.',
1195
+ 'Need a human-readable summary? Switch to contentMode="markdown" on search/read results.',
786
1196
  'Need to rename a note? Use remnote_update_note with remId and title only.',
787
- 'Need to create a note? Use remnote_create_note; pass tagRemIds for exact-ID tag assignment.',
1197
+ 'Need to create a note? Use remnote_create_note; pass tagRemIds for exact-ID tag assignment and asDocument=true when the title/root Rem should be a document.',
1198
+ 'Need to mark an existing Rem as a document? Use remnote_set_document_status dryRun first, include expectedOldRemType for stale-context protection, then rerun with dryRun=false after approval.',
788
1199
  'Need to append to today journal? Use remnote_append_journal; pass tagRemIds when the journal entry should be tagged.',
789
1200
  'Need to insert children? Use remnote_insert_children with an explicit position.',
1201
+ 'Need to move a note? Use remnote_move_note dryRun first, include expectedOldParentRemId for stale-context protection, then rerun with dryRun=false after approval.',
790
1202
  'Need to replace children? Check remnote_status first; remnote_replace_children requires acceptReplaceOperation=true.',
791
1203
  'Need to update tags on an existing note? Use remnote_update_tags with exact tag Rem IDs.',
792
1204
  ],
@@ -795,18 +1207,25 @@ export function registerAllTools(server, wsServer, logger) {
795
1207
  branchTraversal: NAVIGATION_PRESET,
796
1208
  },
797
1209
  contentModes: {
798
- structured: 'ID-first traversal mode. Returns contentStructured with child remIds for deterministic follow-up reads.',
799
- markdown: 'Summary mode. Returns rendered markdown content for human-facing synthesis, not deterministic child ID traversal.',
1210
+ structured: 'ID-first traversal mode. Returns contentStructured with child remIds and inlineRefs for deterministic follow-up reads.',
1211
+ markdown: 'Summary mode. Returns rendered markdown content for human-facing synthesis; inline Rem references render as [[Target Title]].',
800
1212
  none: 'Metadata-only mode when content is not needed.',
801
1213
  },
1214
+ outputViews: {
1215
+ compact: 'Small metadata surface for broad discovery. Omits tags, aliases, inlineRefs, and diagnostic fields unless another option requires them.',
1216
+ standard: 'Normal discovery metadata for most agent workflows.',
1217
+ full: 'Verbose metadata for diagnostics or detailed audits.',
1218
+ },
802
1219
  writePolicy: {
803
1220
  statusTool: 'remnote_status',
804
1221
  requiredFields: ['acceptWriteOperations', 'acceptReplaceOperation'],
805
1222
  guidance: [
806
1223
  'Create/update/insert/replace/tag/journal writes require acceptWriteOperations=true.',
807
1224
  'remnote_update_note is metadata-only; use insert_children, replace_children, and update_tags for structural or tag writes.',
1225
+ 'remnote_set_document_status changes only document status; it preserves concept/card status and defaults to dryRun=true.',
808
1226
  'remnote_replace_children requires acceptReplaceOperation=true.',
809
1227
  'remnote_insert_children preserves existing child Rem IDs; remnote_replace_children removes them.',
1228
+ 'remnote_move_note preserves the moved Rem ID and subtree; dryRun defaults to true.',
810
1229
  'All production tag writes use exact tag Rem IDs: create_note.tagRemIds, append_journal.tagRemIds, and update_tags add/remove arrays.',
811
1230
  ],
812
1231
  },