@creator-notes/cnotes 0.16.11 → 0.16.15
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/.mcp.json +3 -3
- package/README.md +3 -3
- package/dist/cn.js +3 -4
- package/dist/cn.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +1 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/canvas.d.ts.map +1 -1
- package/dist/commands/canvas.js +15 -16
- package/dist/commands/canvas.js.map +1 -1
- package/dist/commands/mcp.js +9 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/notes.d.ts.map +1 -1
- package/dist/commands/notes.js +65 -2
- package/dist/commands/notes.js.map +1 -1
- package/dist/commands/operations.d.ts +2 -2
- package/dist/commands/operations.d.ts.map +1 -1
- package/dist/commands/operations.js +3 -4
- package/dist/commands/operations.js.map +1 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +46 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/versions.d.ts.map +1 -1
- package/dist/commands/versions.js +18 -1
- package/dist/commands/versions.js.map +1 -1
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +3 -4
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/auth-store.d.ts.map +1 -1
- package/dist/lib/auth-store.js +1 -2
- package/dist/lib/auth-store.js.map +1 -1
- package/dist/lib/build-schema.d.ts +6 -6
- package/dist/lib/build-schema.js +6 -6
- package/dist/lib/build-schema.js.map +1 -1
- package/dist/lib/claude-session.d.ts +1 -1
- package/dist/lib/claude-session.d.ts.map +1 -1
- package/dist/lib/claude-session.js +3 -4
- package/dist/lib/claude-session.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +13 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +33 -2
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/install-mcp.js +6 -6
- package/dist/lib/install-mcp.js.map +1 -1
- package/dist/lib/mention-lint.d.ts +53 -0
- package/dist/lib/mention-lint.d.ts.map +1 -0
- package/dist/lib/mention-lint.js +104 -0
- package/dist/lib/mention-lint.js.map +1 -0
- package/dist/mcp-server.js +121 -72
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/cnotes/SKILL.md +87 -13
package/dist/mcp-server.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// "command": "node",
|
|
11
11
|
// "args": ["/path/to/pm-notes/cli/dist/mcp-server.js"],
|
|
12
12
|
// "env": {
|
|
13
|
-
// "
|
|
14
|
-
// "
|
|
15
|
-
// "
|
|
13
|
+
// "CNOTES_SERVER": "https://your-codespace.app.github.dev",
|
|
14
|
+
// "CNOTES_TOKEN": "your-token",
|
|
15
|
+
// "CNOTES_WORKSPACE": "your-workspace-id"
|
|
16
16
|
// }
|
|
17
17
|
// }
|
|
18
18
|
// }
|
|
@@ -30,21 +30,21 @@ import { buildSchema } from "./lib/build-schema.js";
|
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
31
31
|
// Environment
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
33
|
+
const CNOTES_SERVER = process.env.CNOTES_SERVER;
|
|
34
|
+
const CNOTES_TOKEN = process.env.CNOTES_TOKEN;
|
|
35
|
+
const CNOTES_WORKSPACE = process.env.CNOTES_WORKSPACE;
|
|
36
|
+
if (!CNOTES_SERVER || !CNOTES_TOKEN || !CNOTES_WORKSPACE) {
|
|
37
37
|
const missing = [
|
|
38
|
-
!
|
|
39
|
-
!
|
|
40
|
-
!
|
|
38
|
+
!CNOTES_SERVER && "CNOTES_SERVER",
|
|
39
|
+
!CNOTES_TOKEN && "CNOTES_TOKEN",
|
|
40
|
+
!CNOTES_WORKSPACE && "CNOTES_WORKSPACE",
|
|
41
41
|
].filter(Boolean);
|
|
42
42
|
process.stderr.write(`cnotes-mcp: missing required env vars: ${missing.join(", ")}\n` +
|
|
43
43
|
"Set them in your Claude Desktop config or shell environment.\n");
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
|
-
const api = new ApiClient(
|
|
47
|
-
const workspaceId =
|
|
46
|
+
const api = new ApiClient(CNOTES_SERVER, CNOTES_TOKEN);
|
|
47
|
+
const workspaceId = CNOTES_WORKSPACE;
|
|
48
48
|
// ---------------------------------------------------------------------------
|
|
49
49
|
// Helpers
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
@@ -108,7 +108,7 @@ server.resource("types", "cnotes://types", async () => ({
|
|
|
108
108
|
// ---------------------------------------------------------------------------
|
|
109
109
|
// Notes tools
|
|
110
110
|
// ---------------------------------------------------------------------------
|
|
111
|
-
server.tool("
|
|
111
|
+
server.tool("cnotes_notes_list", "List notes in the workspace. Returns metadata by default. Set includeContent=true to include full note content (useful to avoid multiple cnotes_notes_get calls).", {
|
|
112
112
|
search: z.string().optional().describe("Full-text search query"),
|
|
113
113
|
type: z.string().optional().describe("Filter by note type (e.g. Meeting, PRD)"),
|
|
114
114
|
tags: z.string().optional().describe("Filter by tags (comma-separated)"),
|
|
@@ -126,7 +126,7 @@ server.tool("cn_notes_list", "List notes in the workspace. Returns metadata by d
|
|
|
126
126
|
format: includeContent ? "markdown" : undefined,
|
|
127
127
|
limit: String(limit ?? 20),
|
|
128
128
|
})));
|
|
129
|
-
server.tool("
|
|
129
|
+
server.tool("cnotes_notes_get", "Get one or more notes by display ID (e.g. MEETING-12, PRD-3). Always returns an array, in input order, in a single round-trip. Pass one ID for one note, or many IDs to batch-fetch — never call this tool multiple times in parallel for different IDs.", {
|
|
130
130
|
ids: z
|
|
131
131
|
.array(z.string())
|
|
132
132
|
.min(1)
|
|
@@ -151,13 +151,13 @@ server.tool("cn_notes_get", "Get one or more notes by display ID (e.g. MEETING-1
|
|
|
151
151
|
// Preserve input order.
|
|
152
152
|
return ids.map((id) => byDisplayId.get(id));
|
|
153
153
|
}));
|
|
154
|
-
server.tool("
|
|
154
|
+
server.tool("cnotes_notes_delete", "Delete (archive) a note.", {
|
|
155
155
|
id: z.string().describe("Note display ID or Convex ID"),
|
|
156
156
|
}, async ({ id }) => withErrorHandling(async () => {
|
|
157
157
|
const noteId = await resolveNoteId(api, workspaceId, id);
|
|
158
158
|
return api.delete(`/api/notes/${noteId}`);
|
|
159
159
|
}));
|
|
160
|
-
server.tool("
|
|
160
|
+
server.tool("cnotes_notes_bulk_archive", "Archive or unarchive multiple notes in one operation. More efficient than calling cnotes_notes_delete multiple times.", {
|
|
161
161
|
ids: z.array(z.string()).describe("Array of note display IDs or Convex IDs"),
|
|
162
162
|
unarchive: z.boolean().optional().default(false).describe("Set true to unarchive instead of archive"),
|
|
163
163
|
}, async ({ ids, unarchive }) => withErrorHandling(async () => {
|
|
@@ -168,7 +168,7 @@ server.tool("cn_notes_bulk_archive", "Archive or unarchive multiple notes in one
|
|
|
168
168
|
isArchived: !unarchive,
|
|
169
169
|
});
|
|
170
170
|
}));
|
|
171
|
-
server.tool("
|
|
171
|
+
server.tool("cnotes_notes_bulk_retype", "Retype multiple notes to a new type in one operation. Each note is atomically retyped (new note created, old archived, cross-linked).", {
|
|
172
172
|
ids: z.array(z.string()).describe("Array of note display IDs or Convex IDs to retype"),
|
|
173
173
|
targetType: z.string().describe("Target type name (must be an existing supertag, e.g. 'Insight')"),
|
|
174
174
|
}, async ({ ids, targetType }) => withErrorHandling(async () => {
|
|
@@ -179,7 +179,7 @@ server.tool("cn_notes_bulk_retype", "Retype multiple notes to a new type in one
|
|
|
179
179
|
targetType,
|
|
180
180
|
});
|
|
181
181
|
}));
|
|
182
|
-
server.tool("
|
|
182
|
+
server.tool("cnotes_notes_update", "Update note metadata (type, tags, pin/archive status). Does NOT change content — use cnotes_versions_create for that. Title is read-only (derived from # h1 heading in content).", {
|
|
183
183
|
id: z.string().describe("Note display ID or Convex ID"),
|
|
184
184
|
type: z.string().optional().describe("New note type (must be an existing supertag, e.g. 'Insight')"),
|
|
185
185
|
tags: z.string().optional().describe("Replace tags (comma-separated). Use empty string to clear."),
|
|
@@ -214,16 +214,43 @@ const NoteCreateItem = z.object({
|
|
|
214
214
|
markdown: z.string().describe("Note content as markdown (the first # h1 heading becomes the title)"),
|
|
215
215
|
tags: z.array(z.string()).optional().describe("Tags for this note"),
|
|
216
216
|
});
|
|
217
|
-
server.tool("
|
|
217
|
+
server.tool("cnotes_notes_create", "Create one or more interlinked notes in a single atomic transaction. Before authoring, read the target type's rubric (cnotes_types_show, or the validationPrompt field from cnotes_types_list) and write to it so the note lands well-formed. Pass `notes` as a single item object or an array of items. Title is derived from the # h1 heading in each note's markdown. Relationship link syntax inside markdown: use [@key: Title](relationship:references) to cross-reference another note in the same batch (the @key is replaced with the new note's display ID after creation), or [NOTE-12: Title](relationship:references) to reference an existing note. ALWAYS include the `: Title` part — the title-less form `[NOTE-12](relationship:references)` saves a chip with `title: null` that renders as \"Untitled\" in the UI. The relationship name is free-form (e.g. references, supports, triggers); use `references` if unsure. The response echoes each used type's rubric under `rubrics` — revise in-loop if a note falls short. If validation fails, the response includes a structured `details.items` array pinpointing which item (by index + key) is malformed and why.", {
|
|
218
218
|
notes: z
|
|
219
219
|
.union([NoteCreateItem, z.array(NoteCreateItem).min(1)])
|
|
220
220
|
.describe("Either a single item object or a non-empty array of items."),
|
|
221
|
-
}, async ({ notes }) => withErrorHandling(() =>
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
}, async ({ notes }) => withErrorHandling(async () => {
|
|
222
|
+
const items = Array.isArray(notes) ? notes : [notes];
|
|
223
|
+
const result = await api.post("/api/notes/bulk", {
|
|
224
|
+
action: "bulkCreate",
|
|
225
|
+
workspaceId,
|
|
226
|
+
notes: items,
|
|
227
|
+
});
|
|
228
|
+
// A1 — echo the rubric for each type used so the agent sees the standard
|
|
229
|
+
// for what it just wrote and can revise in-loop. Zero LLM. Non-fatal: the
|
|
230
|
+
// create already succeeded if we got here.
|
|
231
|
+
try {
|
|
232
|
+
const supertags = await api.get("/api/supertags", {
|
|
233
|
+
workspaceId,
|
|
234
|
+
});
|
|
235
|
+
const byName = new Map();
|
|
236
|
+
for (const s of Array.isArray(supertags) ? supertags : []) {
|
|
237
|
+
const nm = String(s.name || "").toLowerCase();
|
|
238
|
+
if (nm && s.validationPrompt)
|
|
239
|
+
byName.set(nm, String(s.validationPrompt));
|
|
240
|
+
}
|
|
241
|
+
const rubrics = Array.from(new Set(items.map((n) => String(n.type))))
|
|
242
|
+
.map((t) => ({ type: t, rubric: byName.get(t.toLowerCase()) || "" }))
|
|
243
|
+
.filter((r) => r.rubric);
|
|
244
|
+
if (rubrics.length > 0 && result && typeof result === "object") {
|
|
245
|
+
result.rubrics = rubrics;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Skip the rubric echo if supertags can't be fetched.
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}));
|
|
253
|
+
server.tool("cnotes_notes_retype", "Change a single note's type. Atomic operation: creates a new note with the target type, archives the old one, and cross-links both.", {
|
|
227
254
|
noteId: z.string().describe("Display ID of the note to retype (e.g. NOTE-1)"),
|
|
228
255
|
targetType: z.string().describe("Target type name (must be an existing supertag)"),
|
|
229
256
|
}, async ({ noteId, targetType }) => withErrorHandling(() => api.post("/api/notes/retype", {
|
|
@@ -234,7 +261,7 @@ server.tool("cn_notes_retype", "Change a single note's type. Atomic operation: c
|
|
|
234
261
|
// ---------------------------------------------------------------------------
|
|
235
262
|
// Search tools
|
|
236
263
|
// ---------------------------------------------------------------------------
|
|
237
|
-
server.tool("
|
|
264
|
+
server.tool("cnotes_search_semantic", "Semantic (vector) search across notes. Better for meaning-based queries.", {
|
|
238
265
|
query: z.string().describe("Search query"),
|
|
239
266
|
limit: z.number().optional().default(10).describe("Max results"),
|
|
240
267
|
canvasId: z.string().optional().describe("Scope search to notes on a canvas"),
|
|
@@ -247,26 +274,26 @@ server.tool("cn_search_semantic", "Semantic (vector) search across notes. Better
|
|
|
247
274
|
// ---------------------------------------------------------------------------
|
|
248
275
|
// Canvas tools
|
|
249
276
|
// ---------------------------------------------------------------------------
|
|
250
|
-
server.tool("
|
|
277
|
+
server.tool("cnotes_canvas_list", "List canvases in the workspace. Archived canvases are excluded by default.", {
|
|
251
278
|
includeArchived: z.boolean().optional().default(false).describe("Include archived canvases"),
|
|
252
279
|
}, async ({ includeArchived }) => withErrorHandling(() => api.get("/api/canvas", {
|
|
253
280
|
workspaceId,
|
|
254
281
|
...(includeArchived ? { includeArchived: "true" } : {}),
|
|
255
282
|
})));
|
|
256
|
-
server.tool("
|
|
283
|
+
server.tool("cnotes_canvas_get", "Get canvas details including nodes, edges, and canvas links. Richtext node content is returned as markdown.", {
|
|
257
284
|
canvasId: z.string().describe("Canvas ID"),
|
|
258
285
|
}, async ({ canvasId }) => withErrorHandling(() => api.get(`/api/canvas/${encodeURIComponent(canvasId)}`, { workspaceId, format: "markdown" })));
|
|
259
|
-
server.tool("
|
|
286
|
+
server.tool("cnotes_canvas_read", "Read every note on a canvas as one concatenated markdown document, in display order (top-to-bottom, then left-to-right). Use this to think across a canvas in a single round-trip — preferred over canvas_get + N notes_get calls.", {
|
|
260
287
|
canvasId: z.string().describe("Canvas ID"),
|
|
261
288
|
}, async ({ canvasId }) => withErrorHandling(async () => readCanvasMarkdown(api, workspaceId, canvasId)));
|
|
262
|
-
server.tool("
|
|
289
|
+
server.tool("cnotes_canvas_create", "Create a new canvas. Goal and target audience are required — every canvas must declare its purpose and who it serves.", {
|
|
263
290
|
name: z.string().describe("Canvas name"),
|
|
264
291
|
goal: z.string().describe("Canvas goal — the purpose, desired outcome, and what it should communicate (markdown). Required."),
|
|
265
292
|
targetAudience: z.string().describe("Target audience — who this canvas is meant to serve or inform. Required."),
|
|
266
293
|
}, async ({ name, goal, targetAudience }) => {
|
|
267
294
|
return withErrorHandling(() => api.post("/api/canvas", { workspaceId, title: name, goal, targetAudience }));
|
|
268
295
|
});
|
|
269
|
-
server.tool("
|
|
296
|
+
server.tool("cnotes_canvas_update", "Update canvas metadata: title, goal (purpose/outcome), and/or target audience. Use this to set or change the intent metadata that guides agents working on this canvas.", {
|
|
270
297
|
canvasId: z.string().describe("Canvas ID"),
|
|
271
298
|
title: z.string().optional().describe("New canvas title"),
|
|
272
299
|
goal: z.string().optional().describe("Canvas goal — the purpose, desired outcome, and what it should communicate (markdown)"),
|
|
@@ -284,21 +311,21 @@ server.tool("cn_canvas_update", "Update canvas metadata: title, goal (purpose/ou
|
|
|
284
311
|
body.targetAudience = targetAudience;
|
|
285
312
|
return withErrorHandling(() => api.patch(`/api/canvas/${encodeURIComponent(canvasId)}`, body, { workspaceId }));
|
|
286
313
|
});
|
|
287
|
-
server.tool("
|
|
314
|
+
server.tool("cnotes_canvas_delete", "Delete a canvas.", {
|
|
288
315
|
canvasId: z.string().describe("Canvas ID"),
|
|
289
316
|
}, async ({ canvasId }) => withErrorHandling(() => api.delete(`/api/canvas/${canvasId}`)));
|
|
290
|
-
server.tool("
|
|
317
|
+
server.tool("cnotes_canvas_archive", "Archive or unarchive a canvas. Archiving is a soft delete (sets archivedAt timestamp). Use unarchive=true to restore.", {
|
|
291
318
|
canvasId: z.string().describe("Canvas ID"),
|
|
292
319
|
unarchive: z.boolean().optional().default(false).describe("Set true to unarchive/restore instead of archive"),
|
|
293
320
|
}, async ({ canvasId, unarchive }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
294
321
|
action: unarchive ? "unarchive" : "archive",
|
|
295
322
|
})));
|
|
296
|
-
server.tool("
|
|
323
|
+
server.tool("cnotes_canvas_set_home", "Set a canvas as the workspace home/default canvas. Clears any previous home canvas.", {
|
|
297
324
|
canvasId: z.string().describe("Canvas ID to set as home"),
|
|
298
325
|
}, async ({ canvasId }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, { action: "setAsHome" })));
|
|
299
326
|
// --- Deprecated single-item wrappers (delegate to bulk_add) ---
|
|
300
327
|
// Kept for backward compatibility with existing MCP clients.
|
|
301
|
-
server.tool("
|
|
328
|
+
server.tool("cnotes_canvas_add_node", "[Deprecated: use cnotes_canvas_bulk_add] Add a note to a canvas as a node.", {
|
|
302
329
|
canvasId: z.string().describe("Canvas ID"),
|
|
303
330
|
noteId: z.string().describe("Note display ID or Convex ID"),
|
|
304
331
|
x: z.number().optional().default(100).describe("X position"),
|
|
@@ -307,7 +334,7 @@ server.tool("cn_canvas_add_node", "[Deprecated: use cn_canvas_bulk_add] Add a no
|
|
|
307
334
|
action: "bulkAddNodes",
|
|
308
335
|
items: [{ type: "note", noteId, positionX: x, positionY: y }],
|
|
309
336
|
})));
|
|
310
|
-
server.tool("
|
|
337
|
+
server.tool("cnotes_canvas_add_text", "[Deprecated: use cnotes_canvas_bulk_add] Add a text annotation node to a canvas.", {
|
|
311
338
|
canvasId: z.string().describe("Canvas ID"),
|
|
312
339
|
text: z.string().describe("Text content"),
|
|
313
340
|
size: z.enum(["heading", "paragraph"]).optional().default("heading").describe("Text size"),
|
|
@@ -318,9 +345,9 @@ server.tool("cn_canvas_add_text", "[Deprecated: use cn_canvas_bulk_add] Add a te
|
|
|
318
345
|
action: "bulkAddNodes",
|
|
319
346
|
items: [{ type: "text", content: text, fontSize: size === "paragraph" ? 18 : 32, colorVariant: color, positionX: x, positionY: y }],
|
|
320
347
|
})));
|
|
321
|
-
server.tool("
|
|
348
|
+
server.tool("cnotes_canvas_add_richtext", "[Deprecated: use cnotes_canvas_bulk_add] Add a richtext annotation node to a canvas.", {
|
|
322
349
|
canvasId: z.string().describe("Canvas ID"),
|
|
323
|
-
content: z.string().describe("Richtext content as markdown.
|
|
350
|
+
content: z.string().describe("Richtext content as markdown. Plain markdown ONLY — do NOT use [NOTE-X](relationship:...) or [[...]] mention syntax here (it corrupts the card, which renders blank); reference notes as plain display IDs (\"see RISK-64\"). Keep it sticky-sized (≤ ~3 sentences): richtext is unversioned, unsearchable presentation furniture — knowledge worth keeping belongs in a typed note."),
|
|
324
351
|
size: z.enum(["small", "medium", "large"]).optional().default("small").describe("Display size"),
|
|
325
352
|
color: z.string().optional().describe("Background color hex"),
|
|
326
353
|
x: z.number().optional().default(100).describe("X position"),
|
|
@@ -329,7 +356,7 @@ server.tool("cn_canvas_add_richtext", "[Deprecated: use cn_canvas_bulk_add] Add
|
|
|
329
356
|
action: "bulkAddNodes",
|
|
330
357
|
items: [{ type: "richtext", content, size, colorHex: color, positionX: x, positionY: y }],
|
|
331
358
|
})));
|
|
332
|
-
server.tool("
|
|
359
|
+
server.tool("cnotes_canvas_add_list", "[Deprecated: use cnotes_canvas_bulk_add] Add a list/grid node grouping notes on a canvas. The description is the only label; its first paragraph derives the searchable title.", {
|
|
333
360
|
canvasId: z.string().describe("Canvas ID"),
|
|
334
361
|
description: z.string().min(1).describe("Rich-text description (markdown). Required. First paragraph derives the searchable title."),
|
|
335
362
|
noteIds: z.string().optional().describe("Comma-separated note IDs"),
|
|
@@ -347,7 +374,7 @@ server.tool("cn_canvas_add_list", "[Deprecated: use cn_canvas_bulk_add] Add a li
|
|
|
347
374
|
positionY: y,
|
|
348
375
|
}],
|
|
349
376
|
})));
|
|
350
|
-
server.tool("
|
|
377
|
+
server.tool("cnotes_canvas_add_link", "[Deprecated: use cnotes_canvas_bulk_add] Add a link to another canvas.", {
|
|
351
378
|
canvasId: z.string().describe("Canvas ID"),
|
|
352
379
|
targetCanvasId: z.string().describe("Target canvas ID to link to"),
|
|
353
380
|
x: z.number().optional().default(100).describe("X position"),
|
|
@@ -357,7 +384,7 @@ server.tool("cn_canvas_add_link", "[Deprecated: use cn_canvas_bulk_add] Add a li
|
|
|
357
384
|
items: [{ type: "canvas", linkedCanvasId: targetCanvasId, positionX: x, positionY: y }],
|
|
358
385
|
})));
|
|
359
386
|
// --- Primary bulk tool ---
|
|
360
|
-
server.tool("
|
|
387
|
+
server.tool("cnotes_canvas_bulk_add", "Add one or more items to a canvas. Use this for ALL canvas additions — single or multiple. Supports all node types: note, text, richtext, list, canvas (link to another canvas), and edge. ALWAYS prefer this over repeated single-item calls. Items are processed in order so edges can reference nodes created earlier in the same batch via sourceItemIndex/targetItemIndex.\n\nELEMENT CHOICE — notes are the knowledge tier (typed, versioned, searchable, the only content agents read back in full); everything else is presentation furniture. Default any claim/fact/decision/finding worth keeping to a typed NOTE. Use text for one-line section labels, richtext only for sticky-sized framing (orientation banners, emphasis quoting a note, image tiles — never the sole home of a claim), list for grouping member notes (description = one-line frame; N distinct things = N notes), canvas for portals, edge for note-to-note relationships (notes only — text/richtext endpoints fail; mirror the relationship as a mention in the note bodies).", {
|
|
361
388
|
canvasId: z.string().describe("Canvas ID"),
|
|
362
389
|
notes: z.array(z.object({
|
|
363
390
|
noteId: z.string().describe("Note display ID or Convex ID"),
|
|
@@ -381,7 +408,7 @@ server.tool("cn_canvas_bulk_add", "Add one or more items to a canvas. Use this f
|
|
|
381
408
|
}),
|
|
382
409
|
z.object({
|
|
383
410
|
type: z.literal("richtext"),
|
|
384
|
-
content: z.string().describe("Markdown content.
|
|
411
|
+
content: z.string().describe("Markdown content. Plain markdown ONLY — no [NOTE-X](relationship:...) / [[...]] mention syntax (corrupts the card); reference notes as plain display IDs. Sticky-sized (≤ ~3 sentences); knowledge worth keeping goes in a note."),
|
|
385
412
|
x: z.number().describe("X position"),
|
|
386
413
|
y: z.number().describe("Y position"),
|
|
387
414
|
size: z.enum(["small", "medium", "large"]).optional().describe("Node size"),
|
|
@@ -389,7 +416,7 @@ server.tool("cn_canvas_bulk_add", "Add one or more items to a canvas. Use this f
|
|
|
389
416
|
}),
|
|
390
417
|
z.object({
|
|
391
418
|
type: z.literal("list"),
|
|
392
|
-
description: z.string().min(1).describe("
|
|
419
|
+
description: z.string().min(1).describe("One-line FRAME (markdown). Required. First paragraph derives the searchable title — this is the only label for list nodes. The CONTENT of a list is its member notes (noteIds), never the description: N distinct things = N typed notes grouped here. No mention syntax."),
|
|
393
420
|
x: z.number().describe("X position"),
|
|
394
421
|
y: z.number().describe("Y position"),
|
|
395
422
|
viewMode: z.enum(["list", "grid"]).optional(),
|
|
@@ -439,14 +466,14 @@ server.tool("cn_canvas_bulk_add", "Add one or more items to a canvas. Use this f
|
|
|
439
466
|
items: allItems,
|
|
440
467
|
});
|
|
441
468
|
}));
|
|
442
|
-
server.tool("
|
|
469
|
+
server.tool("cnotes_canvas_place", "Place items on a canvas using a DECLARATIVE LAYOUT (no x/y math). The server measures every item, packs them with no overlaps, and inserts them in a single batch. PREFER THIS over cnotes_canvas_bulk_add whenever you can express the layout as stacks/grids/anchors — which is almost always. The agent should describe STRUCTURE (rows, columns, what's near what), not pixel coordinates.\n\nELEMENT CHOICE: default content to typed NOTES (the only versioned, searchable tier agents can read back); text = one-line labels, richtext = sticky-sized framing only, list = member notes behind a one-line frame, edges connect notes only.\n\nThe call is best-effort, NOT atomic: per-item failures are reported in the response (HTTP 207) but the items that succeeded stay on the canvas. Always inspect `items[].error` and `edges[].error` in the response.\n\nThe `doc` argument is a layout document:\n { root: <LayoutNode>, edges?: [{from, to, label?}], origin?: {x, y} }\n\n<LayoutNode> is one of:\n • { kind: 'stack', axis: 'vertical'|'horizontal', gap?: 'tight'|'medium'|'spacious'|<px>, align?: 'start'|'center'|'end', items: [<LayoutNode>...] }\n • { kind: 'grid', columns: <n>, gap?: ..., items: [<LayoutNode>...] }\n • { kind: 'anchor', to: '<displayId or canvas node id>', direction: 'right'|'left'|'above'|'below', gap?: ..., child: <LayoutNode> }\n • { kind: 'item', type: 'note'|'text'|'richtext'|'list'|'canvas', key?: '<name>', ...item-specific fields }\n\nLeaf item shapes (no x/y!):\n note: { kind:'item', type:'note', noteId:'NOTE-3', key?:'a' }\n text: { kind:'item', type:'text', content:'Hello', fontSize?:32, colorVariant?:'normal'|'muted'|'highlighted' } // one-line section labels only\n richtext: { kind:'item', type:'richtext', content:'# Markdown OK', size?:'small'|'medium'|'large', colorHex?:'<hex>' } // sticky-sized framing only; plain markdown, NO [NOTE-X](relationship:...) mention syntax (corrupts the card); knowledge worth keeping = a note\n list: { kind:'item', type:'list', description:'One-line frame — first paragraph becomes the title', noteIds?:['NOTE-1','NOTE-2'], viewMode?:'list'|'grid' } // description is REQUIRED and is a frame, never the content: N distinct things = N notes in noteIds\n canvas: { kind:'item', type:'canvas', linkedCanvasId:'<id>' }\n\nEdges reference endpoints by, in priority order: `@<key>` (any item with a `key` placed in the same call), the original noteId/displayId of a note placed in this call (e.g. 'NOTE-7'), or the displayId / convex id of a node already on the canvas.\n\nExample (a SWOT-like quadrant):\n {\n root: {\n kind:'stack', axis:'vertical', gap:'spacious',\n items:[\n { kind:'stack', axis:'horizontal', gap:'spacious', items:[\n { kind:'grid', columns:2, items:[\n { kind:'item', type:'note', noteId:'STR-1' },\n { kind:'item', type:'note', noteId:'STR-2' },\n ]},\n { kind:'grid', columns:2, items:[\n { kind:'item', type:'note', noteId:'WEAK-1' },\n ]},\n ]},\n { kind:'stack', axis:'horizontal', gap:'spacious', items:[ /* opportunities + threats */ ]},\n ],\n },\n }", {
|
|
443
470
|
canvasId: z.string().describe("Canvas ID"),
|
|
444
471
|
doc: z.any().describe("Layout document. See tool description for shape."),
|
|
445
472
|
}, async ({ canvasId, doc }) => withErrorHandling(() => api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(workspaceId)}`, {
|
|
446
473
|
action: "placeLayout",
|
|
447
474
|
doc,
|
|
448
475
|
})));
|
|
449
|
-
server.tool("
|
|
476
|
+
server.tool("cnotes_canvas_bulk_move", "Move multiple nodes to new positions in one operation. More efficient than calling cnotes_canvas_move_node multiple times.", {
|
|
450
477
|
canvasId: z.string().describe("Canvas ID"),
|
|
451
478
|
moves: z.array(z.object({
|
|
452
479
|
nodeId: z.string().describe("Canvas node ID"),
|
|
@@ -463,14 +490,14 @@ server.tool("cn_canvas_bulk_move", "Move multiple nodes to new positions in one
|
|
|
463
490
|
positionY: m.y,
|
|
464
491
|
})),
|
|
465
492
|
})));
|
|
466
|
-
server.tool("
|
|
493
|
+
server.tool("cnotes_canvas_bulk_remove", "Remove multiple nodes from a canvas in one operation. More efficient than calling cnotes_canvas_remove_node multiple times.", {
|
|
467
494
|
canvasId: z.string().describe("Canvas ID"),
|
|
468
495
|
nodeIds: z.array(z.string()).describe("Array of node IDs to remove"),
|
|
469
496
|
}, async ({ canvasId, nodeIds }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
470
497
|
action: "bulkRemoveNodes",
|
|
471
498
|
nodeIds,
|
|
472
499
|
})));
|
|
473
|
-
server.tool("
|
|
500
|
+
server.tool("cnotes_canvas_add_edge", "Connect two nodes on a canvas with an edge. Defaults to an arrow at the target end and a solid line.", {
|
|
474
501
|
canvasId: z.string().describe("Canvas ID"),
|
|
475
502
|
sourceNodeId: z.string().describe("Source node ID"),
|
|
476
503
|
targetNodeId: z.string().describe("Target node ID"),
|
|
@@ -494,7 +521,7 @@ server.tool("cn_canvas_add_edge", "Connect two nodes on a canvas with an edge. D
|
|
|
494
521
|
lineStyle,
|
|
495
522
|
});
|
|
496
523
|
}));
|
|
497
|
-
server.tool("
|
|
524
|
+
server.tool("cnotes_canvas_update_edge", "Update an edge: change its label, toggle arrowheads, switch line style, or reverse direction. Only the fields you pass are changed.", {
|
|
498
525
|
canvasId: z.string().describe("Canvas ID"),
|
|
499
526
|
edgeId: z.string().describe("Canvas edge ID"),
|
|
500
527
|
label: z
|
|
@@ -523,7 +550,7 @@ server.tool("cn_canvas_update_edge", "Update an edge: change its label, toggle a
|
|
|
523
550
|
body.reverse = reverse;
|
|
524
551
|
return api.post(`/api/canvas/${canvasId}`, body);
|
|
525
552
|
}));
|
|
526
|
-
server.tool("
|
|
553
|
+
server.tool("cnotes_canvas_move_node", "Move a node to a new position on a canvas.", {
|
|
527
554
|
canvasId: z.string().describe("Canvas ID"),
|
|
528
555
|
nodeId: z.string().describe("Canvas node ID"),
|
|
529
556
|
nodeType: z.enum(["note", "text", "list", "canvas", "richtext"]).optional().default("note").describe("Node type"),
|
|
@@ -536,27 +563,27 @@ server.tool("cn_canvas_move_node", "Move a node to a new position on a canvas.",
|
|
|
536
563
|
positionX: x,
|
|
537
564
|
positionY: y,
|
|
538
565
|
})));
|
|
539
|
-
server.tool("
|
|
566
|
+
server.tool("cnotes_canvas_remove_node", "Remove a node from a canvas.", {
|
|
540
567
|
canvasId: z.string().describe("Canvas ID"),
|
|
541
568
|
nodeId: z.string().describe("Canvas node ID"),
|
|
542
569
|
}, async ({ canvasId, nodeId }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
543
570
|
action: "removeNode",
|
|
544
571
|
nodeId,
|
|
545
572
|
})));
|
|
546
|
-
server.tool("
|
|
573
|
+
server.tool("cnotes_canvas_remove_edge", "Remove an edge from a canvas.", {
|
|
547
574
|
canvasId: z.string().describe("Canvas ID"),
|
|
548
575
|
edgeId: z.string().describe("Canvas edge ID"),
|
|
549
576
|
}, async ({ canvasId, edgeId }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
550
577
|
action: "removeEdge",
|
|
551
578
|
edgeId,
|
|
552
579
|
})));
|
|
553
|
-
server.tool("
|
|
580
|
+
server.tool("cnotes_canvas_digest", "Get the AI-generated digest of a canvas: narrative summary, key themes, and a per-note summary list covering every note on the canvas. Run this first when starting work on any canvas — it gives you full orientation in a single call, so you don't need to read each note individually. Each note entry has shape { displayId, title, type, summary }; when summary is null the AI summary hasn't generated yet. The notes list is always populated, even when digest status is 'none' or 'stale'.", {
|
|
554
581
|
canvasId: z.string().describe("Canvas ID"),
|
|
555
582
|
}, async ({ canvasId }) => withErrorHandling(() => api.get(`/api/canvas/${encodeURIComponent(canvasId)}/digest`, { workspaceId })));
|
|
556
|
-
server.tool("
|
|
583
|
+
server.tool("cnotes_canvas_digest_generate", "Request digest (re)generation for a canvas. Use when digest status is 'none' or you want a fresh summary. The digest is generated async — poll cnotes_canvas_digest after ~60s.", {
|
|
557
584
|
canvasId: z.string().describe("Canvas ID"),
|
|
558
585
|
}, async ({ canvasId }) => withErrorHandling(() => api.post(`/api/canvas/${encodeURIComponent(canvasId)}/digest?workspaceId=${encodeURIComponent(workspaceId)}`, {})));
|
|
559
|
-
server.tool("
|
|
586
|
+
server.tool("cnotes_canvas_activity", "Show recent activity on a canvas — edits, agent runs, gestures, restores — newest first. Use this to find out what changed and who changed it before making further edits, or to confirm a previous agent run actually applied. Returns { canvasName, operations: Array<{ _id, operationType, actorName, actorSource ('human'|'agent'|'import'|'api'), agentRunId, userPrompt, status ('in_progress'|'applied'|'reverted'|'failed'), eventCount, affectedEntityIds, summary, structuredSummary: { headline, summary, byCategory[], themes[] }, startedAt, completedAt }>}.", {
|
|
560
587
|
canvasId: z.string().describe("Canvas ID"),
|
|
561
588
|
limit: z.number().int().min(1).max(200).optional().describe("Max entries (default 50, max 200)"),
|
|
562
589
|
}, async ({ canvasId, limit }) => {
|
|
@@ -565,8 +592,8 @@ server.tool("cn_canvas_activity", "Show recent activity on a canvas — edits, a
|
|
|
565
592
|
params.limit = String(limit);
|
|
566
593
|
return withErrorHandling(() => api.get(`/api/canvas/${encodeURIComponent(canvasId)}/activity`, params));
|
|
567
594
|
});
|
|
568
|
-
server.tool("
|
|
569
|
-
server.tool("
|
|
595
|
+
server.tool("cnotes_canvas_templates", "List available canvas templates.", {}, async () => withErrorHandling(() => api.get("/api/canvas/template")));
|
|
596
|
+
server.tool("cnotes_canvas_from_template", "Create a canvas from a predefined template.", {
|
|
570
597
|
templateId: z.string().describe("Template ID"),
|
|
571
598
|
title: z.string().optional().describe("Custom canvas title"),
|
|
572
599
|
}, async ({ templateId, title }) => withErrorHandling(() => api.post("/api/canvas/template", {
|
|
@@ -577,7 +604,7 @@ server.tool("cn_canvas_from_template", "Create a canvas from a predefined templa
|
|
|
577
604
|
// ---------------------------------------------------------------------------
|
|
578
605
|
// Timeline
|
|
579
606
|
// ---------------------------------------------------------------------------
|
|
580
|
-
server.tool("
|
|
607
|
+
server.tool("cnotes_timeline", "Show recent changes across notes. Supports relative time (7d, 2w) or ISO dates.", {
|
|
581
608
|
since: z.string().optional().default("7d").describe("Start of range (e.g. 7d, 2w, 2026-03-21)"),
|
|
582
609
|
until: z.string().optional().describe("End of range (default: now)"),
|
|
583
610
|
type: z.string().optional().describe("Filter by note type (comma-separated)"),
|
|
@@ -618,8 +645,30 @@ function parseDuration(input) {
|
|
|
618
645
|
// ---------------------------------------------------------------------------
|
|
619
646
|
// Types (supertags)
|
|
620
647
|
// ---------------------------------------------------------------------------
|
|
621
|
-
server.tool("
|
|
622
|
-
server.tool("
|
|
648
|
+
server.tool("cnotes_types_list", "List note types (supertags) in the workspace. Each entry carries a `validationPrompt` (its quality rubric — what a good instance contains); read it before authoring.", {}, async () => withErrorHandling(() => api.get("/api/supertags", { workspaceId })));
|
|
649
|
+
server.tool("cnotes_types_show", "Show one note type's quality rubric (validationPrompt) — what a good instance of that type contains. Read this BEFORE authoring a note of this type so it lands well-formed.", {
|
|
650
|
+
name: z.string().describe("Type name (e.g. 'Decision', 'Insight')"),
|
|
651
|
+
}, async ({ name }) => withErrorHandling(async () => {
|
|
652
|
+
const list = await api.get("/api/supertags", { workspaceId });
|
|
653
|
+
const want = name.trim().toLowerCase();
|
|
654
|
+
const match = (Array.isArray(list) ? list : []).find((s) => String(s.name || "").toLowerCase() === want ||
|
|
655
|
+
String(s.displayName || "").toLowerCase() === want);
|
|
656
|
+
if (!match) {
|
|
657
|
+
return {
|
|
658
|
+
error: `Type "${name}" not found`,
|
|
659
|
+
hint: "Call cnotes_types_list to see valid type names.",
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
name: match.name,
|
|
664
|
+
displayName: match.displayName,
|
|
665
|
+
prefix: match.prefix,
|
|
666
|
+
description: match.description ?? null,
|
|
667
|
+
rubric: match.validationPrompt ?? null,
|
|
668
|
+
validationEnabled: match.validationEnabled ?? false,
|
|
669
|
+
};
|
|
670
|
+
}));
|
|
671
|
+
server.tool("cnotes_types_create", "Create a new note type (supertag).", {
|
|
623
672
|
name: z.string().describe("Type name, PascalCase, no spaces (e.g. PainPoint, UserStory)"),
|
|
624
673
|
displayName: z.string().optional().describe("Display name (can have spaces)"),
|
|
625
674
|
prefix: z.string().optional().describe("ID prefix, uppercase (e.g. PAINPOINT, USERSTORY)"),
|
|
@@ -633,7 +682,7 @@ server.tool("cn_types_create", "Create a new note type (supertag).", {
|
|
|
633
682
|
description,
|
|
634
683
|
color,
|
|
635
684
|
})));
|
|
636
|
-
server.tool("
|
|
685
|
+
server.tool("cnotes_types_update", "Update a note type (supertag).", {
|
|
637
686
|
name: z.string().describe("Type name to update"),
|
|
638
687
|
displayName: z.string().optional().describe("New display name"),
|
|
639
688
|
prefix: z.string().optional().describe("New ID prefix (uppercase)"),
|
|
@@ -654,7 +703,7 @@ server.tool("cn_types_update", "Update a note type (supertag).", {
|
|
|
654
703
|
// ---------------------------------------------------------------------------
|
|
655
704
|
// Relationships
|
|
656
705
|
// ---------------------------------------------------------------------------
|
|
657
|
-
server.tool("
|
|
706
|
+
server.tool("cnotes_relationships_list", "List note relationships in the workspace.", {
|
|
658
707
|
noteDisplayId: z.string().optional().describe("Filter by note display ID"),
|
|
659
708
|
}, async ({ noteDisplayId }) => withErrorHandling(() => api.get("/api/relationships", {
|
|
660
709
|
workspaceId,
|
|
@@ -663,7 +712,7 @@ server.tool("cn_relationships_list", "List note relationships in the workspace."
|
|
|
663
712
|
// ---------------------------------------------------------------------------
|
|
664
713
|
// Memory (Zep)
|
|
665
714
|
// ---------------------------------------------------------------------------
|
|
666
|
-
server.tool("
|
|
715
|
+
server.tool("cnotes_memory_query", "Search facts and entities in the knowledge graph by relevance.", {
|
|
667
716
|
query: z.string().describe("Search query"),
|
|
668
717
|
limit: z.number().optional().default(10).describe("Max results per category"),
|
|
669
718
|
}, async ({ query, limit }) => withErrorHandling(() => api.post("/api/zep/query", {
|
|
@@ -671,7 +720,7 @@ server.tool("cn_memory_query", "Search facts and entities in the knowledge graph
|
|
|
671
720
|
query,
|
|
672
721
|
limit: limit ?? 10,
|
|
673
722
|
})));
|
|
674
|
-
server.tool("
|
|
723
|
+
server.tool("cnotes_memory_facts", "List all workspace facts from the knowledge graph.", {}, async () => withErrorHandling(async () => {
|
|
675
724
|
const data = await api.post("/api/zep/query", {
|
|
676
725
|
workspaceId,
|
|
677
726
|
query: "all facts",
|
|
@@ -679,7 +728,7 @@ server.tool("cn_memory_facts", "List all workspace facts from the knowledge grap
|
|
|
679
728
|
});
|
|
680
729
|
return data.allFacts;
|
|
681
730
|
}));
|
|
682
|
-
server.tool("
|
|
731
|
+
server.tool("cnotes_memory_entities", "List all workspace entities (people, topics, orgs) from the knowledge graph.", {}, async () => withErrorHandling(async () => {
|
|
683
732
|
const data = await api.post("/api/zep/query", {
|
|
684
733
|
workspaceId,
|
|
685
734
|
query: "all entities",
|
|
@@ -690,7 +739,7 @@ server.tool("cn_memory_entities", "List all workspace entities (people, topics,
|
|
|
690
739
|
// ---------------------------------------------------------------------------
|
|
691
740
|
// Versions
|
|
692
741
|
// ---------------------------------------------------------------------------
|
|
693
|
-
server.tool("
|
|
742
|
+
server.tool("cnotes_versions_list", "List versions of a note.", {
|
|
694
743
|
noteId: z.string().describe("Note display ID or Convex ID"),
|
|
695
744
|
}, async ({ noteId }) => withErrorHandling(async () => {
|
|
696
745
|
const notesList = await api.get("/api/notes", {
|
|
@@ -703,7 +752,7 @@ server.tool("cn_versions_list", "List versions of a note.", {
|
|
|
703
752
|
throw new Error(`Note not found: ${noteId}`);
|
|
704
753
|
return note.versions || [];
|
|
705
754
|
}));
|
|
706
|
-
server.tool("
|
|
755
|
+
server.tool("cnotes_versions_create", "Create a new version of a note. Content is markdown. To link to other notes, use [NOTE-123: Title](relationship:references) inside the markdown — always include the title (the part after the colon) or the chip renders as 'Untitled'. The relationship name is free-form (e.g. references, triggers, caused-by); use 'references' if unsure.", {
|
|
707
756
|
noteId: z.string().describe("Note display ID or Convex ID"),
|
|
708
757
|
markdown: z.string().describe("New content as markdown"),
|
|
709
758
|
description: z.string().describe("Change description (required)"),
|
|
@@ -717,8 +766,8 @@ server.tool("cn_versions_create", "Create a new version of a note. Content is ma
|
|
|
717
766
|
// ---------------------------------------------------------------------------
|
|
718
767
|
// Workspace
|
|
719
768
|
// ---------------------------------------------------------------------------
|
|
720
|
-
server.tool("
|
|
721
|
-
server.tool("
|
|
769
|
+
server.tool("cnotes_workspace_list", "List all workspaces the authenticated user belongs to.", {}, async () => withErrorHandling(() => api.get("/api/workspaces")));
|
|
770
|
+
server.tool("cnotes_workspace_current", "Get details about the current workspace.", {}, async () => withErrorHandling(() => api.get(`/api/workspaces/${workspaceId}`)));
|
|
722
771
|
// ---------------------------------------------------------------------------
|
|
723
772
|
// Files
|
|
724
773
|
// ---------------------------------------------------------------------------
|
|
@@ -729,7 +778,7 @@ const FILE_MIME_MAP = {
|
|
|
729
778
|
".gif": "image/gif",
|
|
730
779
|
".webp": "image/webp",
|
|
731
780
|
};
|
|
732
|
-
server.tool("
|
|
781
|
+
server.tool("cnotes_files_upload", "Upload an image file to the workspace. Returns the public URL. Supports .jpg, .jpeg, .png, .gif, .webp (max 5MB).", {
|
|
733
782
|
filePath: z.string().describe("Absolute path to the image file on disk"),
|
|
734
783
|
}, async ({ filePath }) => withErrorHandling(async () => {
|
|
735
784
|
const absPath = path.resolve(filePath);
|
|
@@ -759,14 +808,14 @@ server.tool("cn_files_upload", "Upload an image file to the workspace. Returns t
|
|
|
759
808
|
const result = await api.postFormData("/api/upload-image", buildFormData);
|
|
760
809
|
return {
|
|
761
810
|
...result,
|
|
762
|
-
fullUrl: `${
|
|
763
|
-
markdown: ``,
|
|
764
813
|
};
|
|
765
814
|
}));
|
|
766
815
|
// ---------------------------------------------------------------------------
|
|
767
816
|
// Schema
|
|
768
817
|
// ---------------------------------------------------------------------------
|
|
769
|
-
server.tool("
|
|
818
|
+
server.tool("cnotes_schema", "Describe this workspace in one call: valid note types (with prefixes), common relationship slugs, resource field shapes, and the canvas node kinds. Call this once at the start of a session instead of guessing type names or reading docs.", {}, async () => withErrorHandling(() => buildSchema(api, workspaceId)));
|
|
770
819
|
// ---------------------------------------------------------------------------
|
|
771
820
|
// Start
|
|
772
821
|
// ---------------------------------------------------------------------------
|