remnote-mcp-server 0.15.0 → 0.17.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.
- package/CHANGELOG.md +76 -0
- package/README.md +14 -9
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +7 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +26 -2
- package/dist/config.js.map +1 -1
- package/dist/daemon.js.map +1 -1
- package/dist/http-server.d.ts +2 -1
- package/dist/http-server.js +4 -2
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +3 -8
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +32 -0
- package/dist/media.js +184 -0
- package/dist/media.js.map +1 -0
- package/dist/remnote-cli/cli.js +8 -2
- package/dist/remnote-cli/cli.js.map +1 -1
- package/dist/remnote-cli/client/mcp-server-client.d.ts +1 -0
- package/dist/remnote-cli/client/mcp-server-client.js +24 -4
- package/dist/remnote-cli/client/mcp-server-client.js.map +1 -1
- package/dist/remnote-cli/commands/create.js +5 -2
- package/dist/remnote-cli/commands/create.js.map +1 -1
- package/dist/remnote-cli/commands/journal.js +1 -1
- package/dist/remnote-cli/commands/journal.js.map +1 -1
- package/dist/remnote-cli/commands/media.d.ts +2 -0
- package/dist/remnote-cli/commands/media.js +62 -0
- package/dist/remnote-cli/commands/media.js.map +1 -0
- package/dist/remnote-cli/commands/read.js +29 -3
- package/dist/remnote-cli/commands/read.js.map +1 -1
- package/dist/remnote-cli/commands/search.d.ts +1 -0
- package/dist/remnote-cli/commands/search.js +91 -7
- package/dist/remnote-cli/commands/search.js.map +1 -1
- package/dist/remnote-cli/commands/update.js +1 -1
- package/dist/remnote-cli/commands/update.js.map +1 -1
- package/dist/remnote-cli/commands/write-actions.d.ts +3 -0
- package/dist/remnote-cli/commands/write-actions.js +158 -2
- package/dist/remnote-cli/commands/write-actions.js.map +1 -1
- package/dist/schemas/remnote-schemas.d.ts +107 -9
- package/dist/schemas/remnote-schemas.js +194 -50
- package/dist/schemas/remnote-schemas.js.map +1 -1
- package/dist/shutdown.d.ts +15 -0
- package/dist/shutdown.js +59 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/tools/index.d.ts +3364 -585
- package/dist/tools/index.js +685 -41
- package/dist/tools/index.js.map +1 -1
- package/dist/types/bridge.d.ts +1 -0
- package/dist/websocket-server.d.ts +5 -1
- package/dist/websocket-server.js +18 -2
- package/dist/websocket-server.js.map +1 -1
- package/mcpb/remnote-local/manifest.json +26 -6
- package/mcpb/remnote-local/package.json +1 -1
- package/mcpb/remnote-local/remnote-local.mcpb +0 -0
- package/mcpb/remnote-local/server/fallback-tools.generated.js +285 -14
- package/mcpb/remnote-local/server/index.js +4 -2
- package/package.json +4 -4
package/dist/tools/index.js
CHANGED
|
@@ -3,18 +3,39 @@ import { CreateNoteSchema } from '../schemas/remnote-schemas.js';
|
|
|
3
3
|
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
|
+
import { GetMediaSchema } from '../schemas/remnote-schemas.js';
|
|
6
7
|
import { UpdateNoteSchema } from '../schemas/remnote-schemas.js';
|
|
8
|
+
import { SetDocumentStatusSchema } from '../schemas/remnote-schemas.js';
|
|
9
|
+
import { ListChildrenSchema } from '../schemas/remnote-schemas.js';
|
|
10
|
+
import { MoveNoteSchema } from '../schemas/remnote-schemas.js';
|
|
7
11
|
import { InsertChildrenSchema } from '../schemas/remnote-schemas.js';
|
|
8
12
|
import { ReplaceChildrenSchema } from '../schemas/remnote-schemas.js';
|
|
9
13
|
import { UpdateTagsSchema } from '../schemas/remnote-schemas.js';
|
|
14
|
+
import { SetPropertySchema } from '../schemas/remnote-schemas.js';
|
|
10
15
|
import { AppendJournalSchema } from '../schemas/remnote-schemas.js';
|
|
11
16
|
import { ReadTableSchema } from '../schemas/remnote-schemas.js';
|
|
12
17
|
import { checkVersionCompatibility } from '../version-compat.js';
|
|
18
|
+
import { parseMediaLocator, resolveManagedImage } from '../media.js';
|
|
19
|
+
const MEDIA_CAPABILITY = 'media.images.v1';
|
|
13
20
|
const NAVIGATION_PRESET = {
|
|
14
|
-
|
|
21
|
+
contentMode: 'structured',
|
|
22
|
+
view: 'compact',
|
|
15
23
|
depth: 1,
|
|
16
24
|
childLimit: 500,
|
|
17
25
|
};
|
|
26
|
+
const ANCESTOR_SCHEMA = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
remId: { type: 'string', description: 'Ancestor Rem ID' },
|
|
30
|
+
title: { type: 'string', description: 'Rendered ancestor title' },
|
|
31
|
+
remType: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Ancestor Rem classification when available',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ['remId', 'title'],
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
};
|
|
18
39
|
const TAG_INFO_SCHEMA = {
|
|
19
40
|
type: 'object',
|
|
20
41
|
properties: {
|
|
@@ -24,19 +45,70 @@ const TAG_INFO_SCHEMA = {
|
|
|
24
45
|
required: ['tagRemId', 'name'],
|
|
25
46
|
additionalProperties: false,
|
|
26
47
|
};
|
|
48
|
+
const INLINE_REF_SCHEMA = {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
text: { type: 'string', description: 'Rendered target text used in output' },
|
|
52
|
+
targetRemId: { type: 'string', description: 'Exact target Rem ID' },
|
|
53
|
+
kind: { type: 'string', enum: ['rem'], description: 'Inline reference kind' },
|
|
54
|
+
},
|
|
55
|
+
required: ['text', 'targetRemId', 'kind'],
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
};
|
|
58
|
+
const MATCHED_REM_SCHEMA = {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
remId: { type: 'string', description: 'Directly tagged Rem ID' },
|
|
62
|
+
title: { type: 'string', description: 'Rendered title with markdown formatting' },
|
|
63
|
+
headline: { type: 'string', description: 'Display-oriented full line' },
|
|
64
|
+
inlineRefs: {
|
|
65
|
+
type: 'array',
|
|
66
|
+
items: INLINE_REF_SCHEMA,
|
|
67
|
+
description: 'Resolvable inline Rem references from the matched Rem title/headline',
|
|
68
|
+
},
|
|
69
|
+
remType: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description: 'Rem classification: document, dailyDocument, concept, descriptor, portal, or text',
|
|
72
|
+
},
|
|
73
|
+
parentRemId: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Direct parent Rem ID (omitted for top-level Rems)',
|
|
76
|
+
},
|
|
77
|
+
parentTitle: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Direct parent title/front text (omitted for top-level Rems)',
|
|
80
|
+
},
|
|
81
|
+
ancestors: {
|
|
82
|
+
type: 'array',
|
|
83
|
+
items: ANCESTOR_SCHEMA,
|
|
84
|
+
description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
|
|
85
|
+
},
|
|
86
|
+
ancestorsTruncated: {
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
description: 'Whether the ancestor chain was capped by ancestorDepth',
|
|
89
|
+
},
|
|
90
|
+
tags: {
|
|
91
|
+
type: 'array',
|
|
92
|
+
items: TAG_INFO_SCHEMA,
|
|
93
|
+
description: 'Direct tags applied to the matched Rem as exact tag Rem IDs plus names',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ['remId', 'title', 'headline', 'remType'],
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
};
|
|
27
99
|
export const CREATE_NOTE_TOOL = {
|
|
28
100
|
name: 'remnote_create_note',
|
|
29
|
-
description: 'Create a new note in RemNote with optional content, parent, and exact tag Rem IDs. Supports hierarchical markdown
|
|
101
|
+
description: 'Create a new note in RemNote with optional content, parent, and exact tag Rem IDs. Supports hierarchical markdown, flashcard syntax (e.g. "- Term :: Definition"), and exact inline Rem references as [[id:<remId>]]. At least one of title or content must be provided. Recommended preflight once per session: remnote_status.',
|
|
30
102
|
inputSchema: {
|
|
31
103
|
type: 'object',
|
|
32
104
|
properties: {
|
|
33
105
|
title: {
|
|
34
106
|
type: 'string',
|
|
35
|
-
description: 'The title of the note (optional if content is provided)',
|
|
107
|
+
description: 'The title of the note (optional if content is provided). Supports exact Rem references as [[id:<remId>]].',
|
|
36
108
|
},
|
|
37
109
|
content: {
|
|
38
110
|
type: 'string',
|
|
39
|
-
description: 'Content as plain text, child bullets or hierarchical markdown',
|
|
111
|
+
description: 'Content as plain text, child bullets, or hierarchical markdown. Use [[id:<remId>]] for exact Rem references.',
|
|
40
112
|
},
|
|
41
113
|
parentId: { type: 'string', description: 'Parent Rem ID' },
|
|
42
114
|
tagRemIds: {
|
|
@@ -44,6 +116,10 @@ export const CREATE_NOTE_TOOL = {
|
|
|
44
116
|
items: { type: 'string' },
|
|
45
117
|
description: 'Exact tag Rem IDs to apply',
|
|
46
118
|
},
|
|
119
|
+
asDocument: {
|
|
120
|
+
type: 'boolean',
|
|
121
|
+
description: 'Mark the created title/root Rem as a document while preserving any concept/card status',
|
|
122
|
+
},
|
|
47
123
|
},
|
|
48
124
|
required: [],
|
|
49
125
|
additionalProperties: false,
|
|
@@ -67,17 +143,35 @@ export const CREATE_NOTE_TOOL = {
|
|
|
67
143
|
};
|
|
68
144
|
export const SEARCH_TOOL = {
|
|
69
145
|
name: 'remnote_search',
|
|
70
|
-
description: 'Search the RemNote knowledge base. For whole-KB orientation, prefer
|
|
146
|
+
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
147
|
inputSchema: {
|
|
72
148
|
type: 'object',
|
|
73
149
|
properties: {
|
|
74
150
|
query: { type: 'string', description: 'Search query text' },
|
|
151
|
+
parentRemId: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
minLength: 1,
|
|
154
|
+
description: "Optional non-empty Rem ID. Scope the search to within this Rem's subtree. The Rem itself is excluded from results.",
|
|
155
|
+
},
|
|
75
156
|
limit: { type: 'number', description: 'Maximum results (1-150, default: 50)' },
|
|
76
|
-
|
|
157
|
+
cursor: {
|
|
158
|
+
type: 'string',
|
|
159
|
+
description: 'Opaque cursor returned by a previous remnote_search response',
|
|
160
|
+
},
|
|
161
|
+
contentMode: {
|
|
77
162
|
type: 'string',
|
|
78
163
|
enum: ['none', 'markdown', 'structured'],
|
|
79
164
|
description: 'Content rendering mode: "none" omits content (default), "markdown" renders child subtree as indented markdown, "structured" returns nested child objects with remIds',
|
|
80
165
|
},
|
|
166
|
+
view: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
enum: ['compact', 'standard', 'full'],
|
|
169
|
+
description: 'Output detail level: compact, standard, or full',
|
|
170
|
+
},
|
|
171
|
+
ancestorDepth: {
|
|
172
|
+
type: 'number',
|
|
173
|
+
description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
|
|
174
|
+
},
|
|
81
175
|
depth: {
|
|
82
176
|
type: 'number',
|
|
83
177
|
description: 'Depth of child hierarchy to render for markdown/structured content (0-10, default: 1)',
|
|
@@ -108,6 +202,11 @@ export const SEARCH_TOOL = {
|
|
|
108
202
|
type: 'string',
|
|
109
203
|
description: 'Display-oriented full line: title + type-aware delimiter + detail (e.g. "Term :: Definition")',
|
|
110
204
|
},
|
|
205
|
+
inlineRefs: {
|
|
206
|
+
type: 'array',
|
|
207
|
+
items: INLINE_REF_SCHEMA,
|
|
208
|
+
description: 'Resolvable inline Rem references from the rendered title/headline. Markdown text preserves these as [[Target Title]].',
|
|
209
|
+
},
|
|
111
210
|
parentRemId: {
|
|
112
211
|
type: 'string',
|
|
113
212
|
description: 'Direct parent Rem ID (omitted for top-level Rems)',
|
|
@@ -116,6 +215,15 @@ export const SEARCH_TOOL = {
|
|
|
116
215
|
type: 'string',
|
|
117
216
|
description: 'Direct parent title/front text (omitted for top-level Rems)',
|
|
118
217
|
},
|
|
218
|
+
ancestors: {
|
|
219
|
+
type: 'array',
|
|
220
|
+
items: ANCESTOR_SCHEMA,
|
|
221
|
+
description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
|
|
222
|
+
},
|
|
223
|
+
ancestorsTruncated: {
|
|
224
|
+
type: 'boolean',
|
|
225
|
+
description: 'Whether the ancestor chain was capped by ancestorDepth',
|
|
226
|
+
},
|
|
119
227
|
aliases: {
|
|
120
228
|
type: 'array',
|
|
121
229
|
items: { type: 'string' },
|
|
@@ -130,17 +238,35 @@ export const SEARCH_TOOL = {
|
|
|
130
238
|
type: 'string',
|
|
131
239
|
description: 'Rem classification: document, dailyDocument, concept, descriptor, portal, or text',
|
|
132
240
|
},
|
|
241
|
+
matchedRems: {
|
|
242
|
+
type: 'array',
|
|
243
|
+
items: MATCHED_REM_SCHEMA,
|
|
244
|
+
description: 'For remnote_search_by_tag context mode, direct Rems carrying the requested tag that produced this context result',
|
|
245
|
+
},
|
|
246
|
+
contextRemId: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
description: 'For remnote_search_by_tag tagged mode, resolved ancestor/context Rem ID for this direct match',
|
|
249
|
+
},
|
|
250
|
+
contextTitle: {
|
|
251
|
+
type: 'string',
|
|
252
|
+
description: 'For remnote_search_by_tag tagged mode, resolved ancestor/context title for this direct match',
|
|
253
|
+
},
|
|
254
|
+
contextReason: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
enum: ['ancestor-document', 'ancestor-concept', 'ancestor-context', 'self'],
|
|
257
|
+
description: 'For remnote_search_by_tag tagged mode, why the context Rem was selected',
|
|
258
|
+
},
|
|
133
259
|
cardDirection: {
|
|
134
260
|
type: 'string',
|
|
135
261
|
description: 'Flashcard direction: forward, reverse, or bidirectional (omitted if not a flashcard)',
|
|
136
262
|
},
|
|
137
263
|
content: {
|
|
138
264
|
type: 'string',
|
|
139
|
-
description: 'Rendered markdown content of child subtree (only when
|
|
265
|
+
description: 'Rendered markdown content of child subtree (only when contentMode="markdown")',
|
|
140
266
|
},
|
|
141
267
|
contentStructured: {
|
|
142
268
|
type: 'array',
|
|
143
|
-
description: 'Structured child subtree with nested remIds and metadata (only when
|
|
269
|
+
description: 'Structured child subtree with nested remIds and metadata (only when contentMode="structured")',
|
|
144
270
|
items: {
|
|
145
271
|
type: 'object',
|
|
146
272
|
properties: {
|
|
@@ -150,6 +276,11 @@ export const SEARCH_TOOL = {
|
|
|
150
276
|
type: 'string',
|
|
151
277
|
description: 'Display-oriented child line with type-aware delimiter',
|
|
152
278
|
},
|
|
279
|
+
inlineRefs: {
|
|
280
|
+
type: 'array',
|
|
281
|
+
items: INLINE_REF_SCHEMA,
|
|
282
|
+
description: 'Resolvable inline Rem references from the child title/headline',
|
|
283
|
+
},
|
|
153
284
|
aliases: {
|
|
154
285
|
type: 'array',
|
|
155
286
|
items: { type: 'string' },
|
|
@@ -170,11 +301,11 @@ export const SEARCH_TOOL = {
|
|
|
170
301
|
},
|
|
171
302
|
children: {
|
|
172
303
|
type: 'array',
|
|
173
|
-
description: 'Nested child nodes (same shape recursively)',
|
|
304
|
+
description: 'Nested child nodes (same shape recursively; omitted for leaf nodes)',
|
|
174
305
|
items: { type: 'object' },
|
|
175
306
|
},
|
|
176
307
|
},
|
|
177
|
-
required: ['remId', 'title', 'headline', 'remType'
|
|
308
|
+
required: ['remId', 'title', 'headline', 'remType'],
|
|
178
309
|
},
|
|
179
310
|
},
|
|
180
311
|
contentProperties: {
|
|
@@ -198,12 +329,29 @@ export const SEARCH_TOOL = {
|
|
|
198
329
|
},
|
|
199
330
|
},
|
|
200
331
|
},
|
|
332
|
+
hasMore: {
|
|
333
|
+
type: 'boolean',
|
|
334
|
+
description: 'Whether more results are available through nextCursor',
|
|
335
|
+
},
|
|
336
|
+
nextCursor: {
|
|
337
|
+
type: 'string',
|
|
338
|
+
description: 'Opaque cursor for the next page when hasMore is true',
|
|
339
|
+
},
|
|
340
|
+
truncated: {
|
|
341
|
+
type: 'boolean',
|
|
342
|
+
description: 'Whether search results may be incomplete because the bridge hit its snapshot cap',
|
|
343
|
+
},
|
|
344
|
+
truncationReason: {
|
|
345
|
+
type: 'string',
|
|
346
|
+
enum: ['cursor_snapshot_limit'],
|
|
347
|
+
description: 'Reason search results may be incomplete when truncated is true',
|
|
348
|
+
},
|
|
201
349
|
},
|
|
202
350
|
},
|
|
203
351
|
};
|
|
204
352
|
export const SEARCH_BY_TAG_TOOL = {
|
|
205
353
|
name: 'remnote_search_by_tag',
|
|
206
|
-
description: 'Find notes by exact tag Rem ID
|
|
354
|
+
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
355
|
inputSchema: {
|
|
208
356
|
type: 'object',
|
|
209
357
|
properties: {
|
|
@@ -211,12 +359,26 @@ export const SEARCH_BY_TAG_TOOL = {
|
|
|
211
359
|
type: 'string',
|
|
212
360
|
description: 'Exact tag Rem ID to search',
|
|
213
361
|
},
|
|
362
|
+
resultMode: {
|
|
363
|
+
type: 'string',
|
|
364
|
+
enum: ['context', 'tagged'],
|
|
365
|
+
description: '"context" returns resolved ancestor context targets with matchedRems (default); "tagged" returns directly tagged Rems with context metadata',
|
|
366
|
+
},
|
|
214
367
|
limit: { type: 'number', description: 'Maximum results (1-150, default: 50)' },
|
|
215
|
-
|
|
368
|
+
contentMode: {
|
|
216
369
|
type: 'string',
|
|
217
370
|
enum: ['none', 'markdown', 'structured'],
|
|
218
371
|
description: 'Content rendering mode: "none" omits content (default), "markdown" renders child subtree as indented markdown, "structured" returns nested child objects with remIds',
|
|
219
372
|
},
|
|
373
|
+
view: {
|
|
374
|
+
type: 'string',
|
|
375
|
+
enum: ['compact', 'standard', 'full'],
|
|
376
|
+
description: 'Output detail level: compact, standard, or full',
|
|
377
|
+
},
|
|
378
|
+
ancestorDepth: {
|
|
379
|
+
type: 'number',
|
|
380
|
+
description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
|
|
381
|
+
},
|
|
220
382
|
depth: {
|
|
221
383
|
type: 'number',
|
|
222
384
|
description: 'Depth of child hierarchy to render for markdown/structured content (0-10, default: 1)',
|
|
@@ -229,14 +391,31 @@ export const SEARCH_BY_TAG_TOOL = {
|
|
|
229
391
|
type: 'number',
|
|
230
392
|
description: 'Maximum character length for rendered content (default: 3000)',
|
|
231
393
|
},
|
|
394
|
+
cursor: {
|
|
395
|
+
type: 'string',
|
|
396
|
+
description: 'Opaque cursor returned by a previous remnote_search_by_tag response',
|
|
397
|
+
},
|
|
398
|
+
timeoutMs: {
|
|
399
|
+
type: 'number',
|
|
400
|
+
description: 'Per-call bridge wait timeout in milliseconds (1-60000, default: 15000). Does not cancel plugin-side work.',
|
|
401
|
+
},
|
|
232
402
|
},
|
|
233
403
|
required: ['tagRemId'],
|
|
234
404
|
},
|
|
235
|
-
outputSchema:
|
|
405
|
+
outputSchema: {
|
|
406
|
+
type: 'object',
|
|
407
|
+
properties: {
|
|
408
|
+
results: SEARCH_TOOL.outputSchema.properties.results,
|
|
409
|
+
hasMore: SEARCH_TOOL.outputSchema.properties.hasMore,
|
|
410
|
+
nextCursor: SEARCH_TOOL.outputSchema.properties.nextCursor,
|
|
411
|
+
truncated: SEARCH_TOOL.outputSchema.properties.truncated,
|
|
412
|
+
truncationReason: SEARCH_TOOL.outputSchema.properties.truncationReason,
|
|
413
|
+
},
|
|
414
|
+
},
|
|
236
415
|
};
|
|
237
416
|
export const READ_NOTE_TOOL = {
|
|
238
417
|
name: 'remnote_read_note',
|
|
239
|
-
description: 'Read a specific note from RemNote by its Rem ID. For hierarchy traversal, prefer
|
|
418
|
+
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
419
|
inputSchema: {
|
|
241
420
|
type: 'object',
|
|
242
421
|
properties: {
|
|
@@ -245,11 +424,20 @@ export const READ_NOTE_TOOL = {
|
|
|
245
424
|
type: 'number',
|
|
246
425
|
description: 'Depth of child hierarchy to render (0-10, default: 5)',
|
|
247
426
|
},
|
|
248
|
-
|
|
427
|
+
contentMode: {
|
|
249
428
|
type: 'string',
|
|
250
429
|
enum: ['none', 'markdown', 'structured'],
|
|
251
430
|
description: 'Content rendering mode: "markdown" renders child subtree (default), "structured" returns nested child objects with remIds, "none" omits content',
|
|
252
431
|
},
|
|
432
|
+
view: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
enum: ['compact', 'standard', 'full'],
|
|
435
|
+
description: 'Output detail level: compact, standard, or full',
|
|
436
|
+
},
|
|
437
|
+
ancestorDepth: {
|
|
438
|
+
type: 'number',
|
|
439
|
+
description: 'Number of parent Rems to include, direct parent first (0-20, default: 0)',
|
|
440
|
+
},
|
|
253
441
|
childLimit: {
|
|
254
442
|
type: 'number',
|
|
255
443
|
description: 'Maximum children per level (1-500, default: 100)',
|
|
@@ -258,6 +446,10 @@ export const READ_NOTE_TOOL = {
|
|
|
258
446
|
type: 'number',
|
|
259
447
|
description: 'Maximum character length for rendered content (default: 100000)',
|
|
260
448
|
},
|
|
449
|
+
includeMediaMetadata: {
|
|
450
|
+
type: 'boolean',
|
|
451
|
+
description: 'Include ordered image metadata from the root Rem text and backText fields (default: false)',
|
|
452
|
+
},
|
|
261
453
|
},
|
|
262
454
|
required: ['remId'],
|
|
263
455
|
},
|
|
@@ -270,6 +462,11 @@ export const READ_NOTE_TOOL = {
|
|
|
270
462
|
type: 'string',
|
|
271
463
|
description: 'Display-oriented full line: title + type-aware delimiter + detail (e.g. "Term :: Definition")',
|
|
272
464
|
},
|
|
465
|
+
inlineRefs: {
|
|
466
|
+
type: 'array',
|
|
467
|
+
items: INLINE_REF_SCHEMA,
|
|
468
|
+
description: 'Resolvable inline Rem references from the rendered title/headline. Markdown text preserves these as [[Target Title]].',
|
|
469
|
+
},
|
|
273
470
|
parentRemId: {
|
|
274
471
|
type: 'string',
|
|
275
472
|
description: 'Direct parent Rem ID (omitted for top-level Rems)',
|
|
@@ -278,6 +475,15 @@ export const READ_NOTE_TOOL = {
|
|
|
278
475
|
type: 'string',
|
|
279
476
|
description: 'Direct parent title/front text (omitted for top-level Rems)',
|
|
280
477
|
},
|
|
478
|
+
ancestors: {
|
|
479
|
+
type: 'array',
|
|
480
|
+
items: ANCESTOR_SCHEMA,
|
|
481
|
+
description: 'Parent-first ancestor chain when ancestorDepth is greater than zero',
|
|
482
|
+
},
|
|
483
|
+
ancestorsTruncated: {
|
|
484
|
+
type: 'boolean',
|
|
485
|
+
description: 'Whether the ancestor chain was capped by ancestorDepth',
|
|
486
|
+
},
|
|
281
487
|
aliases: {
|
|
282
488
|
type: 'array',
|
|
283
489
|
items: { type: 'string' },
|
|
@@ -298,11 +504,11 @@ export const READ_NOTE_TOOL = {
|
|
|
298
504
|
},
|
|
299
505
|
content: {
|
|
300
506
|
type: 'string',
|
|
301
|
-
description: 'Rendered markdown content of child subtree (when
|
|
507
|
+
description: 'Rendered markdown content of child subtree (when contentMode="markdown", which is the default)',
|
|
302
508
|
},
|
|
303
509
|
contentStructured: {
|
|
304
510
|
type: 'array',
|
|
305
|
-
description: 'Structured child subtree with nested remIds and metadata (only when
|
|
511
|
+
description: 'Structured child subtree with nested remIds and metadata (only when contentMode="structured")',
|
|
306
512
|
items: {
|
|
307
513
|
type: 'object',
|
|
308
514
|
properties: {
|
|
@@ -312,6 +518,11 @@ export const READ_NOTE_TOOL = {
|
|
|
312
518
|
type: 'string',
|
|
313
519
|
description: 'Display-oriented child line with type-aware delimiter',
|
|
314
520
|
},
|
|
521
|
+
inlineRefs: {
|
|
522
|
+
type: 'array',
|
|
523
|
+
items: INLINE_REF_SCHEMA,
|
|
524
|
+
description: 'Resolvable inline Rem references from the child title/headline',
|
|
525
|
+
},
|
|
315
526
|
aliases: {
|
|
316
527
|
type: 'array',
|
|
317
528
|
items: { type: 'string' },
|
|
@@ -332,11 +543,11 @@ export const READ_NOTE_TOOL = {
|
|
|
332
543
|
},
|
|
333
544
|
children: {
|
|
334
545
|
type: 'array',
|
|
335
|
-
description: 'Nested child nodes (same shape recursively)',
|
|
546
|
+
description: 'Nested child nodes (same shape recursively; omitted for leaf nodes)',
|
|
336
547
|
items: { type: 'object' },
|
|
337
548
|
},
|
|
338
549
|
},
|
|
339
|
-
required: ['remId', 'title', 'headline', 'remType'
|
|
550
|
+
required: ['remId', 'title', 'headline', 'remType'],
|
|
340
551
|
},
|
|
341
552
|
},
|
|
342
553
|
contentProperties: {
|
|
@@ -357,17 +568,193 @@ export const READ_NOTE_TOOL = {
|
|
|
357
568
|
},
|
|
358
569
|
},
|
|
359
570
|
},
|
|
571
|
+
media: {
|
|
572
|
+
type: 'array',
|
|
573
|
+
description: 'Ordered root-Rem image metadata from text followed by backText when includeMediaMetadata is true',
|
|
574
|
+
items: {
|
|
575
|
+
type: 'object',
|
|
576
|
+
properties: {
|
|
577
|
+
mediaId: { type: 'string' },
|
|
578
|
+
kind: { type: 'string', enum: ['image'] },
|
|
579
|
+
field: { type: 'string', enum: ['text', 'backText'] },
|
|
580
|
+
elementIndex: { type: 'number' },
|
|
581
|
+
imageIndex: { type: 'number' },
|
|
582
|
+
imgId: { type: 'string' },
|
|
583
|
+
title: { type: 'string' },
|
|
584
|
+
dimensions: {
|
|
585
|
+
type: 'object',
|
|
586
|
+
properties: { width: { type: 'number' }, height: { type: 'number' } },
|
|
587
|
+
required: ['width', 'height'],
|
|
588
|
+
},
|
|
589
|
+
mimeType: { type: 'string' },
|
|
590
|
+
source: { type: 'string', enum: ['remnote_managed_local'] },
|
|
591
|
+
},
|
|
592
|
+
required: ['mediaId', 'kind', 'field', 'elementIndex', 'imageIndex'],
|
|
593
|
+
},
|
|
594
|
+
},
|
|
360
595
|
},
|
|
361
596
|
},
|
|
362
597
|
};
|
|
598
|
+
export const GET_MEDIA_TOOL = {
|
|
599
|
+
name: 'remnote_get_media',
|
|
600
|
+
description: 'Retrieve a RemNote-managed local image as MCP-native image content. First call remnote_read_note with includeMediaMetadata=true, then pass the returned remId, field, and mediaId. Requires bridge capability media.images.v1. External URL retrieval is not supported.',
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: 'object',
|
|
603
|
+
properties: {
|
|
604
|
+
remId: { type: 'string', description: 'Root Rem ID containing the image' },
|
|
605
|
+
field: {
|
|
606
|
+
type: 'string',
|
|
607
|
+
enum: ['text', 'backText'],
|
|
608
|
+
description: 'Rich-text field containing the image',
|
|
609
|
+
},
|
|
610
|
+
mediaId: {
|
|
611
|
+
type: 'string',
|
|
612
|
+
description: 'Stable media ID returned by remnote_read_note',
|
|
613
|
+
},
|
|
614
|
+
maxInlineBytes: {
|
|
615
|
+
type: 'number',
|
|
616
|
+
description: 'Maximum bytes to inline (default 5 MiB, hard maximum 10 MiB)',
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
required: ['remId', 'field', 'mediaId'],
|
|
620
|
+
additionalProperties: false,
|
|
621
|
+
},
|
|
622
|
+
outputSchema: {
|
|
623
|
+
type: 'object',
|
|
624
|
+
properties: {
|
|
625
|
+
remId: { type: 'string' },
|
|
626
|
+
mediaId: { type: 'string' },
|
|
627
|
+
kind: { type: 'string', enum: ['image'] },
|
|
628
|
+
field: { type: 'string', enum: ['text', 'backText'] },
|
|
629
|
+
elementIndex: { type: 'number' },
|
|
630
|
+
imageIndex: { type: 'number' },
|
|
631
|
+
imgId: { type: 'string' },
|
|
632
|
+
title: { type: 'string' },
|
|
633
|
+
dimensions: {
|
|
634
|
+
type: 'object',
|
|
635
|
+
properties: { width: { type: 'number' }, height: { type: 'number' } },
|
|
636
|
+
required: ['width', 'height'],
|
|
637
|
+
},
|
|
638
|
+
mimeType: { type: 'string' },
|
|
639
|
+
source: { type: 'string', enum: ['remnote_managed_local'] },
|
|
640
|
+
sizeBytes: { type: 'number' },
|
|
641
|
+
},
|
|
642
|
+
required: [
|
|
643
|
+
'remId',
|
|
644
|
+
'mediaId',
|
|
645
|
+
'kind',
|
|
646
|
+
'field',
|
|
647
|
+
'elementIndex',
|
|
648
|
+
'imageIndex',
|
|
649
|
+
'mimeType',
|
|
650
|
+
'source',
|
|
651
|
+
'sizeBytes',
|
|
652
|
+
],
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
export const LIST_CHILDREN_TOOL = {
|
|
656
|
+
name: 'remnote_list_children',
|
|
657
|
+
description: 'List direct child Rems under a parent without rendering a subtree. Use for cheap hierarchy traversal; page with nextCursor when hasMore is true.',
|
|
658
|
+
inputSchema: {
|
|
659
|
+
type: 'object',
|
|
660
|
+
properties: {
|
|
661
|
+
parentRemId: { type: 'string', description: 'Parent Rem ID whose direct children to list' },
|
|
662
|
+
limit: { type: 'number', description: 'Maximum direct children (1-150, default: 50)' },
|
|
663
|
+
cursor: {
|
|
664
|
+
type: 'string',
|
|
665
|
+
description: 'Opaque cursor returned by a previous remnote_list_children response',
|
|
666
|
+
},
|
|
667
|
+
view: {
|
|
668
|
+
type: 'string',
|
|
669
|
+
enum: ['compact', 'standard', 'full'],
|
|
670
|
+
description: 'Output detail level for child metadata: compact, standard, or full',
|
|
671
|
+
},
|
|
672
|
+
ancestorDepth: {
|
|
673
|
+
type: 'number',
|
|
674
|
+
description: 'Number of parent Rems to include for each child, direct parent first',
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
required: ['parentRemId'],
|
|
678
|
+
},
|
|
679
|
+
outputSchema: {
|
|
680
|
+
type: 'object',
|
|
681
|
+
properties: {
|
|
682
|
+
children: {
|
|
683
|
+
type: 'array',
|
|
684
|
+
items: SEARCH_TOOL.outputSchema.properties.results.items,
|
|
685
|
+
},
|
|
686
|
+
hasMore: SEARCH_TOOL.outputSchema.properties.hasMore,
|
|
687
|
+
nextCursor: SEARCH_TOOL.outputSchema.properties.nextCursor,
|
|
688
|
+
totalChildren: {
|
|
689
|
+
type: 'number',
|
|
690
|
+
description: 'Total direct children observed when listing this parent',
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
};
|
|
695
|
+
export const MOVE_NOTE_TOOL = {
|
|
696
|
+
name: 'remnote_move_note',
|
|
697
|
+
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.',
|
|
698
|
+
inputSchema: {
|
|
699
|
+
type: 'object',
|
|
700
|
+
properties: {
|
|
701
|
+
remId: { type: 'string', description: 'Rem ID to move' },
|
|
702
|
+
newParentRemId: { type: 'string', description: 'New parent Rem ID' },
|
|
703
|
+
position: {
|
|
704
|
+
type: 'string',
|
|
705
|
+
enum: ['first', 'last', 'before', 'after'],
|
|
706
|
+
description: 'Where to place the moved Rem under the new parent (default: last)',
|
|
707
|
+
},
|
|
708
|
+
siblingRemId: {
|
|
709
|
+
type: 'string',
|
|
710
|
+
description: 'Sibling Rem ID required for before/after positioning',
|
|
711
|
+
},
|
|
712
|
+
dryRun: {
|
|
713
|
+
type: 'boolean',
|
|
714
|
+
description: 'Preview without mutating RemNote (default: true)',
|
|
715
|
+
},
|
|
716
|
+
expectedOldParentRemId: {
|
|
717
|
+
type: 'string',
|
|
718
|
+
description: 'Reject if the current direct parent differs from this Rem ID',
|
|
719
|
+
},
|
|
720
|
+
ancestorDepth: {
|
|
721
|
+
type: 'number',
|
|
722
|
+
description: 'Number of parent Rems to include before/after the move',
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
required: ['remId', 'newParentRemId'],
|
|
726
|
+
},
|
|
727
|
+
outputSchema: {
|
|
728
|
+
type: 'object',
|
|
729
|
+
properties: {
|
|
730
|
+
remId: { type: 'string' },
|
|
731
|
+
title: { type: 'string' },
|
|
732
|
+
dryRun: { type: 'boolean' },
|
|
733
|
+
oldParentRemId: { type: 'string' },
|
|
734
|
+
oldParentTitle: { type: 'string' },
|
|
735
|
+
newParentRemId: { type: 'string' },
|
|
736
|
+
newParentTitle: { type: 'string' },
|
|
737
|
+
position: { type: 'string', enum: ['first', 'last', 'before', 'after'] },
|
|
738
|
+
siblingRemId: { type: 'string' },
|
|
739
|
+
ancestorsBefore: { type: 'array', items: ANCESTOR_SCHEMA },
|
|
740
|
+
ancestorsBeforeTruncated: { type: 'boolean' },
|
|
741
|
+
ancestorsAfter: { type: 'array', items: ANCESTOR_SCHEMA },
|
|
742
|
+
ancestorsAfterTruncated: { type: 'boolean' },
|
|
743
|
+
},
|
|
744
|
+
required: ['remId', 'title', 'dryRun', 'newParentRemId', 'newParentTitle', 'position'],
|
|
745
|
+
},
|
|
746
|
+
};
|
|
363
747
|
export const UPDATE_NOTE_TOOL = {
|
|
364
748
|
name: 'remnote_update_note',
|
|
365
|
-
description: 'Update note metadata in RemNote. Use this tool for title changes only.',
|
|
749
|
+
description: 'Update note metadata in RemNote. Use this tool for title changes only. The title supports exact Rem references as [[id:<remId>]].',
|
|
366
750
|
inputSchema: {
|
|
367
751
|
type: 'object',
|
|
368
752
|
properties: {
|
|
369
753
|
remId: { type: 'string', description: 'The Rem ID to update' },
|
|
370
|
-
title: {
|
|
754
|
+
title: {
|
|
755
|
+
type: 'string',
|
|
756
|
+
description: 'New title. Use [[id:<remId>]] for exact Rem references.',
|
|
757
|
+
},
|
|
371
758
|
},
|
|
372
759
|
required: ['remId', 'title'],
|
|
373
760
|
additionalProperties: false,
|
|
@@ -389,6 +776,100 @@ export const UPDATE_NOTE_TOOL = {
|
|
|
389
776
|
required: ['remIds', 'titles'],
|
|
390
777
|
},
|
|
391
778
|
};
|
|
779
|
+
export const SET_DOCUMENT_STATUS_TOOL = {
|
|
780
|
+
name: 'remnote_set_document_status',
|
|
781
|
+
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.',
|
|
782
|
+
inputSchema: {
|
|
783
|
+
type: 'object',
|
|
784
|
+
properties: {
|
|
785
|
+
remId: { type: 'string', description: 'The Rem ID whose document status should change' },
|
|
786
|
+
isDocument: {
|
|
787
|
+
type: 'boolean',
|
|
788
|
+
description: 'Whether the Rem should be marked as a document',
|
|
789
|
+
},
|
|
790
|
+
dryRun: {
|
|
791
|
+
type: 'boolean',
|
|
792
|
+
description: 'Preview the document-status change without mutating RemNote (default: true)',
|
|
793
|
+
},
|
|
794
|
+
expectedOldRemType: {
|
|
795
|
+
type: 'string',
|
|
796
|
+
enum: ['document', 'dailyDocument', 'concept', 'descriptor', 'portal', 'text'],
|
|
797
|
+
description: 'Optional stale-context guard; reject if current remType differs from this value',
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
required: ['remId', 'isDocument'],
|
|
801
|
+
additionalProperties: false,
|
|
802
|
+
},
|
|
803
|
+
outputSchema: {
|
|
804
|
+
type: 'object',
|
|
805
|
+
properties: {
|
|
806
|
+
remId: { type: 'string', description: 'The Rem ID that was checked or changed' },
|
|
807
|
+
title: { type: 'string', description: 'Rendered title/front text' },
|
|
808
|
+
oldRemType: {
|
|
809
|
+
type: 'string',
|
|
810
|
+
description: 'Bridge remType before the requested document-status change',
|
|
811
|
+
},
|
|
812
|
+
newRemType: {
|
|
813
|
+
type: 'string',
|
|
814
|
+
description: 'Bridge remType after the change, or previewed remType for dry runs',
|
|
815
|
+
},
|
|
816
|
+
oldIsDocument: {
|
|
817
|
+
type: 'boolean',
|
|
818
|
+
description: 'Document status before the requested change',
|
|
819
|
+
},
|
|
820
|
+
newIsDocument: {
|
|
821
|
+
type: 'boolean',
|
|
822
|
+
description: 'Document status after the change, or previewed status for dry runs',
|
|
823
|
+
},
|
|
824
|
+
requestedIsDocument: {
|
|
825
|
+
type: 'boolean',
|
|
826
|
+
description: 'Requested document status',
|
|
827
|
+
},
|
|
828
|
+
dryRun: {
|
|
829
|
+
type: 'boolean',
|
|
830
|
+
description: 'Whether the request was only a preview',
|
|
831
|
+
},
|
|
832
|
+
changed: {
|
|
833
|
+
type: 'boolean',
|
|
834
|
+
description: 'Whether RemNote was actually mutated by this call',
|
|
835
|
+
},
|
|
836
|
+
wouldChange: {
|
|
837
|
+
type: 'boolean',
|
|
838
|
+
description: 'Whether the requested status differs from the current status',
|
|
839
|
+
},
|
|
840
|
+
sdkSupportsDocumentStatus: {
|
|
841
|
+
type: 'boolean',
|
|
842
|
+
description: 'Whether the bridge runtime exposes a safe SDK document-status setter',
|
|
843
|
+
},
|
|
844
|
+
warnings: {
|
|
845
|
+
type: 'array',
|
|
846
|
+
items: { type: 'string' },
|
|
847
|
+
description: 'Important side-effect notes, especially around preserved card status',
|
|
848
|
+
},
|
|
849
|
+
cardDirectionBefore: {
|
|
850
|
+
type: 'string',
|
|
851
|
+
description: 'Card direction before the change when available',
|
|
852
|
+
},
|
|
853
|
+
cardDirectionAfter: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: 'Card direction after the change when available',
|
|
856
|
+
},
|
|
857
|
+
},
|
|
858
|
+
required: [
|
|
859
|
+
'remId',
|
|
860
|
+
'title',
|
|
861
|
+
'oldRemType',
|
|
862
|
+
'newRemType',
|
|
863
|
+
'oldIsDocument',
|
|
864
|
+
'newIsDocument',
|
|
865
|
+
'requestedIsDocument',
|
|
866
|
+
'dryRun',
|
|
867
|
+
'changed',
|
|
868
|
+
'wouldChange',
|
|
869
|
+
'sdkSupportsDocumentStatus',
|
|
870
|
+
],
|
|
871
|
+
},
|
|
872
|
+
};
|
|
392
873
|
export const INSERT_CHILDREN_TOOL = {
|
|
393
874
|
name: 'remnote_insert_children',
|
|
394
875
|
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.',
|
|
@@ -399,7 +880,10 @@ export const INSERT_CHILDREN_TOOL = {
|
|
|
399
880
|
type: 'string',
|
|
400
881
|
description: 'Parent Rem ID that will receive the new children',
|
|
401
882
|
},
|
|
402
|
-
content: {
|
|
883
|
+
content: {
|
|
884
|
+
type: 'string',
|
|
885
|
+
description: 'Markdown content to insert as child Rems. Use [[id:<remId>]] for exact Rem references.',
|
|
886
|
+
},
|
|
403
887
|
position: {
|
|
404
888
|
type: 'string',
|
|
405
889
|
enum: ['first', 'last', 'before', 'after'],
|
|
@@ -427,7 +911,7 @@ export const REPLACE_CHILDREN_TOOL = {
|
|
|
427
911
|
},
|
|
428
912
|
content: {
|
|
429
913
|
type: 'string',
|
|
430
|
-
description: 'Markdown content to use as replacement children; empty string clears all direct children',
|
|
914
|
+
description: 'Markdown content to use as replacement children; empty string clears all direct children. Use [[id:<remId>]] for exact Rem references.',
|
|
431
915
|
},
|
|
432
916
|
},
|
|
433
917
|
required: ['parentRemId', 'content'],
|
|
@@ -460,6 +944,77 @@ export const UPDATE_TAGS_TOOL = {
|
|
|
460
944
|
},
|
|
461
945
|
outputSchema: UPDATE_NOTE_TOOL.outputSchema,
|
|
462
946
|
};
|
|
947
|
+
export const SET_PROPERTY_TOOL = {
|
|
948
|
+
name: 'remnote_set_property',
|
|
949
|
+
description: 'Set or clear a tag/table property value on a Rem by exact IDs. The bridge verifies the property belongs to the supplied tag/table Rem, adds the tag idempotently, then writes the property value. For select properties, pass the option Rem ID as value.kind="rem_reference".',
|
|
950
|
+
inputSchema: {
|
|
951
|
+
type: 'object',
|
|
952
|
+
properties: {
|
|
953
|
+
remId: { type: 'string', description: 'The Rem ID whose tag property should be set' },
|
|
954
|
+
tagRemId: {
|
|
955
|
+
type: 'string',
|
|
956
|
+
description: 'Exact tag/table Rem ID that owns the property',
|
|
957
|
+
},
|
|
958
|
+
propertyRemId: {
|
|
959
|
+
type: 'string',
|
|
960
|
+
description: 'Exact property Rem ID under the tag/table Rem',
|
|
961
|
+
},
|
|
962
|
+
value: {
|
|
963
|
+
oneOf: [
|
|
964
|
+
{
|
|
965
|
+
type: 'object',
|
|
966
|
+
properties: {
|
|
967
|
+
kind: { type: 'string', const: 'text' },
|
|
968
|
+
text: {
|
|
969
|
+
type: 'string',
|
|
970
|
+
description: 'Plain text or markdown property value. Use [[id:<remId>]] for exact Rem references.',
|
|
971
|
+
},
|
|
972
|
+
},
|
|
973
|
+
required: ['kind', 'text'],
|
|
974
|
+
additionalProperties: false,
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
type: 'object',
|
|
978
|
+
properties: {
|
|
979
|
+
kind: { type: 'string', const: 'rem_reference' },
|
|
980
|
+
remId: {
|
|
981
|
+
type: 'string',
|
|
982
|
+
description: 'Referenced Rem ID; use select-option Rem IDs here too',
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
required: ['kind', 'remId'],
|
|
986
|
+
additionalProperties: false,
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: 'object',
|
|
990
|
+
properties: {
|
|
991
|
+
kind: { type: 'string', const: 'clear' },
|
|
992
|
+
},
|
|
993
|
+
required: ['kind'],
|
|
994
|
+
additionalProperties: false,
|
|
995
|
+
},
|
|
996
|
+
],
|
|
997
|
+
description: 'Property value. Use text for plain values, rem_reference for Rem references and select options, or clear to remove the value.',
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
required: ['remId', 'tagRemId', 'propertyRemId', 'value'],
|
|
1001
|
+
additionalProperties: false,
|
|
1002
|
+
},
|
|
1003
|
+
outputSchema: {
|
|
1004
|
+
type: 'object',
|
|
1005
|
+
properties: {
|
|
1006
|
+
remId: { type: 'string', description: 'The Rem ID whose property was set' },
|
|
1007
|
+
tagRemId: { type: 'string', description: 'Exact tag/table Rem ID' },
|
|
1008
|
+
propertyRemId: { type: 'string', description: 'Exact property Rem ID' },
|
|
1009
|
+
valueKind: {
|
|
1010
|
+
type: 'string',
|
|
1011
|
+
enum: ['text', 'rem_reference', 'clear'],
|
|
1012
|
+
description: 'The value payload kind applied by the bridge',
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
required: ['remId', 'tagRemId', 'propertyRemId', 'valueKind'],
|
|
1016
|
+
},
|
|
1017
|
+
};
|
|
463
1018
|
export const APPEND_JOURNAL_TOOL = {
|
|
464
1019
|
name: 'remnote_append_journal',
|
|
465
1020
|
description: "Append content to today's daily document in RemNote with optional exact tag Rem IDs. Recommended preflight once per session: remnote_status.",
|
|
@@ -468,7 +1023,7 @@ export const APPEND_JOURNAL_TOOL = {
|
|
|
468
1023
|
properties: {
|
|
469
1024
|
content: {
|
|
470
1025
|
type: 'string',
|
|
471
|
-
description: "Content to append to today's daily document (markdown supported)",
|
|
1026
|
+
description: "Content to append to today's daily document (markdown supported). Use [[id:<remId>]] for exact Rem references.",
|
|
472
1027
|
},
|
|
473
1028
|
timestamp: { type: 'boolean', description: 'Include timestamp (default: true)' },
|
|
474
1029
|
tagRemIds: {
|
|
@@ -510,6 +1065,11 @@ export const STATUS_TOOL = {
|
|
|
510
1065
|
connected: { type: 'boolean', description: 'Whether bridge plugin is currently connected' },
|
|
511
1066
|
serverVersion: { type: 'string', description: 'MCP server version' },
|
|
512
1067
|
pluginVersion: { type: 'string', description: 'Connected bridge plugin version' },
|
|
1068
|
+
capabilities: {
|
|
1069
|
+
type: 'array',
|
|
1070
|
+
items: { type: 'string' },
|
|
1071
|
+
description: 'Bridge protocol capabilities advertised during handshake',
|
|
1072
|
+
},
|
|
513
1073
|
version_warning: {
|
|
514
1074
|
type: 'string',
|
|
515
1075
|
description: 'Compatibility warning when server/bridge versions differ',
|
|
@@ -634,7 +1194,8 @@ export const PLAYBOOK_TOOL = {
|
|
|
634
1194
|
orientation: {
|
|
635
1195
|
type: 'object',
|
|
636
1196
|
properties: {
|
|
637
|
-
|
|
1197
|
+
contentMode: { type: 'string' },
|
|
1198
|
+
view: { type: 'string' },
|
|
638
1199
|
depth: { type: 'number' },
|
|
639
1200
|
childLimit: { type: 'number' },
|
|
640
1201
|
},
|
|
@@ -670,21 +1231,27 @@ export const ALL_TOOLS = [
|
|
|
670
1231
|
SEARCH_TOOL,
|
|
671
1232
|
SEARCH_BY_TAG_TOOL,
|
|
672
1233
|
READ_NOTE_TOOL,
|
|
1234
|
+
GET_MEDIA_TOOL,
|
|
1235
|
+
LIST_CHILDREN_TOOL,
|
|
673
1236
|
UPDATE_NOTE_TOOL,
|
|
1237
|
+
SET_DOCUMENT_STATUS_TOOL,
|
|
1238
|
+
MOVE_NOTE_TOOL,
|
|
674
1239
|
INSERT_CHILDREN_TOOL,
|
|
675
1240
|
REPLACE_CHILDREN_TOOL,
|
|
676
1241
|
UPDATE_TAGS_TOOL,
|
|
1242
|
+
SET_PROPERTY_TOOL,
|
|
677
1243
|
APPEND_JOURNAL_TOOL,
|
|
678
1244
|
PLAYBOOK_TOOL,
|
|
679
1245
|
STATUS_TOOL,
|
|
680
1246
|
READ_TABLE_TOOL,
|
|
681
1247
|
];
|
|
682
|
-
export function registerAllTools(server, wsServer, logger) {
|
|
1248
|
+
export function registerAllTools(server, wsServer, logger, mediaRoots = []) {
|
|
683
1249
|
const toolLogger = logger.child({ context: 'tools' });
|
|
684
1250
|
async function buildStatusResult() {
|
|
685
1251
|
const connected = wsServer.isConnected();
|
|
686
1252
|
const serverVersion = wsServer.getServerVersion();
|
|
687
1253
|
const bridgeVersion = wsServer.getBridgeVersion();
|
|
1254
|
+
const bridgeCapabilities = wsServer.getBridgeCapabilities?.() ?? [];
|
|
688
1255
|
if (!connected) {
|
|
689
1256
|
return { connected: false, serverVersion, message: 'RemNote plugin not connected' };
|
|
690
1257
|
}
|
|
@@ -701,6 +1268,7 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
701
1268
|
connected: true,
|
|
702
1269
|
serverVersion,
|
|
703
1270
|
...statusObj,
|
|
1271
|
+
capabilities: bridgeCapabilities,
|
|
704
1272
|
...(versionWarning ? { version_warning: versionWarning } : {}),
|
|
705
1273
|
};
|
|
706
1274
|
}
|
|
@@ -711,6 +1279,7 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
711
1279
|
toolLogger.debug({ tool: toolName, args: request.params.arguments }, 'Executing tool');
|
|
712
1280
|
try {
|
|
713
1281
|
let result;
|
|
1282
|
+
let nativeToolResult;
|
|
714
1283
|
switch (toolName) {
|
|
715
1284
|
case 'remnote_create_note': {
|
|
716
1285
|
const args = CreateNoteSchema.parse(request.params.arguments);
|
|
@@ -724,7 +1293,8 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
724
1293
|
}
|
|
725
1294
|
case 'remnote_search_by_tag': {
|
|
726
1295
|
const args = SearchByTagSchema.parse(request.params.arguments);
|
|
727
|
-
|
|
1296
|
+
const { timeoutMs, ...payload } = args;
|
|
1297
|
+
result = await wsServer.sendRequest('search_by_tag', payload, timeoutMs);
|
|
728
1298
|
break;
|
|
729
1299
|
}
|
|
730
1300
|
case 'remnote_read_note': {
|
|
@@ -732,16 +1302,62 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
732
1302
|
result = await wsServer.sendRequest('read_note', args);
|
|
733
1303
|
break;
|
|
734
1304
|
}
|
|
1305
|
+
case 'remnote_get_media': {
|
|
1306
|
+
const args = GetMediaSchema.parse(request.params.arguments);
|
|
1307
|
+
if (!wsServer.hasBridgeCapability(MEDIA_CAPABILITY)) {
|
|
1308
|
+
throw new Error(`Bridge capability/version mismatch: connected bridge does not advertise ${MEDIA_CAPABILITY}`);
|
|
1309
|
+
}
|
|
1310
|
+
const locator = parseMediaLocator(await wsServer.sendRequest('get_media_locator', {
|
|
1311
|
+
remId: args.remId,
|
|
1312
|
+
field: args.field,
|
|
1313
|
+
mediaId: args.mediaId,
|
|
1314
|
+
}));
|
|
1315
|
+
if (locator.remId !== args.remId ||
|
|
1316
|
+
locator.field !== args.field ||
|
|
1317
|
+
locator.mediaId !== args.mediaId) {
|
|
1318
|
+
throw new Error('Invalid media locator payload received from bridge: request identity mismatch');
|
|
1319
|
+
}
|
|
1320
|
+
const media = await resolveManagedImage(locator, mediaRoots, args.maxInlineBytes);
|
|
1321
|
+
const metadata = {
|
|
1322
|
+
...media.metadata,
|
|
1323
|
+
mimeType: media.mimeType,
|
|
1324
|
+
sizeBytes: media.sizeBytes,
|
|
1325
|
+
};
|
|
1326
|
+
nativeToolResult = {
|
|
1327
|
+
structuredContent: metadata,
|
|
1328
|
+
content: [
|
|
1329
|
+
{ type: 'image', data: media.data, mimeType: media.mimeType },
|
|
1330
|
+
{ type: 'text', text: JSON.stringify(metadata) },
|
|
1331
|
+
],
|
|
1332
|
+
};
|
|
1333
|
+
result = metadata;
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
case 'remnote_list_children': {
|
|
1337
|
+
const args = ListChildrenSchema.parse(request.params.arguments);
|
|
1338
|
+
result = await wsServer.sendRequest('list_children', args);
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
735
1341
|
case 'remnote_update_note': {
|
|
736
1342
|
const args = UpdateNoteSchema.parse(request.params.arguments);
|
|
737
1343
|
result = await wsServer.sendRequest('update_note', args);
|
|
738
1344
|
break;
|
|
739
1345
|
}
|
|
1346
|
+
case 'remnote_set_document_status': {
|
|
1347
|
+
const args = SetDocumentStatusSchema.parse(request.params.arguments);
|
|
1348
|
+
result = await wsServer.sendRequest('set_document_status', args);
|
|
1349
|
+
break;
|
|
1350
|
+
}
|
|
740
1351
|
case 'remnote_insert_children': {
|
|
741
1352
|
const args = InsertChildrenSchema.parse(request.params.arguments);
|
|
742
1353
|
result = await wsServer.sendRequest('insert_children', args);
|
|
743
1354
|
break;
|
|
744
1355
|
}
|
|
1356
|
+
case 'remnote_move_note': {
|
|
1357
|
+
const args = MoveNoteSchema.parse(request.params.arguments);
|
|
1358
|
+
result = await wsServer.sendRequest('move_note', args);
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
745
1361
|
case 'remnote_replace_children': {
|
|
746
1362
|
const args = ReplaceChildrenSchema.parse(request.params.arguments);
|
|
747
1363
|
result = await wsServer.sendRequest('replace_children', args);
|
|
@@ -752,6 +1368,11 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
752
1368
|
result = await wsServer.sendRequest('update_tags', args);
|
|
753
1369
|
break;
|
|
754
1370
|
}
|
|
1371
|
+
case 'remnote_set_property': {
|
|
1372
|
+
const args = SetPropertySchema.parse(request.params.arguments);
|
|
1373
|
+
result = await wsServer.sendRequest('set_property', args);
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
755
1376
|
case 'remnote_append_journal': {
|
|
756
1377
|
const args = AppendJournalSchema.parse(request.params.arguments);
|
|
757
1378
|
result = await wsServer.sendRequest('append_journal', args);
|
|
@@ -769,45 +1390,65 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
769
1390
|
};
|
|
770
1391
|
}
|
|
771
1392
|
result = {
|
|
772
|
-
playbookVersion: '1.
|
|
773
|
-
summary: 'Use this playbook to
|
|
1393
|
+
playbookVersion: '1.8.0',
|
|
1394
|
+
summary: 'Use this playbook to check RemNote connection and write gates, navigate by remId with paged search/read/list workflows, retrieve managed images through capability-gated metadata, choose compact/full output views, and apply safe exact-ID writes including inline [[id:<remId>]] references, tag property values, and dry-run-first document status changes.',
|
|
774
1395
|
recommendedStatusCheck: {
|
|
775
1396
|
tool: 'remnote_status',
|
|
776
1397
|
cadence: 'recommended once per session and before risky writes',
|
|
777
|
-
rationale: 'status exposes connection health, version compatibility warnings,
|
|
1398
|
+
rationale: 'status exposes connection health, version compatibility warnings, write-policy gates, and bridge capabilities',
|
|
778
1399
|
},
|
|
779
1400
|
decisionTree: [
|
|
780
1401
|
'Need connection/capability context? Call remnote_status first.',
|
|
781
|
-
'Need
|
|
782
|
-
'Need
|
|
783
|
-
'Need
|
|
1402
|
+
'Need an embedded RemNote-managed image? Confirm media.images.v1, call remnote_read_note with includeMediaMetadata=true, then call remnote_get_media with the returned remId, field, and mediaId.',
|
|
1403
|
+
'Need to orient across the KB? Use remnote_search with contentMode="structured", view="compact", depth=1, childLimit=500.',
|
|
1404
|
+
'Need broad search enumeration? Continue remnote_search or remnote_search_by_tag with nextCursor while hasMore is true.',
|
|
1405
|
+
'Need to search within a specific branch? Use remnote_search with parentRemId; keep the same parentRemId when continuing with nextCursor.',
|
|
1406
|
+
'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.',
|
|
1407
|
+
'Need hierarchy placement context? Add ancestorDepth, typically 5, to search/read/search_by_tag/list_children; ancestors are direct-parent first.',
|
|
1408
|
+
'Need strict tag verification? Use remnote_search_by_tag with resultMode="tagged", or verify the exact Rem in matchedRems from context mode.',
|
|
1409
|
+
'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.',
|
|
1410
|
+
'Need to traverse a specific branch cheaply? Use remnote_list_children on the parentRemId and page through direct children.',
|
|
1411
|
+
'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.',
|
|
1412
|
+
'Need to follow inline graph references? Inspect inlineRefs on search/read results and structured child nodes for exact target Rem IDs.',
|
|
784
1413
|
'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
|
|
786
|
-
'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.',
|
|
788
|
-
'Need to
|
|
789
|
-
'Need to
|
|
790
|
-
'Need to
|
|
1414
|
+
'Need a human-readable summary? Switch to contentMode="markdown" on search/read results.',
|
|
1415
|
+
'Need to rename a note? Use remnote_update_note with remId and title only; use [[id:<remId>]] inside the title for exact inline Rem references.',
|
|
1416
|
+
'Need to create a note? Use remnote_create_note; pass tagRemIds for exact-ID tag assignment, [[id:<remId>]] for exact inline Rem references, and asDocument=true when the title/root Rem should be a document.',
|
|
1417
|
+
'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.',
|
|
1418
|
+
'Need to append to today journal? Use remnote_append_journal; pass tagRemIds when the journal entry should be tagged and [[id:<remId>]] for exact inline Rem references.',
|
|
1419
|
+
'Need to insert children? Use remnote_insert_children with an explicit position; use [[id:<remId>]] for exact inline Rem references.',
|
|
1420
|
+
'Need to move a note? Use remnote_move_note dryRun first, include expectedOldParentRemId for stale-context protection, then rerun with dryRun=false after approval.',
|
|
1421
|
+
'Need to replace children? Check remnote_status first; remnote_replace_children requires acceptReplaceOperation=true and supports [[id:<remId>]] for exact inline Rem references.',
|
|
791
1422
|
'Need to update tags on an existing note? Use remnote_update_tags with exact tag Rem IDs.',
|
|
1423
|
+
'Need to set a tag/table property value? Use remnote_set_property with exact remId, tagRemId, propertyRemId, and a text/rem_reference/clear value payload. For select properties, pass the option Rem ID as rem_reference.remId; for inline references in text values, use [[id:<remId>]].',
|
|
792
1424
|
],
|
|
793
1425
|
navigationPresets: {
|
|
794
1426
|
orientation: NAVIGATION_PRESET,
|
|
795
1427
|
branchTraversal: NAVIGATION_PRESET,
|
|
796
1428
|
},
|
|
797
1429
|
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
|
|
1430
|
+
structured: 'ID-first traversal mode. Returns contentStructured with child remIds and inlineRefs for deterministic follow-up reads.',
|
|
1431
|
+
markdown: 'Summary mode. Returns rendered markdown content for human-facing synthesis; inline Rem references render as [[Target Title]].',
|
|
800
1432
|
none: 'Metadata-only mode when content is not needed.',
|
|
801
1433
|
},
|
|
1434
|
+
outputViews: {
|
|
1435
|
+
compact: 'Small metadata surface for broad discovery. Omits tags, aliases, inlineRefs, and diagnostic fields unless another option requires them.',
|
|
1436
|
+
standard: 'Normal discovery metadata for most agent workflows.',
|
|
1437
|
+
full: 'Verbose metadata for diagnostics or detailed audits.',
|
|
1438
|
+
},
|
|
802
1439
|
writePolicy: {
|
|
803
1440
|
statusTool: 'remnote_status',
|
|
804
1441
|
requiredFields: ['acceptWriteOperations', 'acceptReplaceOperation'],
|
|
805
1442
|
guidance: [
|
|
806
1443
|
'Create/update/insert/replace/tag/journal writes require acceptWriteOperations=true.',
|
|
807
1444
|
'remnote_update_note is metadata-only; use insert_children, replace_children, and update_tags for structural or tag writes.',
|
|
1445
|
+
'remnote_set_document_status changes only document status; it preserves concept/card status and defaults to dryRun=true.',
|
|
808
1446
|
'remnote_replace_children requires acceptReplaceOperation=true.',
|
|
809
1447
|
'remnote_insert_children preserves existing child Rem IDs; remnote_replace_children removes them.',
|
|
1448
|
+
'remnote_move_note preserves the moved Rem ID and subtree; dryRun defaults to true.',
|
|
810
1449
|
'All production tag writes use exact tag Rem IDs: create_note.tagRemIds, append_journal.tagRemIds, and update_tags add/remove arrays.',
|
|
1450
|
+
'Markdown-capable write fields support [[id:<remId>]] to create real inline references to existing Rems without name lookup.',
|
|
1451
|
+
'remnote_set_property writes exact-ID tag/table property values and requires acceptWriteOperations=true.',
|
|
811
1452
|
],
|
|
812
1453
|
},
|
|
813
1454
|
currentStatus,
|
|
@@ -830,6 +1471,9 @@ export function registerAllTools(server, wsServer, logger) {
|
|
|
830
1471
|
tool: toolName,
|
|
831
1472
|
duration_ms: Date.now() - startTime,
|
|
832
1473
|
}, 'Tool completed');
|
|
1474
|
+
if (nativeToolResult) {
|
|
1475
|
+
return nativeToolResult;
|
|
1476
|
+
}
|
|
833
1477
|
return {
|
|
834
1478
|
structuredContent: result,
|
|
835
1479
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|