@naumu/mcp 0.6.4 → 0.7.1
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/README.md +1 -1
- package/dist/index.js +507 -644
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -118,7 +118,7 @@ var NaumuClient = class {
|
|
|
118
118
|
// ../mcp-core/src/instructions.ts
|
|
119
119
|
var NAUMU_INSTRUCTIONS = `The Naumu MCP server gives structured access to Naumu knowledge graphs (also called spaces). Prefer these tools over WebFetch whenever the user mentions a naumu.ai URL \u2014 Naumu pages are client-rendered React, so WebFetch returns an empty shell with no data.
|
|
120
120
|
|
|
121
|
-
Getting information about a space:
|
|
121
|
+
Getting information about a space: use naumu_ask. It puts your question to the @Naumu agent (which has full read access and inspects the graph for you) and returns a synthesised, node-grounded answer with the exact source node ids and a confidence hint, in a single call. It is the authoritative answer for what is in a space, what is new or recently changed, how something works, or any summary - present it and its sources directly. Do NOT then re-read the graph yourself with naumu_get_schema, naumu_filter, naumu_get_node or fetch to verify or sanity-check the answer: that repeats work naumu_ask already did and is dramatically slower (it can turn a 40-second answer into minutes). Trust the answer and its cited sources. Reach for a granular read only to fetch one specific node the answer pointed to, or for a need naumu_ask genuinely cannot serve (naumu_search to locate nodes by meaning, naumu_list_threads + naumu_read_thread for conversation history). Recording the question and answer as a visible conversation in the space is expected and useful, so do not avoid naumu_ask to prevent creating a thread. To hand @Naumu work to carry out in the background (add knowledge, make changes, record a status update), use naumu_delegate.
|
|
122
122
|
|
|
123
123
|
IMPORTANT: graphId is a UUID (e.g. "0464cbfa-60ca-41b3-ac8f-bbeb8243a193"). The value in the URL right after /spaces/ is a slug (e.g. "naumu-0464cbfa"), NOT the graphId. You must resolve the slug to a graphId first.
|
|
124
124
|
|
|
@@ -127,20 +127,9 @@ Call naumu_list_graphs and find the graph whose 'slug' field matches the URL seg
|
|
|
127
127
|
|
|
128
128
|
URL \u2192 tool mapping (the value after /spaces/ is the slug \u2014 resolve it first):
|
|
129
129
|
- naumu.ai/spaces/{slug} \u2192 naumu_list_graphs (resolve), then naumu_get_schema for an overview
|
|
130
|
-
- naumu.ai/spaces/{slug}/views/{viewId} \u2192 naumu_list_graphs (resolve), then naumu_get_view, then naumu_list_view_nodes
|
|
131
130
|
- naumu.ai/spaces/{slug}/nodes/{nodeId} \u2192 naumu_list_graphs (resolve), then naumu_get_node
|
|
132
131
|
- naumu.ai/spaces/{slug}/chat/{threadId} \u2192 naumu_list_graphs (resolve), then naumu_read_thread
|
|
133
|
-
- Other panel URLs (notes, canvases, conversations, members, settings, schema, heat, health, changelog) have no dedicated tool \u2014 fall back to naumu_get_node with the relevant id, or naumu_get_schema for the space-level question.
|
|
134
|
-
|
|
135
|
-
Recommended workflow when a user pastes a view URL:
|
|
136
|
-
1. naumu_list_graphs \u2014 find the graph whose slug matches the URL. Note its 'id' (the UUID) as graphId.
|
|
137
|
-
2. naumu_get_view {graphId, viewId} \u2014 read the returned 'summary' and 'filters' to understand what the view returns. This is cheap.
|
|
138
|
-
3. naumu_list_view_nodes {graphId, viewId} \u2014 page through results.
|
|
139
|
-
- fields:"summary" (default) for {id, label, type} per node \u2014 best for browsing.
|
|
140
|
-
- fields:"id" for {id} only \u2014 best when you just need to count or iterate.
|
|
141
|
-
- fields:"full" for the complete node payload \u2014 only when you need every attribute.
|
|
142
|
-
- The response always includes totalCount, so you don't need to page through everything to get a count.
|
|
143
|
-
4. naumu_get_node {graphId, nodeId} for per-node detail when needed.
|
|
132
|
+
- Other panel URLs (notes, canvases, views, conversations, members, settings, schema, heat, health, changelog) have no dedicated tool \u2014 fall back to naumu_get_node with the relevant id, or naumu_get_schema for the space-level question.
|
|
144
133
|
|
|
145
134
|
Localhost URLs (http://localhost:3000/spaces/{slug}/...) follow the same shape \u2014 resolve the slug the same way. The MCP backend host is configured separately; the URL the user pastes is just for parsing structure.`;
|
|
146
135
|
|
|
@@ -151,7 +140,7 @@ function registerListGraphs(server2, client2) {
|
|
|
151
140
|
"naumu_list_graphs",
|
|
152
141
|
{
|
|
153
142
|
title: "List Graphs",
|
|
154
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
143
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
155
144
|
description: "List all knowledge graphs (spaces) the authenticated user has access to. Returns graph IDs, names, and roles.",
|
|
156
145
|
inputSchema: z.object({})
|
|
157
146
|
},
|
|
@@ -164,8 +153,44 @@ function registerListGraphs(server2, client2) {
|
|
|
164
153
|
);
|
|
165
154
|
}
|
|
166
155
|
|
|
167
|
-
// ../mcp-core/src/tools/
|
|
156
|
+
// ../mcp-core/src/tools/list-members.ts
|
|
168
157
|
import { z as z2 } from "zod";
|
|
158
|
+
function registerListMembers(server2, client2) {
|
|
159
|
+
server2.registerTool(
|
|
160
|
+
"naumu_list_members",
|
|
161
|
+
{
|
|
162
|
+
title: "List Members",
|
|
163
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
164
|
+
description: "List the members of a Naumu space (graph): each member's userId, name, email, and role. Use it to discover teammates and to get the id you pass when @mentioning a human via naumu_post_message (their userId or email both work).",
|
|
165
|
+
inputSchema: z2.object({
|
|
166
|
+
graphId: z2.string().describe("The space (graph) ID to list members of. You must be a member of this space.")
|
|
167
|
+
})
|
|
168
|
+
},
|
|
169
|
+
async ({ graphId }) => {
|
|
170
|
+
try {
|
|
171
|
+
const data = await client2.get(`/api/graphs/${graphId}/members`);
|
|
172
|
+
const members = Array.isArray(data) ? data.map((m) => ({
|
|
173
|
+
userId: m.userId,
|
|
174
|
+
name: m.name,
|
|
175
|
+
email: m.email,
|
|
176
|
+
role: m.role
|
|
177
|
+
})) : data;
|
|
178
|
+
return {
|
|
179
|
+
content: [{ type: "text", text: JSON.stringify(members, null, 2) }]
|
|
180
|
+
};
|
|
181
|
+
} catch (err) {
|
|
182
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
185
|
+
isError: true
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ../mcp-core/src/tools/create-graph.ts
|
|
193
|
+
import { z as z3 } from "zod";
|
|
169
194
|
function registerCreateGraph(server2, client2) {
|
|
170
195
|
server2.registerTool(
|
|
171
196
|
"naumu_create_graph",
|
|
@@ -173,8 +198,8 @@ function registerCreateGraph(server2, client2) {
|
|
|
173
198
|
title: "Create Graph",
|
|
174
199
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
175
200
|
description: "Create a new, empty knowledge graph (space) owned by the authenticated user; use when you need a fresh space to populate. Returns `{id, name, slug, role, memberRole, createdAt, onboardingThreadId}` - use the returned `id` as `graphId` for subsequent tool calls. The space starts empty (no schema, no nodes); follow up with `naumu_update_schema` to register types before any `naumu_add_node` calls.",
|
|
176
|
-
inputSchema:
|
|
177
|
-
name:
|
|
201
|
+
inputSchema: z3.object({
|
|
202
|
+
name: z3.string().min(1).describe("Display name for the new space. A URL slug is generated from this name.")
|
|
178
203
|
})
|
|
179
204
|
},
|
|
180
205
|
async ({ name }) => {
|
|
@@ -187,7 +212,7 @@ function registerCreateGraph(server2, client2) {
|
|
|
187
212
|
}
|
|
188
213
|
|
|
189
214
|
// ../mcp-core/src/tools/get-schema.ts
|
|
190
|
-
import { z as
|
|
215
|
+
import { z as z4 } from "zod";
|
|
191
216
|
function formatConnection(c) {
|
|
192
217
|
const out = { relation: c.relation };
|
|
193
218
|
if (c.polymorphic) {
|
|
@@ -219,8 +244,7 @@ function formatSchema(schema) {
|
|
|
219
244
|
if (v.description) value.description = v.description;
|
|
220
245
|
return value;
|
|
221
246
|
});
|
|
222
|
-
const attr = { name: a.name, values };
|
|
223
|
-
if (a.type) attr.type = a.type;
|
|
247
|
+
const attr = { name: a.name, type: a.type ?? "select", values };
|
|
224
248
|
if (a.description) attr.description = a.description;
|
|
225
249
|
return attr;
|
|
226
250
|
});
|
|
@@ -234,10 +258,10 @@ function registerGetSchema(server2, client2) {
|
|
|
234
258
|
"naumu_get_schema",
|
|
235
259
|
{
|
|
236
260
|
title: "Get Graph Schema",
|
|
237
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
261
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
238
262
|
description: "Get the schema definition for a knowledge graph; call this before classifying a new node into a type, before picking an attribute value, or before extending the schema. Returns a ROUND-TRIPPABLE structural schema: each `types[]` entry has the SAME shape `naumu_update_schema` ingests (type, description, structured `connections: { parent?, required, suggested }` with parent NESTED - not a string, and structured attributes), so you can copy a type straight back into naumu_update_schema without losing data (notably the parent relation). Descriptions are short, contrastive notes from the schema author explaining what each type/attribute/value is for and how it differs from similar-sounding ones.",
|
|
239
|
-
inputSchema:
|
|
240
|
-
graphId:
|
|
263
|
+
inputSchema: z4.object({
|
|
264
|
+
graphId: z4.string().describe("The graph ID")
|
|
241
265
|
})
|
|
242
266
|
},
|
|
243
267
|
async ({ graphId }) => {
|
|
@@ -259,40 +283,40 @@ function registerGetSchema(server2, client2) {
|
|
|
259
283
|
}
|
|
260
284
|
|
|
261
285
|
// ../mcp-core/src/tools/update-schema.ts
|
|
262
|
-
import { z as
|
|
263
|
-
var ConnectionSchema =
|
|
264
|
-
relation:
|
|
265
|
-
target_node:
|
|
266
|
-
polymorphic:
|
|
286
|
+
import { z as z5 } from "zod";
|
|
287
|
+
var ConnectionSchema = z5.object({
|
|
288
|
+
relation: z5.string().describe("UPPER_SNAKE_CASE relation name (e.g. WORKS_AT, BUILT_BY, BELONGS_TO)"),
|
|
289
|
+
target_node: z5.string().optional().describe("Target type name. Omit when polymorphic=true."),
|
|
290
|
+
polymorphic: z5.boolean().optional().describe("Set true when this relation can target multiple types.")
|
|
267
291
|
});
|
|
268
|
-
var AttributeValueSchema =
|
|
269
|
-
label:
|
|
270
|
-
color:
|
|
271
|
-
description:
|
|
292
|
+
var AttributeValueSchema = z5.object({
|
|
293
|
+
label: z5.string().describe("Display label for this enum value"),
|
|
294
|
+
color: z5.string().optional().describe('Optional hex color (e.g. "#ff5722")'),
|
|
295
|
+
description: z5.string().optional().describe('Short note distinguishing this value from sibling values (e.g. "closed-won - signed and revenue committed"). Encouraged when the label alone is ambiguous.')
|
|
272
296
|
});
|
|
273
|
-
var AttributeSchema =
|
|
274
|
-
name:
|
|
275
|
-
type:
|
|
276
|
-
values:
|
|
277
|
-
description:
|
|
297
|
+
var AttributeSchema = z5.object({
|
|
298
|
+
name: z5.string().describe('Attribute key (e.g. "stage", "status", "category")'),
|
|
299
|
+
type: z5.enum(["select", "multiselect", "string", "number", "date"]).describe('Attribute type (required). select/multiselect define a closed set of categories with values; string/number/date hold free-form per-node data with values []. Calendar dates use "date", not select values.'),
|
|
300
|
+
values: z5.array(AttributeValueSchema).describe("Allowed enum values for select/multiselect; pass [] for string/number/date."),
|
|
301
|
+
description: z5.string().optional().describe("Short note explaining what this attribute captures and how it differs from similarly-named attributes elsewhere in the schema. Strongly encouraged.")
|
|
278
302
|
});
|
|
279
|
-
var NodeTypeSchema =
|
|
280
|
-
type:
|
|
281
|
-
connections:
|
|
303
|
+
var NodeTypeSchema = z5.object({
|
|
304
|
+
type: z5.string().describe("Type name in PascalCase (e.g. TypeA, TypeB)"),
|
|
305
|
+
connections: z5.object({
|
|
282
306
|
parent: ConnectionSchema.optional().describe("Optional parent relation (this type nests under another via this connection)."),
|
|
283
|
-
required:
|
|
284
|
-
suggested:
|
|
307
|
+
required: z5.array(ConnectionSchema).default([]).describe("Required outgoing connections to other types."),
|
|
308
|
+
suggested: z5.array(ConnectionSchema).default([]).describe("Suggested-but-optional outgoing connections.")
|
|
285
309
|
}),
|
|
286
|
-
attributes:
|
|
287
|
-
defaultVisibility:
|
|
310
|
+
attributes: z5.array(AttributeSchema).optional().describe("Type-level attributes for instances of this type."),
|
|
311
|
+
defaultVisibility: z5.enum(["restricted", "internal", "open"]).optional().describe(
|
|
288
312
|
'Default visibility for NEW nodes of this type when no explicit visibility is passed on creation. Does NOT retroactively change visibility on existing nodes. "open" = visible to anyone with the space link (including non-members), "internal" = visible to all space members, "restricted" = only members explicitly granted access. Omit to leave the type unset - it then falls back to the space-level defaultVisibility.'
|
|
289
313
|
),
|
|
290
|
-
color:
|
|
291
|
-
description:
|
|
314
|
+
color: z5.string().optional().describe("Optional hex color for instances of this type."),
|
|
315
|
+
description: z5.string().optional().describe('Short one-sentence description of what this type represents AND how it differs from semantically similar types (e.g. "Type A - distinct from Type B, which is broader"). Strongly encouraged on every type. Future agents rely on this when classifying a new node into one of several similar-sounding types.')
|
|
292
316
|
});
|
|
293
|
-
var SchemaDefinitionSchema =
|
|
294
|
-
description:
|
|
295
|
-
nodes:
|
|
317
|
+
var SchemaDefinitionSchema = z5.object({
|
|
318
|
+
description: z5.string().optional().describe("Schema-level description / domain summary."),
|
|
319
|
+
nodes: z5.array(NodeTypeSchema).describe("All node types in the schema.")
|
|
296
320
|
});
|
|
297
321
|
function registerUpdateSchema(server2, client2) {
|
|
298
322
|
server2.registerTool(
|
|
@@ -300,9 +324,9 @@ function registerUpdateSchema(server2, client2) {
|
|
|
300
324
|
{
|
|
301
325
|
title: "Update Graph Schema",
|
|
302
326
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
303
|
-
description: "Replace the graph schema with a new full definition; use to bootstrap an empty graph (cold-start) or fully replace mid-build. STRUCTURAL RULES (load-bearing): (1) HIERARCHICAL with a single root - exactly ONE type has no parent; every other type MUST declare a parent. (2) Each parent / required / suggested connection is XOR: ONE concrete target_node OR polymorphic=true - never both, never a list of multiple targets. (3) DEFAULT TO CONCRETE target_node. Polymorphic is the ESCAPE HATCH - reserve for genuinely cross-cutting concepts like Comment or Tag (entities that validly attach to many distinct types). If you find yourself making most parents polymorphic, you are avoiding the design work - pick concrete relationships instead. If you can't pick a concrete parent, you may be missing a type - add the missing type first. (4) Same-type nesting is implicit: a node can be placed under another of its same type using the existing parent relation - never add a self-relation just to enable nesting. (5) Always provide `description` on every node type, attribute, and select value - one short, contrastive sentence (what it IS and what it is NOT vs sibling types/attrs/values). This is the single strongest disambiguation signal for future agents classifying nodes. When replacing mid-build, call naumu_get_schema first (it returns a round-trippable structural schema you can copy back) and send the FULL new schema; anything you omit is removed. PARENT PRESERVATION: omitting a type's `connections.parent` PRESERVES its existing parent (so a round-trip never silently orphans a type). To intentionally remove a parent and make a type a root, send `parent: null` explicitly. (A parent whose target type you delete is dropped automatically.) Conventions: type names PascalCase
|
|
304
|
-
inputSchema:
|
|
305
|
-
graphId:
|
|
327
|
+
description: "Replace the graph schema with a new full definition; use to bootstrap an empty graph (cold-start) or fully replace mid-build. STRUCTURAL RULES (load-bearing): (1) HIERARCHICAL with a single root - exactly ONE type has no parent; every other type MUST declare a parent. (2) Each parent / required / suggested connection is XOR: ONE concrete target_node OR polymorphic=true - never both, never a list of multiple targets. (3) DEFAULT TO CONCRETE target_node. Polymorphic is the ESCAPE HATCH - reserve for genuinely cross-cutting concepts like Comment or Tag (entities that validly attach to many distinct types). If you find yourself making most parents polymorphic, you are avoiding the design work - pick concrete relationships instead. If you can't pick a concrete parent, you may be missing a type - add the missing type first. (4) Same-type nesting is implicit: a node can be placed under another of its same type using the existing parent relation - never add a self-relation just to enable nesting. (5) Always provide `description` on every node type, attribute, and select value - one short, contrastive sentence (what it IS and what it is NOT vs sibling types/attrs/values). This is the single strongest disambiguation signal for future agents classifying nodes. When replacing mid-build, call naumu_get_schema first (it returns a round-trippable structural schema you can copy back) and send the FULL new schema; anything you omit is removed. PARENT PRESERVATION: omitting a type's `connections.parent` PRESERVES its existing parent (so a round-trip never silently orphans a type). To intentionally remove a parent and make a type a root, send `parent: null` explicitly. (A parent whose target type you delete is dropped automatically.) Conventions: type names PascalCase, relation names UPPER_SNAKE_CASE. Bias toward general types - refine via attributes or nested children, not type proliferation.",
|
|
328
|
+
inputSchema: z5.object({
|
|
329
|
+
graphId: z5.string().describe("The graph ID"),
|
|
306
330
|
schema: SchemaDefinitionSchema
|
|
307
331
|
})
|
|
308
332
|
},
|
|
@@ -318,22 +342,22 @@ function registerUpdateSchema(server2, client2) {
|
|
|
318
342
|
}
|
|
319
343
|
|
|
320
344
|
// ../mcp-core/src/tools/add-node-type.ts
|
|
321
|
-
import { z as
|
|
322
|
-
var ConnectionSchema2 =
|
|
323
|
-
relation:
|
|
324
|
-
target_node:
|
|
325
|
-
polymorphic:
|
|
345
|
+
import { z as z6 } from "zod";
|
|
346
|
+
var ConnectionSchema2 = z6.object({
|
|
347
|
+
relation: z6.string().describe("UPPER_SNAKE_CASE relation name (WORKS_AT, BELONGS_TO)."),
|
|
348
|
+
target_node: z6.string().optional().describe("Target type name. Omit when polymorphic=true."),
|
|
349
|
+
polymorphic: z6.boolean().optional().describe("True when this relation can target many types.")
|
|
326
350
|
});
|
|
327
|
-
var AttributeValueSchema2 =
|
|
328
|
-
label:
|
|
329
|
-
color:
|
|
330
|
-
description:
|
|
351
|
+
var AttributeValueSchema2 = z6.object({
|
|
352
|
+
label: z6.string(),
|
|
353
|
+
color: z6.string().optional(),
|
|
354
|
+
description: z6.string().optional().describe('Short note distinguishing this value from sibling values (e.g. for status="closed-won", "signed and revenue committed"). Encouraged when the label alone is ambiguous.')
|
|
331
355
|
});
|
|
332
|
-
var AttributeSchema2 =
|
|
333
|
-
name:
|
|
334
|
-
type:
|
|
335
|
-
values:
|
|
336
|
-
description:
|
|
356
|
+
var AttributeSchema2 = z6.object({
|
|
357
|
+
name: z6.string(),
|
|
358
|
+
type: z6.enum(["select", "multiselect", "string", "number", "date"]).describe('Attribute type (required). select/multiselect define a closed set of categories with values; string/number/date hold free-form per-node data with values []. Calendar dates use "date", not select values.'),
|
|
359
|
+
values: z6.array(AttributeValueSchema2).default([]),
|
|
360
|
+
description: z6.string().optional().describe('Short note explaining what this attribute captures and how it differs from similarly-named attributes on other types (e.g. "stage on Type A" vs "stage on Type B"). Strongly encouraged.')
|
|
337
361
|
});
|
|
338
362
|
function registerAddNodeType(server2, client2) {
|
|
339
363
|
server2.registerTool(
|
|
@@ -341,19 +365,19 @@ function registerAddNodeType(server2, client2) {
|
|
|
341
365
|
{
|
|
342
366
|
title: "Add Node Type to Schema",
|
|
343
367
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
344
|
-
description: 'Add one new node type to the schema; use to extend an existing schema without resending the whole thing (cheaper than naumu_update_schema). STRUCTURAL RULES: (1) Schemas are hierarchical with a single root. If schema is empty, the first type IS the root - omit `parent`. Every subsequent type MUST set `parent`. (2) DEFAULT TO CONCRETE - pass {relation, target_node: <existing type>}. Polymorphic is the ESCAPE HATCH - use {relation, polymorphic: true} ONLY for genuinely cross-cutting concepts like Comment or Tag (types that validly attach to many distinct parents). If you can\'t pick a concrete parent, you may be missing a type - add the missing type first instead of falling back to polymorphic. (3) Required/suggested connections also each take ONE concrete target_node OR polymorphic - never a list. Default concrete there too. (4) Type name PascalCase
|
|
345
|
-
inputSchema:
|
|
346
|
-
graphId:
|
|
347
|
-
type:
|
|
348
|
-
description:
|
|
368
|
+
description: 'Add one new node type to the schema; use to extend an existing schema without resending the whole thing (cheaper than naumu_update_schema). STRUCTURAL RULES: (1) Schemas are hierarchical with a single root. If schema is empty, the first type IS the root - omit `parent`. Every subsequent type MUST set `parent`. (2) DEFAULT TO CONCRETE - pass {relation, target_node: <existing type>}. Polymorphic is the ESCAPE HATCH - use {relation, polymorphic: true} ONLY for genuinely cross-cutting concepts like Comment or Tag (types that validly attach to many distinct parents). If you can\'t pick a concrete parent, you may be missing a type - add the missing type first instead of falling back to polymorphic. (3) Required/suggested connections also each take ONE concrete target_node OR polymorphic - never a list. Default concrete there too. (4) Type name PascalCase. Relation names UPPER_SNAKE_CASE. (5) ALWAYS provide `description` - one short sentence that says what this type is AND what it is NOT relative to semantically close types (e.g. "Type A \u2014 distinct from Type B, which is broader"). This is the single strongest signal future agents have when classifying a node into one of several similar-sounding types. Returns an error if the type already exists - use naumu_add_attribute / naumu_add_connection to extend it.',
|
|
369
|
+
inputSchema: z6.object({
|
|
370
|
+
graphId: z6.string(),
|
|
371
|
+
type: z6.string().describe("PascalCase type name"),
|
|
372
|
+
description: z6.string().optional().describe(
|
|
349
373
|
"Short one-sentence description of what this type represents and how it differs from sibling types. Contrastive (say what it IS and what it is NOT) is most useful. Strongly encouraged on every new type - agents reading the schema later rely on this to classify nodes."
|
|
350
374
|
),
|
|
351
375
|
parent: ConnectionSchema2.optional().describe("Optional parent connection - sets this type as a child of another type."),
|
|
352
|
-
required:
|
|
353
|
-
suggested:
|
|
354
|
-
attributes:
|
|
355
|
-
color:
|
|
356
|
-
defaultVisibility:
|
|
376
|
+
required: z6.array(ConnectionSchema2).optional(),
|
|
377
|
+
suggested: z6.array(ConnectionSchema2).optional(),
|
|
378
|
+
attributes: z6.array(AttributeSchema2).optional(),
|
|
379
|
+
color: z6.string().optional(),
|
|
380
|
+
defaultVisibility: z6.enum(["restricted", "internal", "open"]).optional().describe(
|
|
357
381
|
'Default visibility for NEW nodes of this type when no explicit visibility is passed on creation. Does NOT retroactively change existing nodes. "open" = visible to anyone with the space link, "internal" = visible to all space members, "restricted" = only members explicitly granted access. Omit to fall back to the space-level defaultVisibility.'
|
|
358
382
|
)
|
|
359
383
|
})
|
|
@@ -388,21 +412,21 @@ function registerAddNodeType(server2, client2) {
|
|
|
388
412
|
}
|
|
389
413
|
|
|
390
414
|
// ../mcp-core/src/tools/add-connection.ts
|
|
391
|
-
import { z as
|
|
415
|
+
import { z as z7 } from "zod";
|
|
392
416
|
function registerAddConnection(server2, client2) {
|
|
393
417
|
server2.registerTool(
|
|
394
418
|
"naumu_add_connection",
|
|
395
419
|
{
|
|
396
420
|
title: "Add Connection to Node Type",
|
|
397
421
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
398
|
-
description: 'Add one connection from an existing node type to another; use to extend a type\'s relations without resending the whole schema. `kind`: "parent" (sets/replaces - every non-root type needs exactly one parent; only ONE type in the whole schema has no parent), "required" (must exist on instances), "suggested" (optional). Each connection is XOR: ONE concrete target_node OR polymorphic=true - never both. DEFAULT TO CONCRETE target_node. Polymorphic is the ESCAPE HATCH - reserve for genuinely cross-cutting relations (e.g. a Tag-style relation that validly applies to many distinct types). If you can\'t pick a concrete target, you may be missing a type - add it first instead of falling back to polymorphic. Same-type nesting (e.g.
|
|
399
|
-
inputSchema:
|
|
400
|
-
graphId:
|
|
401
|
-
source_type:
|
|
402
|
-
relation:
|
|
403
|
-
target_node:
|
|
404
|
-
polymorphic:
|
|
405
|
-
kind:
|
|
422
|
+
description: 'Add one connection from an existing node type to another; use to extend a type\'s relations without resending the whole schema. `kind`: "parent" (sets/replaces - every non-root type needs exactly one parent; only ONE type in the whole schema has no parent), "required" (must exist on instances), "suggested" (optional). Each connection is XOR: ONE concrete target_node OR polymorphic=true - never both. DEFAULT TO CONCRETE target_node. Polymorphic is the ESCAPE HATCH - reserve for genuinely cross-cutting relations (e.g. a Tag-style relation that validly applies to many distinct types). If you can\'t pick a concrete target, you may be missing a type - add it first instead of falling back to polymorphic. Same-type nesting (e.g. Type A under Type A) does NOT need a new connection - use the existing parent relation. Relation name UPPER_SNAKE_CASE.',
|
|
423
|
+
inputSchema: z7.object({
|
|
424
|
+
graphId: z7.string(),
|
|
425
|
+
source_type: z7.string().describe("Existing node type to add the connection to."),
|
|
426
|
+
relation: z7.string().describe("UPPER_SNAKE_CASE relation name."),
|
|
427
|
+
target_node: z7.string().optional().describe("Target type. Omit when polymorphic=true."),
|
|
428
|
+
polymorphic: z7.boolean().optional(),
|
|
429
|
+
kind: z7.enum(["required", "suggested", "parent"]).default("suggested")
|
|
406
430
|
})
|
|
407
431
|
},
|
|
408
432
|
async ({ graphId, source_type, relation, target_node, polymorphic, kind }) => {
|
|
@@ -438,27 +462,27 @@ function registerAddConnection(server2, client2) {
|
|
|
438
462
|
}
|
|
439
463
|
|
|
440
464
|
// ../mcp-core/src/tools/add-attribute.ts
|
|
441
|
-
import { z as
|
|
465
|
+
import { z as z8 } from "zod";
|
|
442
466
|
function registerAddAttribute(server2, client2) {
|
|
443
467
|
server2.registerTool(
|
|
444
468
|
"naumu_add_attribute",
|
|
445
469
|
{
|
|
446
470
|
title: "Add Attribute to Node Type",
|
|
447
471
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
448
|
-
description: 'Add or extend an attribute on an existing node type; use to add a field or merge new enum values into an existing one. If the attribute name doesn\'t exist, it is created. If it exists and is a select/multiselect, new values are merged in (existing values kept).
|
|
449
|
-
inputSchema:
|
|
450
|
-
graphId:
|
|
451
|
-
node_type:
|
|
452
|
-
name:
|
|
453
|
-
type:
|
|
454
|
-
values:
|
|
455
|
-
|
|
456
|
-
label:
|
|
457
|
-
color:
|
|
458
|
-
description:
|
|
472
|
+
description: 'Add or extend an attribute on an existing node type; use to add a field or merge new enum values into an existing one. If the attribute name doesn\'t exist, it is created. If it exists and is a select/multiselect, new values are merged in (existing values kept). Date values on nodes are written via naumu_update_node as either an ISO date string "YYYY-MM-DD" (single day) or an object { start, end? } with ISO date strings (inclusive range). Strongly encouraged to provide `description` on the attribute and on each value - short, contrastive notes (e.g. attribute "stage - sales funnel position; distinct from status which captures health/blockers", value "closed-won - deal signed and revenue committed"). Descriptions are the single strongest signal future agents use when picking which attribute/value to set.',
|
|
473
|
+
inputSchema: z8.object({
|
|
474
|
+
graphId: z8.string(),
|
|
475
|
+
node_type: z8.string().describe("Existing node type to add the attribute to."),
|
|
476
|
+
name: z8.string().describe('Attribute key (e.g. "stage", "status").'),
|
|
477
|
+
type: z8.enum(["select", "multiselect", "string", "number", "date"]).describe('Attribute type (required). select/multiselect define a closed set of categories - provide their values. string/number/date hold free-form per-node data - pass values: []. A specific calendar date is per-node data, so use type "date" and write the date on the node, rather than adding the date as a select value.'),
|
|
478
|
+
values: z8.array(
|
|
479
|
+
z8.object({
|
|
480
|
+
label: z8.string(),
|
|
481
|
+
color: z8.string().optional(),
|
|
482
|
+
description: z8.string().optional().describe('Short note distinguishing this value from sibling values (e.g. "closed-won - deal signed and revenue committed"). Encouraged when the label alone is ambiguous.')
|
|
459
483
|
})
|
|
460
484
|
).default([]),
|
|
461
|
-
description:
|
|
485
|
+
description: z8.string().optional().describe('Short note explaining what this attribute captures and how it differs from similarly-named attributes elsewhere in the schema (e.g. "Sales funnel position; differs from `status` which captures health/blockers"). Strongly encouraged.')
|
|
462
486
|
})
|
|
463
487
|
},
|
|
464
488
|
async ({ graphId, node_type, name, type, values, description }) => {
|
|
@@ -492,21 +516,21 @@ function registerAddAttribute(server2, client2) {
|
|
|
492
516
|
}
|
|
493
517
|
|
|
494
518
|
// ../mcp-core/src/tools/search.ts
|
|
495
|
-
import { z as
|
|
519
|
+
import { z as z9 } from "zod";
|
|
496
520
|
function registerSearch(server2, client2) {
|
|
497
521
|
server2.registerTool(
|
|
498
522
|
"naumu_search",
|
|
499
523
|
{
|
|
500
524
|
title: "Search Graph",
|
|
501
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
525
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
502
526
|
description: 'Hybrid search over graph nodes; use for meaning-based lookup when you don\'t know the exact label. Combines exact-token text matching (good for UUIDs, proper nouns, specific labels) with semantic similarity (good for paraphrase and meaning), then fuses both rankings with Reciprocal Rank Fusion. Returns the top matches with a `matchedVia` tag - `both` is the highest-confidence signal, then `semantic`, then `text`. Use `naumu_filter` for structured queries by type and attributes (e.g. "all in-progress Tasks"). Tip: include both synonyms ("authentication login SSO") and exact tokens you remember in the same query - the fusion handles both.',
|
|
503
|
-
inputSchema:
|
|
504
|
-
graphId:
|
|
505
|
-
query:
|
|
527
|
+
inputSchema: z9.object({
|
|
528
|
+
graphId: z9.string().describe("The graph ID"),
|
|
529
|
+
query: z9.string().describe(
|
|
506
530
|
'Search query text. Mix synonyms and exact tokens freely (e.g. "auth login SSO 2fa Twitter handle").'
|
|
507
531
|
),
|
|
508
|
-
limit:
|
|
509
|
-
nodeTypes:
|
|
532
|
+
limit: z9.number().optional().default(20).describe("Max results to return (default 20, max 200). Adaptive cutoff may return fewer when the top match is weak."),
|
|
533
|
+
nodeTypes: z9.array(z9.string()).optional().describe('Filter to specific node types (e.g. ["Type A", "Type B"])')
|
|
510
534
|
})
|
|
511
535
|
},
|
|
512
536
|
async ({ graphId, query, limit, nodeTypes }) => {
|
|
@@ -523,25 +547,25 @@ function registerSearch(server2, client2) {
|
|
|
523
547
|
}
|
|
524
548
|
|
|
525
549
|
// ../mcp-core/src/tools/filter.ts
|
|
526
|
-
import { z as
|
|
550
|
+
import { z as z10 } from "zod";
|
|
527
551
|
function registerFilter(server2, client2) {
|
|
528
552
|
server2.registerTool(
|
|
529
553
|
"naumu_filter",
|
|
530
554
|
{
|
|
531
555
|
title: "Filter Graph Nodes",
|
|
532
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
556
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
533
557
|
description: 'Filter nodes by type and attributes with deterministic, complete results; use for structured queries like "all in-progress Tasks" or "Bugs not yet resolved." Unlike search tools, this returns every matching node (up to the limit) - no semantic ranking, no missed results. Results are sorted by sortKey or recency.',
|
|
534
|
-
inputSchema:
|
|
535
|
-
graphId:
|
|
536
|
-
nodeTypes:
|
|
537
|
-
includeAttributes:
|
|
558
|
+
inputSchema: z10.object({
|
|
559
|
+
graphId: z10.string().describe("The graph ID"),
|
|
560
|
+
nodeTypes: z10.array(z10.string()).optional().describe('Filter to specific node types (e.g. ["Type A", "Type B"])'),
|
|
561
|
+
includeAttributes: z10.record(z10.string(), z10.array(z10.string())).optional().describe(
|
|
538
562
|
'Only include nodes where attribute matches one of the values. Example: {"Status": ["Todo", "In Progress"]}'
|
|
539
563
|
),
|
|
540
|
-
excludeAttributes:
|
|
564
|
+
excludeAttributes: z10.record(z10.string(), z10.array(z10.string())).optional().describe(
|
|
541
565
|
'Exclude nodes where attribute matches any of the values. Example: {"Status": ["Done", "Wont do"]}'
|
|
542
566
|
),
|
|
543
|
-
sortBy:
|
|
544
|
-
limit:
|
|
567
|
+
sortBy: z10.enum(["sortKey", "updatedAt", "label"]).optional().default("sortKey").describe('Sort order: "sortKey" (default), "updatedAt" (most recent first), or "label" (alphabetical)'),
|
|
568
|
+
limit: z10.number().optional().default(50).describe("Max results to return (default 50, max 200)")
|
|
545
569
|
})
|
|
546
570
|
},
|
|
547
571
|
async ({ graphId, nodeTypes, includeAttributes, excludeAttributes, sortBy, limit }) => {
|
|
@@ -566,17 +590,17 @@ function registerFilter(server2, client2) {
|
|
|
566
590
|
}
|
|
567
591
|
|
|
568
592
|
// ../mcp-core/src/tools/get-node.ts
|
|
569
|
-
import { z as
|
|
593
|
+
import { z as z11 } from "zod";
|
|
570
594
|
function registerGetNode(server2, client2) {
|
|
571
595
|
server2.registerTool(
|
|
572
596
|
"naumu_get_node",
|
|
573
597
|
{
|
|
574
598
|
title: "Get Node",
|
|
575
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
599
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
576
600
|
description: "Get a single node with all its properties and connections (incoming and outgoing edges).",
|
|
577
|
-
inputSchema:
|
|
578
|
-
graphId:
|
|
579
|
-
nodeId:
|
|
601
|
+
inputSchema: z11.object({
|
|
602
|
+
graphId: z11.string().describe("The graph ID"),
|
|
603
|
+
nodeId: z11.string().describe("The node ID")
|
|
580
604
|
})
|
|
581
605
|
},
|
|
582
606
|
async ({ graphId, nodeId }) => {
|
|
@@ -589,38 +613,7 @@ function registerGetNode(server2, client2) {
|
|
|
589
613
|
}
|
|
590
614
|
|
|
591
615
|
// ../mcp-core/src/tools/add-node.ts
|
|
592
|
-
import { z as
|
|
593
|
-
var NodeInput = z11.object({
|
|
594
|
-
label: z11.string().describe("Display name of the node"),
|
|
595
|
-
type: z11.string().describe("Node type from the graph schema (e.g. Feature, Pain, Metric)"),
|
|
596
|
-
content: z11.string().min(1).describe("Rich text content / description. REQUIRED - every node must explain what it is."),
|
|
597
|
-
attributes: z11.record(z11.string(), z11.unknown()).optional().describe("Additional key-value attributes")
|
|
598
|
-
});
|
|
599
|
-
function registerAddNode(server2, client2) {
|
|
600
|
-
server2.registerTool(
|
|
601
|
-
"naumu_add_node",
|
|
602
|
-
{
|
|
603
|
-
title: "Add Nodes (bulk)",
|
|
604
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
605
|
-
description: 'Create 1-25 nodes in the knowledge graph in a single call; use when you have a vetted, dedup-checked batch ready to insert. Keep batches small and atomic (5-25 nodes) so failures stay contained. Each node MUST include a non-empty `content` describing what it is. Returns one entry per input node with `{id, label, type, status: "created"}` - there is NO server-side dedup, every input becomes a node. Dedup is the caller\'s responsibility: BEFORE calling this tool, run `naumu_search` on each candidate label and skip/route to update if a result has high similarity (\u22650.78) and matching type. Warning: nodes are isolated until you connect them with `naumu_add_edge`. Prefer `naumu_delegate` for general knowledge intake - it discovers and creates connections for you.',
|
|
606
|
-
inputSchema: z11.object({
|
|
607
|
-
graphId: z11.string().describe("The graph ID"),
|
|
608
|
-
nodes: z11.array(NodeInput).min(1).max(25).describe("Batch of 1\u201325 nodes to create. Keep batches small for atomicity.")
|
|
609
|
-
})
|
|
610
|
-
},
|
|
611
|
-
async ({ graphId, nodes }) => {
|
|
612
|
-
const payload = nodes.map(({ label, type, content, attributes }) => {
|
|
613
|
-
const node = { label, type, content };
|
|
614
|
-
if (attributes) Object.assign(node, attributes);
|
|
615
|
-
return node;
|
|
616
|
-
});
|
|
617
|
-
const data = await client2.post(`/api/graphs/${graphId}/nodes`, { nodes: payload });
|
|
618
|
-
return {
|
|
619
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
);
|
|
623
|
-
}
|
|
616
|
+
import { z as z13 } from "zod";
|
|
624
617
|
|
|
625
618
|
// ../mcp-core/src/tools/update-node.ts
|
|
626
619
|
import { z as z12 } from "zod";
|
|
@@ -752,13 +745,120 @@ Use naumu_get_schema to check valid attributes and values for this node type.`
|
|
|
752
745
|
);
|
|
753
746
|
}
|
|
754
747
|
|
|
748
|
+
// ../mcp-core/src/tools/add-node.ts
|
|
749
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["id", "label", "type", "content"]);
|
|
750
|
+
function normalizeNodeAttributes(schema, nodeType, attributes) {
|
|
751
|
+
const attrMap = /* @__PURE__ */ new Map();
|
|
752
|
+
const nodeDef = schema.nodes.find((n) => n.type === nodeType);
|
|
753
|
+
for (const attr of nodeDef?.attributes ?? []) {
|
|
754
|
+
attrMap.set(attr.name.toLowerCase(), attr);
|
|
755
|
+
}
|
|
756
|
+
const normalized = {};
|
|
757
|
+
const errors = [];
|
|
758
|
+
for (const [key, val] of Object.entries(attributes)) {
|
|
759
|
+
const lower = key.toLowerCase();
|
|
760
|
+
if (RESERVED_KEYS.has(lower)) {
|
|
761
|
+
errors.push(`Attribute "${key}" conflicts with a reserved node field.`);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
const schemaDef = attrMap.get(lower);
|
|
765
|
+
if (!schemaDef) {
|
|
766
|
+
const validNames = [...attrMap.values()].map((a) => a.name);
|
|
767
|
+
errors.push(
|
|
768
|
+
`Unknown attribute "${key}" for type "${nodeType}". Valid attributes: ${validNames.length > 0 ? validNames.join(", ") : "none"}.`
|
|
769
|
+
);
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (schemaDef.type === "date") {
|
|
773
|
+
const result = validateDateAttribute(schemaDef.name, val);
|
|
774
|
+
if (!result.ok) {
|
|
775
|
+
errors.push(result.error);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (result.value === null) continue;
|
|
779
|
+
const range = typeof result.value === "string" ? { start: result.value } : result.value;
|
|
780
|
+
normalized[lower] = JSON.stringify(range);
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const validValues = schemaDef.values.map((v) => v.label);
|
|
784
|
+
if (typeof val === "string" && validValues.length > 0 && !validValues.includes(val)) {
|
|
785
|
+
errors.push(
|
|
786
|
+
`Invalid value "${val}" for attribute "${schemaDef.name}" on type "${nodeType}". Valid values: ${validValues.join(", ")}.`
|
|
787
|
+
);
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
normalized[lower] = val;
|
|
791
|
+
}
|
|
792
|
+
return errors.length > 0 ? { ok: false, errors } : { ok: true, attributes: normalized };
|
|
793
|
+
}
|
|
794
|
+
var NodeInput = z13.object({
|
|
795
|
+
label: z13.string().describe("Display name of the node"),
|
|
796
|
+
type: z13.string().describe("Node type from the graph schema"),
|
|
797
|
+
content: z13.string().min(1).describe("Rich text content / description. REQUIRED - every node must explain what it is."),
|
|
798
|
+
attributes: z13.record(z13.string(), z13.unknown()).optional().describe(
|
|
799
|
+
`Schema-defined attributes. Keys are matched case-insensitively against the node type's schema attributes and stored in canonical form; unknown attributes or invalid select values reject the whole batch. Date attributes accept "YYYY-MM-DD" or { start, end? }.`
|
|
800
|
+
)
|
|
801
|
+
});
|
|
802
|
+
function registerAddNode(server2, client2) {
|
|
803
|
+
server2.registerTool(
|
|
804
|
+
"naumu_add_node",
|
|
805
|
+
{
|
|
806
|
+
title: "Add Nodes (bulk)",
|
|
807
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
808
|
+
description: 'Create 1-25 nodes in the knowledge graph in a single call; use when you have a vetted, dedup-checked batch ready to insert. Keep batches small and atomic (5-25 nodes) so failures stay contained. Each node MUST include a non-empty `content` describing what it is. Returns one entry per input node with `{id, label, type, status: "created"}` - there is NO server-side dedup, every input becomes a node. Dedup is the caller\'s responsibility: BEFORE calling this tool, run `naumu_search` on each candidate label and skip/route to update if a result has high similarity (\u22650.78) and matching type. Warning: nodes are isolated until you connect them with `naumu_add_edge`. Prefer `naumu_delegate` for general knowledge intake - it discovers and creates connections for you.',
|
|
809
|
+
inputSchema: z13.object({
|
|
810
|
+
graphId: z13.string().describe("The graph ID"),
|
|
811
|
+
nodes: z13.array(NodeInput).min(1).max(25).describe("Batch of 1\u201325 nodes to create. Keep batches small for atomicity.")
|
|
812
|
+
})
|
|
813
|
+
},
|
|
814
|
+
async ({ graphId, nodes }) => {
|
|
815
|
+
let schema = null;
|
|
816
|
+
if (nodes.some((n) => n.attributes && Object.keys(n.attributes).length > 0)) {
|
|
817
|
+
const schemaRes = await client2.get(`/api/graphs/${graphId}/schema`);
|
|
818
|
+
schema = JSON.parse(schemaRes.definition);
|
|
819
|
+
}
|
|
820
|
+
const errors = [];
|
|
821
|
+
const payload = nodes.map(({ label, type, content, attributes }, i) => {
|
|
822
|
+
const node = { label, type, content };
|
|
823
|
+
if (schema && attributes && Object.keys(attributes).length > 0) {
|
|
824
|
+
const result = normalizeNodeAttributes(schema, type, attributes);
|
|
825
|
+
if (result.ok) {
|
|
826
|
+
Object.assign(node, result.attributes);
|
|
827
|
+
} else {
|
|
828
|
+
errors.push(...result.errors.map((e) => `nodes[${i}] ("${label}"): ${e}`));
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return node;
|
|
832
|
+
});
|
|
833
|
+
if (errors.length > 0) {
|
|
834
|
+
return {
|
|
835
|
+
content: [
|
|
836
|
+
{
|
|
837
|
+
type: "text",
|
|
838
|
+
text: `Attribute validation failed \u2014 no nodes were created:
|
|
839
|
+
${errors.join("\n")}
|
|
840
|
+
|
|
841
|
+
Use naumu_get_schema to check valid attribute names, types, and values.`
|
|
842
|
+
}
|
|
843
|
+
],
|
|
844
|
+
isError: true
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
const data = await client2.post(`/api/graphs/${graphId}/nodes`, { nodes: payload });
|
|
848
|
+
return {
|
|
849
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
755
855
|
// ../mcp-core/src/tools/add-edge.ts
|
|
756
|
-
import { z as
|
|
757
|
-
var EdgeInput =
|
|
758
|
-
source:
|
|
759
|
-
target:
|
|
760
|
-
label:
|
|
761
|
-
isParent:
|
|
856
|
+
import { z as z14 } from "zod";
|
|
857
|
+
var EdgeInput = z14.object({
|
|
858
|
+
source: z14.string().describe("Source node ID"),
|
|
859
|
+
target: z14.string().describe("Target node ID"),
|
|
860
|
+
label: z14.string().describe("Relationship type (e.g. RELATES_TO, SOLVES, TRACKS)"),
|
|
861
|
+
isParent: z14.boolean().optional().describe("Whether this is a parent relationship")
|
|
762
862
|
});
|
|
763
863
|
function registerAddEdge(server2, client2) {
|
|
764
864
|
server2.registerTool(
|
|
@@ -767,9 +867,9 @@ function registerAddEdge(server2, client2) {
|
|
|
767
867
|
title: "Add Edges (bulk)",
|
|
768
868
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
769
869
|
description: "Create 1-25 relationships (edges) between existing nodes in a single call; use after a bulk node insert to wire nodes together. Keep batches small and atomic (5-25 edges) so failures stay contained. Typical workflow: create nodes with `naumu_add_node`, then wire them up with batched `naumu_add_edge` calls. Direction matters and must match the schema: each edge's `(source.type, label, target.type)` tuple should appear in the schema's `connections` or `parent` for the source type. When the backend runs with `STRICT_EDGE_VALIDATION=true`, invalid tuples are rejected immediately with `error: invalid_edge` (the response includes `details.allowed_targets_for_relation` and a `hint` for routing the call); otherwise the edge persists and surfaces later as an `invalid_connection_target` / `parent_mismatch` violation. Either way, check `naumu_get_schema` and flip or drop offending edges before calling. Prefer `naumu_delegate` when you want the agent to discover the right connections itself.",
|
|
770
|
-
inputSchema:
|
|
771
|
-
graphId:
|
|
772
|
-
edges:
|
|
870
|
+
inputSchema: z14.object({
|
|
871
|
+
graphId: z14.string().describe("The graph ID"),
|
|
872
|
+
edges: z14.array(EdgeInput).min(1).max(25).describe("Batch of 1\u201325 edges to create. Keep batches small for atomicity.")
|
|
773
873
|
})
|
|
774
874
|
},
|
|
775
875
|
async ({ graphId, edges }) => {
|
|
@@ -788,7 +888,7 @@ function registerAddEdge(server2, client2) {
|
|
|
788
888
|
}
|
|
789
889
|
|
|
790
890
|
// ../mcp-core/src/tools/remove-node.ts
|
|
791
|
-
import { z as
|
|
891
|
+
import { z as z15 } from "zod";
|
|
792
892
|
function registerRemoveNode(server2, client2) {
|
|
793
893
|
server2.registerTool(
|
|
794
894
|
"naumu_remove_node",
|
|
@@ -796,9 +896,9 @@ function registerRemoveNode(server2, client2) {
|
|
|
796
896
|
title: "Remove Node",
|
|
797
897
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
798
898
|
description: "Delete a node and all its connections from the knowledge graph; use only when you need precise, surgical deletion. This is destructive. Prefer `naumu_delegate` for removing knowledge - it understands the impact on the broader graph and can handle cascading changes.",
|
|
799
|
-
inputSchema:
|
|
800
|
-
graphId:
|
|
801
|
-
nodeId:
|
|
899
|
+
inputSchema: z15.object({
|
|
900
|
+
graphId: z15.string().describe("The graph ID"),
|
|
901
|
+
nodeId: z15.string().describe("The node ID to delete")
|
|
802
902
|
})
|
|
803
903
|
},
|
|
804
904
|
async ({ graphId, nodeId }) => {
|
|
@@ -811,7 +911,7 @@ function registerRemoveNode(server2, client2) {
|
|
|
811
911
|
}
|
|
812
912
|
|
|
813
913
|
// ../mcp-core/src/tools/remove-edge.ts
|
|
814
|
-
import { z as
|
|
914
|
+
import { z as z16 } from "zod";
|
|
815
915
|
function registerRemoveEdge(server2, client2) {
|
|
816
916
|
server2.registerTool(
|
|
817
917
|
"naumu_remove_edge",
|
|
@@ -819,11 +919,11 @@ function registerRemoveEdge(server2, client2) {
|
|
|
819
919
|
title: "Remove Edge",
|
|
820
920
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
821
921
|
description: "Delete a single edge identified by `(source, target, label)` tuple; use to correct a wrong-target edge mistake (a recovery tool, not a routine one). Does NOT delete the endpoint nodes - only the edge between them. System edges (HAS_NODE, HAS_THREAD, OWNS, etc.) are rejected with `error: system_edge_not_removable`. Parent edges (`isParent: true`) are removable but the response includes a warning that the child may now be orphaned - call `naumu_reparent` first if you want connectivity preserved. Response: `{deleted: 0 | 1, warnings: string[]}`. Prefer `naumu_delegate` when the structural intent is broader than removing one specific edge - it can reason about the surrounding graph.",
|
|
822
|
-
inputSchema:
|
|
823
|
-
graphId:
|
|
824
|
-
source:
|
|
825
|
-
target:
|
|
826
|
-
label:
|
|
922
|
+
inputSchema: z16.object({
|
|
923
|
+
graphId: z16.string().describe("The graph ID"),
|
|
924
|
+
source: z16.string().describe("Source node id of the edge to delete"),
|
|
925
|
+
target: z16.string().describe("Target node id of the edge to delete"),
|
|
926
|
+
label: z16.string().describe('Relation label of the edge to delete (e.g. "AUTHORED"). Case-insensitive; non-alphanum chars are normalized.')
|
|
827
927
|
})
|
|
828
928
|
},
|
|
829
929
|
async ({ graphId, source, target, label }) => {
|
|
@@ -840,11 +940,11 @@ function registerRemoveEdge(server2, client2) {
|
|
|
840
940
|
}
|
|
841
941
|
|
|
842
942
|
// ../mcp-core/src/tools/remove-edges-bulk.ts
|
|
843
|
-
import { z as
|
|
844
|
-
var EdgeRef =
|
|
845
|
-
source:
|
|
846
|
-
target:
|
|
847
|
-
label:
|
|
943
|
+
import { z as z17 } from "zod";
|
|
944
|
+
var EdgeRef = z17.object({
|
|
945
|
+
source: z17.string().describe("Source node ID"),
|
|
946
|
+
target: z17.string().describe("Target node ID"),
|
|
947
|
+
label: z17.string().describe("Relation label of the edge to delete")
|
|
848
948
|
});
|
|
849
949
|
function registerRemoveEdgesBulk(server2, client2) {
|
|
850
950
|
server2.registerTool(
|
|
@@ -853,9 +953,9 @@ function registerRemoveEdgesBulk(server2, client2) {
|
|
|
853
953
|
title: "Remove Edges (bulk)",
|
|
854
954
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
855
955
|
description: "Delete 1-100 edges in a single atomic call (all-or-none); use to fix multiple wrong-target edge mistakes in one shot. Does NOT delete endpoint nodes. System edges (HAS_NODE, HAS_THREAD, OWNS, etc.) are rejected - if any edge in the batch targets a system relation, the WHOLE batch is rejected with `error: system_edge_not_removable`. Parent edges (`isParent: true`) are removable but each parent removal contributes a warning to the response (`{deleted, warnings: string[]}`) - call `naumu_reparent` first if you want connectivity preserved. Prefer `naumu_delegate` for broader structural cleanup that needs graph-wide reasoning.",
|
|
856
|
-
inputSchema:
|
|
857
|
-
graphId:
|
|
858
|
-
edges:
|
|
956
|
+
inputSchema: z17.object({
|
|
957
|
+
graphId: z17.string().describe("The graph ID"),
|
|
958
|
+
edges: z17.array(EdgeRef).min(1).max(100).describe("1-100 edges to delete. Atomic per call - all succeed or none do.")
|
|
859
959
|
})
|
|
860
960
|
},
|
|
861
961
|
async ({ graphId, edges }) => {
|
|
@@ -868,7 +968,7 @@ function registerRemoveEdgesBulk(server2, client2) {
|
|
|
868
968
|
}
|
|
869
969
|
|
|
870
970
|
// ../mcp-core/src/tools/ask.ts
|
|
871
|
-
import { z as
|
|
971
|
+
import { z as z18 } from "zod";
|
|
872
972
|
function registerAsk(server2, client2) {
|
|
873
973
|
server2.registerTool(
|
|
874
974
|
"naumu_ask",
|
|
@@ -883,15 +983,21 @@ function registerAsk(server2, client2) {
|
|
|
883
983
|
idempotentHint: false,
|
|
884
984
|
openWorldHint: true
|
|
885
985
|
},
|
|
886
|
-
description: 'Ask @Naumu a question about a space and get a synthesised answer
|
|
887
|
-
inputSchema:
|
|
888
|
-
graphId:
|
|
889
|
-
question:
|
|
986
|
+
description: 'Ask @Naumu a question about a space and get back a synthesised, node-grounded answer with the exact source node ids it used and a confidence hint. This is THE tool for any question about a space - what is in it, what is new or recently changed, how something works, or a summary of any topic. @Naumu has full read access and has already inspected the graph for you, so its answer is authoritative: present it and its cited sources directly. Do NOT independently re-read the graph (naumu_get_schema, naumu_filter, naumu_get_node, fetch) to verify or flesh out the answer - that repeats work @Naumu already did and is far slower. Reach for a granular read ONLY to pull a specific node the answer pointed to but did not fully include. Returns { answer, sources, confidence, threadId, status }; the answer is saved as a visible conversation in the space, which is expected and useful, so do not avoid the tool to prevent creating a thread. On a very long synthesis it may return status "processing" with a threadId - in that case wait a few seconds and call naumu_read_thread with that threadId for the final answer, and still do not fall back to manual digging. To hand @Naumu work to carry out (add knowledge, make changes, record status) without waiting, use naumu_delegate instead.',
|
|
987
|
+
inputSchema: z18.object({
|
|
988
|
+
graphId: z18.string().describe("The space (graph) id to ask about."),
|
|
989
|
+
question: z18.string().max(4e3).describe("The question for @Naumu. Up to 4000 characters."),
|
|
990
|
+
topicIds: z18.array(z18.string()).max(8).optional().describe(
|
|
991
|
+
"Topic ids (from naumu_list_topics) to file the resulting conversation into, making it visible to those topics' members from birth. Omit to keep the default behavior (the ask lands in the virtual #misc bucket)."
|
|
992
|
+
)
|
|
890
993
|
})
|
|
891
994
|
},
|
|
892
|
-
async ({ graphId, question }) => {
|
|
995
|
+
async ({ graphId, question, topicIds }) => {
|
|
893
996
|
try {
|
|
894
|
-
const data = await client2.post(`/api/graphs/${graphId}/ask`, {
|
|
997
|
+
const data = await client2.post(`/api/graphs/${graphId}/ask`, {
|
|
998
|
+
question,
|
|
999
|
+
...topicIds && topicIds.length > 0 ? { topicIds } : {}
|
|
1000
|
+
});
|
|
895
1001
|
return {
|
|
896
1002
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
897
1003
|
};
|
|
@@ -907,7 +1013,7 @@ function registerAsk(server2, client2) {
|
|
|
907
1013
|
}
|
|
908
1014
|
|
|
909
1015
|
// ../mcp-core/src/tools/delegate.ts
|
|
910
|
-
import { z as
|
|
1016
|
+
import { z as z19 } from "zod";
|
|
911
1017
|
function registerDelegate(server2, client2) {
|
|
912
1018
|
server2.registerTool(
|
|
913
1019
|
"naumu_delegate",
|
|
@@ -922,23 +1028,30 @@ function registerDelegate(server2, client2) {
|
|
|
922
1028
|
idempotentHint: false,
|
|
923
1029
|
openWorldHint: true
|
|
924
1030
|
},
|
|
925
|
-
description: 'Hand @Naumu a task to carry out asynchronously: add knowledge, make graph-aware changes, or record a status update (e.g. "mark task X done", "log this deployment"). Returns immediately with a threadId; @Naumu works in the background and may take seconds to minutes. For status reports you can fire and forget. To read what @Naumu did, poll naumu_read_thread with the returned threadId. When you instead need an answer synchronously, use naumu_ask.',
|
|
926
|
-
inputSchema:
|
|
927
|
-
graphId:
|
|
928
|
-
task:
|
|
929
|
-
threadId:
|
|
1031
|
+
description: 'Hand @Naumu a task to carry out asynchronously: add knowledge, make graph-aware changes, or record a status update (e.g. "mark task X done", "log this deployment"). Returns immediately with a threadId; @Naumu works in the background and may take seconds to minutes. For status reports you can fire and forget. To read what @Naumu did, poll naumu_read_thread with the returned threadId. When you instead need an answer synchronously, use naumu_ask. File the new thread into topics by passing `topicIds` (see naumu_list_topics): omitting them keeps the default private thread, providing them makes it visible to those topics\' members from birth. This always invokes @Naumu, even in threads where auto-response is paused, so the task text must never contain a literal "@Naumu" mention (plain text never renders or triggers a mention).',
|
|
1032
|
+
inputSchema: z19.object({
|
|
1033
|
+
graphId: z19.string().describe("The space (graph) id to act in."),
|
|
1034
|
+
task: z19.string().describe("What you want @Naumu to do, add, or record."),
|
|
1035
|
+
threadId: z19.string().optional().describe("Continue an existing conversation; omit to start a new one."),
|
|
1036
|
+
topicIds: z19.array(z19.string()).max(8).optional().describe(
|
|
1037
|
+
"Topic ids (from naumu_list_topics) to file a NEW thread into, making it visible to those topics' members. Only used at creation; never adds topics to an existing thread, so it is ignored when threadId is provided. Omit to keep the default private thread."
|
|
1038
|
+
)
|
|
930
1039
|
})
|
|
931
1040
|
},
|
|
932
|
-
async ({ graphId, task, threadId }) => {
|
|
1041
|
+
async ({ graphId, task, threadId, topicIds }) => {
|
|
933
1042
|
try {
|
|
934
1043
|
let resolvedThreadId = threadId;
|
|
935
1044
|
if (!resolvedThreadId) {
|
|
936
|
-
const thread = await client2.post("/api/threads", {
|
|
1045
|
+
const thread = await client2.post("/api/threads", {
|
|
1046
|
+
graphId,
|
|
1047
|
+
...topicIds && topicIds.length > 0 ? { topicIds } : {}
|
|
1048
|
+
});
|
|
937
1049
|
resolvedThreadId = thread.id;
|
|
938
1050
|
}
|
|
939
1051
|
await client2.post(`/api/threads/${resolvedThreadId}/messages`, {
|
|
940
1052
|
content: task,
|
|
941
|
-
async: true
|
|
1053
|
+
async: true,
|
|
1054
|
+
invokeAgent: true
|
|
942
1055
|
});
|
|
943
1056
|
return {
|
|
944
1057
|
content: [
|
|
@@ -963,184 +1076,8 @@ function registerDelegate(server2, client2) {
|
|
|
963
1076
|
);
|
|
964
1077
|
}
|
|
965
1078
|
|
|
966
|
-
// ../mcp-core/src/tools/get-view.ts
|
|
967
|
-
import { z as z19 } from "zod";
|
|
968
|
-
function registerGetView(server2, client2) {
|
|
969
|
-
server2.registerTool(
|
|
970
|
-
"naumu_get_view",
|
|
971
|
-
{
|
|
972
|
-
title: "Get View",
|
|
973
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
974
|
-
description: "Fetch a saved Naumu view's configuration plus a one-line natural-language summary of its filters; call this BEFORE naumu_list_view_nodes whenever you encounter a view URL. The summary tells you what data the view returns so you can decide whether to page through it. Returns: id, name, description, summary, filters, and table column config.",
|
|
975
|
-
inputSchema: z19.object({
|
|
976
|
-
graphId: z19.string().describe("The graph (space) ID. From naumu.ai URLs this is the value after /spaces/."),
|
|
977
|
-
viewId: z19.string().describe('The view ID (typically prefixed with "view_").')
|
|
978
|
-
})
|
|
979
|
-
},
|
|
980
|
-
async ({ graphId, viewId }) => {
|
|
981
|
-
const data = await client2.get(`/api/graphs/${graphId}/views/${viewId}`);
|
|
982
|
-
return {
|
|
983
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// ../mcp-core/src/tools/list-view-nodes.ts
|
|
990
|
-
import { z as z20 } from "zod";
|
|
991
|
-
function registerListViewNodes(server2, client2) {
|
|
992
|
-
server2.registerTool(
|
|
993
|
-
"naumu_list_view_nodes",
|
|
994
|
-
{
|
|
995
|
-
title: "List View Nodes",
|
|
996
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
997
|
-
description: 'List nodes that match a saved view; pair with naumu_get_view first to read the view\'s filters and decide what payload size you need. Cursor-paginated.\n\nResponse shape: { nodes, totalCount, nextCursor, hasMore }. Use totalCount to know how many results exist without paging through them. Use hasMore (boolean) to decide whether to fetch the next page; pass nextCursor back as `cursor` to do so.\n\nfields parameter:\n- "summary" (default): {id, label, type} - best for browsing.\n- "id": {id} only - use when you need to count or iterate cheaply.\n- "full": full node payload with all attributes - use when you need every property.\n\nDefault page size 25, max 100.',
|
|
998
|
-
inputSchema: z20.object({
|
|
999
|
-
graphId: z20.string().describe("The graph (space) ID."),
|
|
1000
|
-
viewId: z20.string().describe("The view ID."),
|
|
1001
|
-
cursor: z20.string().optional().describe("Opaque cursor from a previous response's nextCursor. Omit for the first page."),
|
|
1002
|
-
limit: z20.number().int().min(1).max(100).optional().describe("Page size, default 25, max 100."),
|
|
1003
|
-
fields: z20.enum(["id", "summary", "full"]).optional().describe('How much detail per node. Default "summary".')
|
|
1004
|
-
})
|
|
1005
|
-
},
|
|
1006
|
-
async ({ graphId, viewId, cursor, limit, fields }) => {
|
|
1007
|
-
const params = new URLSearchParams();
|
|
1008
|
-
if (cursor) params.set("cursor", cursor);
|
|
1009
|
-
if (limit !== void 0) params.set("limit", String(limit));
|
|
1010
|
-
if (fields) params.set("fields", fields);
|
|
1011
|
-
const qs = params.toString();
|
|
1012
|
-
const path = `/api/graphs/${graphId}/views/${viewId}/nodes${qs ? `?${qs}` : ""}`;
|
|
1013
|
-
const data = await client2.get(path);
|
|
1014
|
-
return {
|
|
1015
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
);
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
// ../mcp-core/src/tools/list-canvases.ts
|
|
1022
|
-
import { z as z21 } from "zod";
|
|
1023
|
-
function registerListCanvases(server2, client2) {
|
|
1024
|
-
server2.registerTool(
|
|
1025
|
-
"naumu_list_canvases",
|
|
1026
|
-
{
|
|
1027
|
-
title: "List Canvases",
|
|
1028
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1029
|
-
description: "List freeform drawing canvases in a graph. Each canvas is an Excalidraw-style sketch surface that may contain shapes, text, freehand strokes, images, bookmark cards, and embedded references to graph nodes.",
|
|
1030
|
-
inputSchema: z21.object({
|
|
1031
|
-
graphId: z21.string().describe("The graph ID to list canvases for")
|
|
1032
|
-
})
|
|
1033
|
-
},
|
|
1034
|
-
async ({ graphId }) => {
|
|
1035
|
-
const data = await client2.get(`/api/canvases?graphId=${encodeURIComponent(graphId)}`);
|
|
1036
|
-
return {
|
|
1037
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
// ../mcp-core/src/tools/get-canvas-elements.ts
|
|
1044
|
-
import { z as z22 } from "zod";
|
|
1045
|
-
function registerGetCanvasElements(server2, client2) {
|
|
1046
|
-
server2.registerTool(
|
|
1047
|
-
"naumu_get_canvas_elements",
|
|
1048
|
-
{
|
|
1049
|
-
title: "Get Canvas Elements",
|
|
1050
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1051
|
-
description: "Read the structured JSON contents of a canvas - every shape, text, line, freehand stroke, image, bookmark card, and entity-embed with their positions, colors, and text. Use this when you need to reason about what a user has drawn without needing to see the visual output.",
|
|
1052
|
-
inputSchema: z22.object({
|
|
1053
|
-
canvasId: z22.string().describe("The canvas ID")
|
|
1054
|
-
})
|
|
1055
|
-
},
|
|
1056
|
-
async ({ canvasId }) => {
|
|
1057
|
-
const data = await client2.get(`/api/canvases/${encodeURIComponent(canvasId)}/elements`);
|
|
1058
|
-
return {
|
|
1059
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
// ../mcp-core/src/tools/get-canvas-image.ts
|
|
1066
|
-
import { z as z23 } from "zod";
|
|
1067
|
-
var MAX_INLINE_BYTES = 4 * 1024 * 1024;
|
|
1068
|
-
function registerGetCanvasImage(server2, client2) {
|
|
1069
|
-
server2.registerTool(
|
|
1070
|
-
"naumu_get_canvas_image",
|
|
1071
|
-
{
|
|
1072
|
-
title: "Get Canvas Image",
|
|
1073
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1074
|
-
description: 'Render a canvas to a PNG image so you can visually inspect what was drawn - freehand strokes, spatial layout, sketches. Returns the image inline by default so any MCP host can see it directly. Use `mode: "url"` if you specifically need a signed URL (e.g. very large canvases, or to share the link).',
|
|
1075
|
-
inputSchema: z23.object({
|
|
1076
|
-
canvasId: z23.string().describe("The canvas ID"),
|
|
1077
|
-
scale: z23.union([z23.literal(1), z23.literal(2)]).default(2).describe("Pixel density (1 or 2). Default is 2 for retina-quality output."),
|
|
1078
|
-
theme: z23.enum(["light", "dark"]).default("light").describe("Background theme to render with."),
|
|
1079
|
-
mode: z23.enum(["inline", "url"]).default("inline").describe(
|
|
1080
|
-
"`inline` (default) embeds the PNG directly so vision-capable hosts see it. `url` returns a signed S3 URL that expires in 1 hour - useful for large canvases or sharing."
|
|
1081
|
-
)
|
|
1082
|
-
})
|
|
1083
|
-
},
|
|
1084
|
-
async ({ canvasId, scale, theme, mode }) => {
|
|
1085
|
-
if (mode === "url") {
|
|
1086
|
-
return await fetchAsUrl(client2, canvasId, scale, theme);
|
|
1087
|
-
}
|
|
1088
|
-
const params = new URLSearchParams({
|
|
1089
|
-
scale: String(scale),
|
|
1090
|
-
theme
|
|
1091
|
-
});
|
|
1092
|
-
const { buffer, contentType } = await client2.getBinary(
|
|
1093
|
-
`/api/canvases/${encodeURIComponent(canvasId)}/image?${params.toString()}`
|
|
1094
|
-
);
|
|
1095
|
-
if (buffer.byteLength > MAX_INLINE_BYTES) {
|
|
1096
|
-
return await fetchAsUrl(client2, canvasId, scale, theme, {
|
|
1097
|
-
reason: `Canvas PNG is ${formatBytes(buffer.byteLength)}, exceeds inline limit ${formatBytes(MAX_INLINE_BYTES)} \u2014 returning signed URL instead.`
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
const base64 = bufferToBase64(buffer);
|
|
1101
|
-
return {
|
|
1102
|
-
content: [
|
|
1103
|
-
{
|
|
1104
|
-
type: "image",
|
|
1105
|
-
data: base64,
|
|
1106
|
-
mimeType: contentType.startsWith("image/") ? contentType : "image/png"
|
|
1107
|
-
}
|
|
1108
|
-
]
|
|
1109
|
-
};
|
|
1110
|
-
}
|
|
1111
|
-
);
|
|
1112
|
-
}
|
|
1113
|
-
async function fetchAsUrl(client2, canvasId, scale, theme, extra) {
|
|
1114
|
-
const params = new URLSearchParams({
|
|
1115
|
-
as: "url",
|
|
1116
|
-
scale: String(scale),
|
|
1117
|
-
theme
|
|
1118
|
-
});
|
|
1119
|
-
const data = await client2.get(
|
|
1120
|
-
`/api/canvases/${encodeURIComponent(canvasId)}/image?${params.toString()}`
|
|
1121
|
-
);
|
|
1122
|
-
const payload = extra ? { ...data, note: extra.reason } : data;
|
|
1123
|
-
return {
|
|
1124
|
-
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
function bufferToBase64(buffer) {
|
|
1128
|
-
if (typeof Buffer !== "undefined") {
|
|
1129
|
-
return Buffer.from(buffer).toString("base64");
|
|
1130
|
-
}
|
|
1131
|
-
let binary = "";
|
|
1132
|
-
for (let i = 0; i < buffer.byteLength; i++) {
|
|
1133
|
-
binary += String.fromCharCode(buffer[i]);
|
|
1134
|
-
}
|
|
1135
|
-
return btoa(binary);
|
|
1136
|
-
}
|
|
1137
|
-
function formatBytes(bytes) {
|
|
1138
|
-
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
1139
|
-
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
1079
|
// ../mcp-core/src/tools/post-message.ts
|
|
1143
|
-
import { z as
|
|
1080
|
+
import { z as z20 } from "zod";
|
|
1144
1081
|
function registerPostMessage(server2, client2) {
|
|
1145
1082
|
server2.registerTool(
|
|
1146
1083
|
"naumu_post_message",
|
|
@@ -1154,12 +1091,12 @@ function registerPostMessage(server2, client2) {
|
|
|
1154
1091
|
idempotentHint: false,
|
|
1155
1092
|
openWorldHint: false
|
|
1156
1093
|
},
|
|
1157
|
-
description: 'Post a message in a Naumu thread you participate in. Use it to reply to humans (or other bots) in a thread that pinged you. Plain text is accepted by default; for rendered @mentions pass a Tiptap JSON document with mention nodes (`{ type: "mention", attrs: { id, label } }`) and set contentFormat to "tiptap". @mentioning people loops them in without invoking @Naumu. To attach files call naumu_request_attachment_upload first, PUT the bytes to the returned uploadUrl, then pass the resulting attachmentIds here. The message needs either `content` or `attachmentIds`. Returns the created message JSON. To get a synthesised answer from @Naumu, use naumu_ask.',
|
|
1158
|
-
inputSchema:
|
|
1159
|
-
threadId:
|
|
1160
|
-
content:
|
|
1161
|
-
contentFormat:
|
|
1162
|
-
attachmentIds:
|
|
1094
|
+
description: 'Post a message in a Naumu thread you participate in. Use it to reply to humans (or other bots) in a thread that pinged you. Plain text is accepted by default; for rendered @mentions pass a Tiptap JSON document with mention nodes (`{ type: "mention", attrs: { id, label } }`) and set contentFormat to "tiptap". @mentioning people loops them in (mention notifications, prompts) without invoking @Naumu. For a human, the mention `id` is their User id (from naumu_list_members or naumu_get_thread participantDetails) or their email - both work; for a bot/agent, use its identity id. To attach files call naumu_request_attachment_upload first, PUT the bytes to the returned uploadUrl, then pass the resulting attachmentIds here. The message needs either `content` or `attachmentIds`. Returns the created message JSON. To get a synthesised answer from @Naumu, use naumu_ask.',
|
|
1095
|
+
inputSchema: z20.object({
|
|
1096
|
+
threadId: z20.string().describe("The thread ID to post into. You must be a participant in this thread."),
|
|
1097
|
+
content: z20.string().optional().describe('Message body. Plain text by default; pass a Tiptap JSON document only if contentFormat is set to "tiptap". Optional when `attachmentIds` is provided.'),
|
|
1098
|
+
contentFormat: z20.enum(["tiptap", "text"]).optional().describe('Format of `content`. Defaults to "text". Use "tiptap" for rendered mentions/embeds, e.g. a doc containing `{ type: "mention", attrs: { id: userIdOrEmailOrIdentityId, label: displayName } }` (a human mention id is their User id or email; a bot mention id is its identity id).'),
|
|
1099
|
+
attachmentIds: z20.array(z20.string().min(1)).max(25).optional().describe("Attachment IDs from prior `naumu_request_attachment_upload` calls. Each must be a successfully-uploaded pending attachment in this graph (1-hour TTL). Up to 25 per message.")
|
|
1163
1100
|
})
|
|
1164
1101
|
},
|
|
1165
1102
|
async ({ threadId, content, contentFormat, attachmentIds }) => {
|
|
@@ -1185,18 +1122,18 @@ function registerPostMessage(server2, client2) {
|
|
|
1185
1122
|
}
|
|
1186
1123
|
|
|
1187
1124
|
// ../mcp-core/src/tools/read-thread.ts
|
|
1188
|
-
import { z as
|
|
1125
|
+
import { z as z21 } from "zod";
|
|
1189
1126
|
function registerReadThread(server2, client2) {
|
|
1190
1127
|
server2.registerTool(
|
|
1191
1128
|
"naumu_read_thread",
|
|
1192
1129
|
{
|
|
1193
1130
|
title: "Read Thread",
|
|
1194
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1131
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1195
1132
|
description: 'Read messages from a Naumu thread. Returns paginated history ordered newest-first; each message carries a `status` (`processing` while @Naumu is still composing, `complete` when done). Use this to pick up an answer after naumu_ask returns status "processing", or to read what naumu_delegate produced. Use `before` (timestamp ms) to page further back. Default page size 50, max 200.',
|
|
1196
|
-
inputSchema:
|
|
1197
|
-
threadId:
|
|
1198
|
-
before:
|
|
1199
|
-
limit:
|
|
1133
|
+
inputSchema: z21.object({
|
|
1134
|
+
threadId: z21.string().describe("The thread ID to read from."),
|
|
1135
|
+
before: z21.number().optional().describe("Unix timestamp in milliseconds. Returns messages strictly older than this. Omit for the newest page."),
|
|
1136
|
+
limit: z21.number().int().min(1).max(200).optional().describe("Page size, default 50, max 200.")
|
|
1200
1137
|
})
|
|
1201
1138
|
},
|
|
1202
1139
|
async ({ threadId, before, limit }) => {
|
|
@@ -1222,15 +1159,15 @@ function registerReadThread(server2, client2) {
|
|
|
1222
1159
|
}
|
|
1223
1160
|
|
|
1224
1161
|
// ../mcp-core/src/tools/whoami.ts
|
|
1225
|
-
import { z as
|
|
1162
|
+
import { z as z22 } from "zod";
|
|
1226
1163
|
function registerWhoami(server2, client2, allToolNames) {
|
|
1227
1164
|
server2.registerTool(
|
|
1228
1165
|
"naumu_whoami",
|
|
1229
1166
|
{
|
|
1230
1167
|
title: "Who Am I",
|
|
1231
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1168
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1232
1169
|
description: 'Return who the calling key is plus the live MCP tool manifest, so you can bootstrap before the first real operation. A bot identity key returns its Identity row (id, graphId, name, instructions, allowedTools). A user API key returns `kind: "user"` with userId, name, and email - a person spans many graphs, so resolve a specific graph via naumu_list_graphs. No arguments. Always available regardless of the permission grid.',
|
|
1233
|
-
inputSchema:
|
|
1170
|
+
inputSchema: z22.object({})
|
|
1234
1171
|
},
|
|
1235
1172
|
async () => {
|
|
1236
1173
|
try {
|
|
@@ -1253,7 +1190,7 @@ function registerWhoami(server2, client2, allToolNames) {
|
|
|
1253
1190
|
}
|
|
1254
1191
|
|
|
1255
1192
|
// ../mcp-core/src/tools/list-threads.ts
|
|
1256
|
-
import { z as
|
|
1193
|
+
import { z as z23 } from "zod";
|
|
1257
1194
|
function sanitizeThreadParticipants(thread) {
|
|
1258
1195
|
if (!thread || typeof thread !== "object" || !("participantEmails" in thread)) {
|
|
1259
1196
|
return thread;
|
|
@@ -1266,12 +1203,12 @@ function registerListThreads(server2, client2) {
|
|
|
1266
1203
|
"naumu_list_threads",
|
|
1267
1204
|
{
|
|
1268
1205
|
title: "List Threads",
|
|
1269
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1206
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1270
1207
|
description: "List threads sorted by last activity (newest first), for self-discovery before deciding which to engage. With a user API key, pass `graphId` to list threads you can see in that space (resolve it via naumu_list_graphs). With a bot identity key, omit `graphId` to list threads in your own graph \u2014 each row carries an `isParticipant` flag (TRUE means you were explicitly invited and your replies fan out via webhook). Page back with `cursor` set to the oldest `lastActivityAt` from the previous page.",
|
|
1271
|
-
inputSchema:
|
|
1272
|
-
graphId:
|
|
1273
|
-
cursor:
|
|
1274
|
-
limit:
|
|
1208
|
+
inputSchema: z23.object({
|
|
1209
|
+
graphId: z23.string().optional().describe("Graph (space) ID. Required for user API keys; omit for bot identity keys (defaults to your own graph)."),
|
|
1210
|
+
cursor: z23.number().int().optional().describe("Unix timestamp ms \u2014 returns threads with `lastActivityAt` strictly older than this. Omit for the newest page."),
|
|
1211
|
+
limit: z23.number().int().min(1).max(200).optional().describe("Page size, default 50, max 200.")
|
|
1275
1212
|
})
|
|
1276
1213
|
},
|
|
1277
1214
|
async ({ graphId, cursor, limit }) => {
|
|
@@ -1306,8 +1243,53 @@ function registerListThreads(server2, client2) {
|
|
|
1306
1243
|
);
|
|
1307
1244
|
}
|
|
1308
1245
|
|
|
1246
|
+
// ../mcp-core/src/tools/list-topics.ts
|
|
1247
|
+
import { z as z24 } from "zod";
|
|
1248
|
+
function toFilingDestination(topic) {
|
|
1249
|
+
if (!topic || typeof topic !== "object") return null;
|
|
1250
|
+
const t = topic;
|
|
1251
|
+
if (typeof t.id !== "string" || typeof t.name !== "string") return null;
|
|
1252
|
+
return {
|
|
1253
|
+
id: t.id,
|
|
1254
|
+
name: t.name,
|
|
1255
|
+
...typeof t.visibilityMode === "string" ? { visibilityMode: t.visibilityMode } : {},
|
|
1256
|
+
...typeof t.memberCount === "number" ? { memberCount: t.memberCount } : {},
|
|
1257
|
+
...typeof t.openToWeb === "boolean" ? { openToWeb: t.openToWeb } : {},
|
|
1258
|
+
...typeof t.isMember === "boolean" ? { isMember: t.isMember } : {}
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function registerListTopics(server2, client2) {
|
|
1262
|
+
server2.registerTool(
|
|
1263
|
+
"naumu_list_topics",
|
|
1264
|
+
{
|
|
1265
|
+
title: "List Topics",
|
|
1266
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1267
|
+
description: "List a space's real topics \u2014 the filing destinations you can hand to the `topicIds` param of naumu_delegate or naumu_ask when creating a thread. Filing a new thread into one or more topics makes it visible to those topics' members from birth (instead of the default private thread). Each row carries { id, name, visibilityMode, memberCount, openToWeb }; use the id values in `topicIds`. Topics have no separate description field. The virtual #misc bucket is not a real topic and is omitted; archived topics are omitted because they can't be tagged. Resolve `graphId` via naumu_list_graphs first.",
|
|
1268
|
+
inputSchema: z24.object({
|
|
1269
|
+
graphId: z24.string().describe("The space (graph) id to list topics for.")
|
|
1270
|
+
})
|
|
1271
|
+
},
|
|
1272
|
+
async ({ graphId }) => {
|
|
1273
|
+
try {
|
|
1274
|
+
const data = await client2.get(`/api/graphs/${graphId}/topics`);
|
|
1275
|
+
const rows = Array.isArray(data) ? data : [];
|
|
1276
|
+
const topics = rows.filter((t) => !(t && typeof t === "object" && t.archived === true)).map(toFilingDestination).filter((t) => t !== null);
|
|
1277
|
+
return {
|
|
1278
|
+
content: [{ type: "text", text: JSON.stringify(topics, null, 2) }]
|
|
1279
|
+
};
|
|
1280
|
+
} catch (err) {
|
|
1281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1282
|
+
return {
|
|
1283
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1284
|
+
isError: true
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1309
1291
|
// ../mcp-core/src/tools/get-thread.ts
|
|
1310
|
-
import { z as
|
|
1292
|
+
import { z as z25 } from "zod";
|
|
1311
1293
|
function sanitizeThreadParticipants2(thread) {
|
|
1312
1294
|
if (!thread || typeof thread !== "object" || !("participantEmails" in thread)) {
|
|
1313
1295
|
return thread;
|
|
@@ -1320,10 +1302,10 @@ function registerGetThread(server2, client2) {
|
|
|
1320
1302
|
"naumu_get_thread",
|
|
1321
1303
|
{
|
|
1322
1304
|
title: "Get Thread",
|
|
1323
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1305
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1324
1306
|
description: "Fetch a single thread, including the human participant roster (`participantDetails` \u2014 userId, name, image) and bot roster (`identityParticipants` \u2014 id, name, isSystem). Use this when `naumu_list_threads` surfaced a candidate and you want to know exactly who is in it before posting. Pair with `naumu_read_thread` for message history.",
|
|
1325
|
-
inputSchema:
|
|
1326
|
-
threadId:
|
|
1307
|
+
inputSchema: z25.object({
|
|
1308
|
+
threadId: z25.string().describe("The thread ID to fetch.")
|
|
1327
1309
|
})
|
|
1328
1310
|
},
|
|
1329
1311
|
async ({ threadId }) => {
|
|
@@ -1345,7 +1327,7 @@ function registerGetThread(server2, client2) {
|
|
|
1345
1327
|
}
|
|
1346
1328
|
|
|
1347
1329
|
// ../mcp-core/src/tools/create-thread.ts
|
|
1348
|
-
import { z as
|
|
1330
|
+
import { z as z26 } from "zod";
|
|
1349
1331
|
function registerCreateThread(server2, client2) {
|
|
1350
1332
|
server2.registerTool(
|
|
1351
1333
|
"naumu_create_thread",
|
|
@@ -1359,32 +1341,36 @@ function registerCreateThread(server2, client2) {
|
|
|
1359
1341
|
idempotentHint: false,
|
|
1360
1342
|
openWorldHint: false
|
|
1361
1343
|
},
|
|
1362
|
-
description: "Start a new conversation in a space. You are auto-attached as a participant, and the thread's formal creator is your primary owner (the user who registered you), so it shows in their sidebar. Optional `participants` adds humans (by userId) and other bots (by identityId) at creation. Optional `initialMessage` opens the conversation as your first message. Tagging people loops them in without invoking @Naumu; only an explicit @Naumu mention, or naumu_ask, brings the agent in. Returns the created thread (including its id) so you can follow up with naumu_post_message.",
|
|
1363
|
-
inputSchema:
|
|
1364
|
-
title:
|
|
1365
|
-
participants:
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
type:
|
|
1369
|
-
userId:
|
|
1344
|
+
description: "Start a new conversation in a space. You are auto-attached as a participant, and the thread's formal creator is your primary owner (the user who registered you), so it shows in their sidebar. Optional `participants` adds humans (by userId) and other bots (by identityId) at creation. Optional `initialMessage` opens the conversation as your first message. Optional `topicIds` files the new thread into one or more topics (see naumu_list_topics for ids): it becomes visible to those topics' members from birth instead of staying a private thread between you and your owner. Filing is creation-only and only ever widens - it never removes the thread from a topic later. Tagging people loops them in without invoking @Naumu; only an explicit @Naumu mention, or naumu_ask, brings the agent in. Returns the created thread (including its id) so you can follow up with naumu_post_message.",
|
|
1345
|
+
inputSchema: z26.object({
|
|
1346
|
+
title: z26.string().min(1).max(200).optional().describe('Thread title shown in the sidebar. If omitted, Naumu generates a default like "Conversation YYYY-MM-DD".'),
|
|
1347
|
+
participants: z26.array(
|
|
1348
|
+
z26.discriminatedUnion("type", [
|
|
1349
|
+
z26.object({
|
|
1350
|
+
type: z26.literal("user"),
|
|
1351
|
+
userId: z26.string().min(1).describe("User UUID \u2014 get these from `naumu_get_thread`/`naumu_read_thread` participant rosters or webhook payloads.")
|
|
1370
1352
|
}),
|
|
1371
|
-
|
|
1372
|
-
type:
|
|
1373
|
-
identityId:
|
|
1353
|
+
z26.object({
|
|
1354
|
+
type: z26.literal("identity"),
|
|
1355
|
+
identityId: z26.string().min(1).describe("Identity id (`identity-\u2026` or `id-\u2026`). Other bots in the same graph can be co-attached to multi-bot threads.")
|
|
1374
1356
|
})
|
|
1375
1357
|
])
|
|
1376
1358
|
).max(32).optional().describe("Up to 32 humans and/or other bots to attach at creation. Your primary owner is added automatically \u2014 you do NOT need to list them here."),
|
|
1377
|
-
initialMessage:
|
|
1378
|
-
visibility:
|
|
1359
|
+
initialMessage: z26.string().min(1).max(32e3).optional().describe("Markdown body for the first message. Authored by you (the bot), so it appears in the thread under your name."),
|
|
1360
|
+
visibility: z26.enum(["restricted", "internal", "open"]).optional().describe("`restricted` (invite-only, default) hides from non-participants. `internal` is visible to space members. `open` is visible to anyone who can see the space."),
|
|
1361
|
+
topicIds: z26.array(z26.string()).max(8).optional().describe(
|
|
1362
|
+
"Topic ids (from naumu_list_topics) to file the NEW thread into, making it visible to those topics' members from birth. Creation-only and only ever widens - it never removes the thread from a topic later. Omit to keep the default private thread between you and your owner."
|
|
1363
|
+
)
|
|
1379
1364
|
})
|
|
1380
1365
|
},
|
|
1381
|
-
async ({ title, participants, initialMessage, visibility }) => {
|
|
1366
|
+
async ({ title, participants, initialMessage, visibility, topicIds }) => {
|
|
1382
1367
|
try {
|
|
1383
1368
|
const body = {};
|
|
1384
1369
|
if (title !== void 0) body.title = title;
|
|
1385
1370
|
if (participants !== void 0) body.participants = participants;
|
|
1386
1371
|
if (initialMessage !== void 0) body.initialMessage = initialMessage;
|
|
1387
1372
|
if (visibility !== void 0) body.visibility = visibility;
|
|
1373
|
+
if (topicIds !== void 0) body.topicIds = topicIds;
|
|
1388
1374
|
const data = await client2.post("/api/identities/me/threads", body);
|
|
1389
1375
|
return {
|
|
1390
1376
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
@@ -1401,7 +1387,7 @@ function registerCreateThread(server2, client2) {
|
|
|
1401
1387
|
}
|
|
1402
1388
|
|
|
1403
1389
|
// ../mcp-core/src/tools/request-attachment-upload.ts
|
|
1404
|
-
import { z as
|
|
1390
|
+
import { z as z27 } from "zod";
|
|
1405
1391
|
function registerRequestAttachmentUpload(server2, client2) {
|
|
1406
1392
|
server2.registerTool(
|
|
1407
1393
|
"naumu_request_attachment_upload",
|
|
@@ -1409,12 +1395,12 @@ function registerRequestAttachmentUpload(server2, client2) {
|
|
|
1409
1395
|
title: "Request Attachment Upload",
|
|
1410
1396
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1411
1397
|
description: 'Request a presigned S3 upload URL to attach a file to a message; use when you want to deliver generated content as a file (a markdown report, a PDF, an image, an audio recording, a video). Same flow Naumu users use for file uploads: get a signed URL, PUT the bytes to it directly, then call `naumu_post_message` with the returned `attachmentId` in `attachmentIds`. Per-MIME size caps apply (typically 50MB umbrella, 10MB for agent-readable types).\n\nReturns `{ attachmentId, uploadUrl, method, requiredHeaders, expiresAt }`. Use these EXACTLY:\n\u2022 `method` is "PUT".\n\u2022 Send every header in `requiredHeaders` (Content-Type matters for S3 signature validation).\n\u2022 Do NOT add an Authorization header - the URL itself is the auth.\n\u2022 Do NOT log `uploadUrl` - it is a bearer capability for the duration of the TTL.\n\u2022 `expiresAt` is a Unix-ms timestamp; the pending attachment vanishes at that moment whether or not you uploaded. Call `naumu_post_message` with the attachmentId before then or the upload orphans.\n\nServer-side checks at post time enforce that the attachment was uploaded by you, in this graph, for this thread - you cannot reuse an upload across threads.',
|
|
1412
|
-
inputSchema:
|
|
1413
|
-
threadId:
|
|
1414
|
-
fileName:
|
|
1415
|
-
fileType:
|
|
1416
|
-
fileSize:
|
|
1417
|
-
audioDurationSec:
|
|
1398
|
+
inputSchema: z27.object({
|
|
1399
|
+
threadId: z27.string().describe("Thread the attachment will land in. You must be a participant. The pending attachment is keyed to this thread - you cannot reuse it for a different one."),
|
|
1400
|
+
fileName: z27.string().min(1).describe("Original filename (with extension). Used as the display name in the message and for the S3 object suffix. Special characters are sanitized server-side."),
|
|
1401
|
+
fileType: z27.string().min(1).describe("MIME type, e.g. `application/pdf`, `image/png`, `text/markdown`, `audio/mpeg`, `video/mp4`. The S3 PUT will enforce this Content-Type."),
|
|
1402
|
+
fileSize: z27.number().int().positive().describe("File size in bytes. Validated against per-MIME caps before the URL is issued - exceeding the cap returns a 400."),
|
|
1403
|
+
audioDurationSec: z27.number().positive().optional().describe("For audio attachments, duration in seconds. Validated against the audio recording cap (currently 8 hours).")
|
|
1418
1404
|
})
|
|
1419
1405
|
},
|
|
1420
1406
|
async ({ threadId, fileName, fileType, fileSize, audioDurationSec }) => {
|
|
@@ -1444,7 +1430,7 @@ function registerRequestAttachmentUpload(server2, client2) {
|
|
|
1444
1430
|
}
|
|
1445
1431
|
|
|
1446
1432
|
// ../mcp-core/src/tools/add-reaction.ts
|
|
1447
|
-
import { z as
|
|
1433
|
+
import { z as z28 } from "zod";
|
|
1448
1434
|
function registerAddReaction(server2, client2) {
|
|
1449
1435
|
server2.registerTool(
|
|
1450
1436
|
"naumu_add_reaction",
|
|
@@ -1452,10 +1438,10 @@ function registerAddReaction(server2, client2) {
|
|
|
1452
1438
|
title: "Add Reaction",
|
|
1453
1439
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1454
1440
|
description: 'Add an emoji reaction to a message in a thread you are participating in; use for lightweight acknowledgement instead of posting a message. Idempotent - calling twice with the same emoji is a no-op (use `naumu_remove_reaction` to undo). Returns `{ ok, messageId, emoji, alreadyExisted, reactionCount, reactions }` so you can confirm the state without re-reading the thread; `alreadyExisted: true` means the reaction was already on the message and the call was a no-op.\n\nWhen to react vs. when to post a message:\n\u2022 React (no message) for lightweight acknowledgement (\u{1F440}, \u2705, \u{1F44D}), appreciation (\u2764\uFE0F, \u{1F64C}), laughter (\u{1F602}), or "I saw this".\n\u2022 Post a message for direct questions, clarification, important corrections, or final results - situations where words are required.\n\u2022 For long tasks: react \u{1F440} first to acknowledge, optionally post a short "On it - I\'ll report back" if the work will take >20s, do the work, then post the final result.\n\u2022 Ignore casual human banter, side-conversations someone else already answered, or anything where you would only say "ok"/"nice"/"lol".\n\nUse at most one reaction per message unless explicitly useful. Reactions are social backpressure relief, not a sparkle-confetti channel.',
|
|
1455
|
-
inputSchema:
|
|
1456
|
-
threadId:
|
|
1457
|
-
messageId:
|
|
1458
|
-
emoji:
|
|
1441
|
+
inputSchema: z28.object({
|
|
1442
|
+
threadId: z28.string().describe("Thread containing the message. You must be a participant."),
|
|
1443
|
+
messageId: z28.string().describe("The message to react to."),
|
|
1444
|
+
emoji: z28.string().min(1).describe('Emoji character (e.g. "\u{1F440}", "\u2705", "\u2764\uFE0F"). Custom-emoji shortcodes are NOT supported here - pass a real Unicode emoji.')
|
|
1459
1445
|
})
|
|
1460
1446
|
},
|
|
1461
1447
|
async ({ threadId, messageId, emoji }) => {
|
|
@@ -1479,7 +1465,7 @@ function registerAddReaction(server2, client2) {
|
|
|
1479
1465
|
}
|
|
1480
1466
|
|
|
1481
1467
|
// ../mcp-core/src/tools/remove-reaction.ts
|
|
1482
|
-
import { z as
|
|
1468
|
+
import { z as z29 } from "zod";
|
|
1483
1469
|
function registerRemoveReaction(server2, client2) {
|
|
1484
1470
|
server2.registerTool(
|
|
1485
1471
|
"naumu_remove_reaction",
|
|
@@ -1487,10 +1473,10 @@ function registerRemoveReaction(server2, client2) {
|
|
|
1487
1473
|
title: "Remove Reaction",
|
|
1488
1474
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1489
1475
|
description: "Remove your own emoji reaction from a message; use to walk back an acknowledgement you previously added. Idempotent - calling on a reaction you never added is a no-op. Pair with `naumu_add_reaction` (e.g. you reacted \u{1F440} to start a task and want to clear it after a final result message lands). Returns `{ ok, messageId, emoji, alreadyExisted, reactionCount, reactions }` - `alreadyExisted: false` means there was nothing to remove and the call was a no-op.",
|
|
1490
|
-
inputSchema:
|
|
1491
|
-
threadId:
|
|
1492
|
-
messageId:
|
|
1493
|
-
emoji:
|
|
1476
|
+
inputSchema: z29.object({
|
|
1477
|
+
threadId: z29.string().describe("Thread containing the message. You must be a participant."),
|
|
1478
|
+
messageId: z29.string().describe("The message to remove your reaction from."),
|
|
1479
|
+
emoji: z29.string().min(1).describe("Emoji character to remove (must match what you originally reacted with).")
|
|
1494
1480
|
})
|
|
1495
1481
|
},
|
|
1496
1482
|
async ({ threadId, messageId, emoji }) => {
|
|
@@ -1514,7 +1500,7 @@ function registerRemoveReaction(server2, client2) {
|
|
|
1514
1500
|
}
|
|
1515
1501
|
|
|
1516
1502
|
// ../mcp-core/src/tools/naumu-typing.ts
|
|
1517
|
-
import { z as
|
|
1503
|
+
import { z as z30 } from "zod";
|
|
1518
1504
|
function registerNaumuTyping(server2, client2) {
|
|
1519
1505
|
server2.registerTool(
|
|
1520
1506
|
"naumu_typing",
|
|
@@ -1525,9 +1511,9 @@ function registerNaumuTyping(server2, client2) {
|
|
|
1525
1511
|
// repeating the same state is a no-op renew, so idempotent.
|
|
1526
1512
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1527
1513
|
description: 'Show or hide your "is typing\u2026" pill in a thread; use to signal that you are composing a reply. Call with `state: "start"` the moment you decide to compose a reply (before any LLM call), and the server holds the pill alive - re-broadcasting on a short interval - until you stop, post a message, or the lease cap (~5 min) fires. You do NOT need to refresh on a timer; that\'s the lease\'s job.\n\nThe pill clears automatically when:\n\u2022 you call this tool with `state: "stop"`\n\u2022 you call `naumu_post_message` for the same thread (cleared on commit)\n\u2022 the lease cap expires\n\nUse `start` whenever you start work, even if you might end up not replying - call `stop` if you decide NOT to post. Calling `start` while a lease is already active renews it (resets the cap), so a long-running run can call `start` again as a heartbeat without breaking the indicator. You must be a participant of the thread.',
|
|
1528
|
-
inputSchema:
|
|
1529
|
-
threadId:
|
|
1530
|
-
state:
|
|
1514
|
+
inputSchema: z30.object({
|
|
1515
|
+
threadId: z30.string().describe("The thread ID to set typing in. You must be a participant."),
|
|
1516
|
+
state: z30.enum(["start", "stop"]).describe('"start" acquires/renews the lease; "stop" ends it and clears the pill immediately.')
|
|
1531
1517
|
})
|
|
1532
1518
|
},
|
|
1533
1519
|
async ({ threadId, state }) => {
|
|
@@ -1548,16 +1534,16 @@ function registerNaumuTyping(server2, client2) {
|
|
|
1548
1534
|
}
|
|
1549
1535
|
|
|
1550
1536
|
// ../mcp-core/src/tools/note-read.ts
|
|
1551
|
-
import { z as
|
|
1537
|
+
import { z as z31 } from "zod";
|
|
1552
1538
|
function registerNoteRead(server2, client2) {
|
|
1553
1539
|
server2.registerTool(
|
|
1554
1540
|
"naumu_note_read",
|
|
1555
1541
|
{
|
|
1556
1542
|
title: "Read Note",
|
|
1557
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1543
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1558
1544
|
description: "Read the current contents of a note as markdown; use before editing so you know what you're working with. `naumu_note_find_replace` and the section-based tools (`naumu_note_insert`, `naumu_note_replace_section`, `naumu_note_delete_section`) anchor on text/headings present in the live doc.",
|
|
1559
|
-
inputSchema:
|
|
1560
|
-
noteId:
|
|
1545
|
+
inputSchema: z31.object({
|
|
1546
|
+
noteId: z31.string().describe("The note (Thought) ID")
|
|
1561
1547
|
})
|
|
1562
1548
|
},
|
|
1563
1549
|
async ({ noteId }) => {
|
|
@@ -1570,7 +1556,7 @@ function registerNoteRead(server2, client2) {
|
|
|
1570
1556
|
}
|
|
1571
1557
|
|
|
1572
1558
|
// ../mcp-core/src/tools/note-append.ts
|
|
1573
|
-
import { z as
|
|
1559
|
+
import { z as z32 } from "zod";
|
|
1574
1560
|
function registerNoteAppend(server2, client2) {
|
|
1575
1561
|
server2.registerTool(
|
|
1576
1562
|
"naumu_note_append",
|
|
@@ -1578,9 +1564,9 @@ function registerNoteAppend(server2, client2) {
|
|
|
1578
1564
|
title: "Append to Note",
|
|
1579
1565
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1580
1566
|
description: "Append markdown blocks to the end of a note; use for additive note writing that never touches existing content. Other participants see your colored cursor while the write lands. Markdown supports headings (1-3), bold/italic/code, lists, blockquotes, code blocks, links, and tables.",
|
|
1581
|
-
inputSchema:
|
|
1582
|
-
noteId:
|
|
1583
|
-
markdown:
|
|
1567
|
+
inputSchema: z32.object({
|
|
1568
|
+
noteId: z32.string().describe("The note (Thought) ID to append to"),
|
|
1569
|
+
markdown: z32.string().min(1).describe("Markdown content to append at the end of the note")
|
|
1584
1570
|
})
|
|
1585
1571
|
},
|
|
1586
1572
|
async ({ noteId, markdown }) => {
|
|
@@ -1593,7 +1579,7 @@ function registerNoteAppend(server2, client2) {
|
|
|
1593
1579
|
}
|
|
1594
1580
|
|
|
1595
1581
|
// ../mcp-core/src/tools/note-insert.ts
|
|
1596
|
-
import { z as
|
|
1582
|
+
import { z as z33 } from "zod";
|
|
1597
1583
|
function registerNoteInsert(server2, client2) {
|
|
1598
1584
|
server2.registerTool(
|
|
1599
1585
|
"naumu_note_insert",
|
|
@@ -1601,10 +1587,10 @@ function registerNoteInsert(server2, client2) {
|
|
|
1601
1587
|
title: "Insert After Heading",
|
|
1602
1588
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1603
1589
|
description: "Insert markdown content into a note immediately after a named section; use to add content under a specific heading without rewriting it. The section ends at the next heading of equal-or-higher level (or end of doc). 404 if no heading matches `headingText` exactly - call `naumu_note_read` first to see the live structure.",
|
|
1604
|
-
inputSchema:
|
|
1605
|
-
noteId:
|
|
1606
|
-
headingText:
|
|
1607
|
-
markdown:
|
|
1590
|
+
inputSchema: z33.object({
|
|
1591
|
+
noteId: z33.string().describe("The note (Thought) ID"),
|
|
1592
|
+
headingText: z33.string().min(1).describe("Exact text of the heading whose section the new content follows"),
|
|
1593
|
+
markdown: z33.string().min(1).describe("Markdown content to insert at the end of that section")
|
|
1608
1594
|
})
|
|
1609
1595
|
},
|
|
1610
1596
|
async ({ noteId, headingText, markdown }) => {
|
|
@@ -1620,7 +1606,7 @@ function registerNoteInsert(server2, client2) {
|
|
|
1620
1606
|
}
|
|
1621
1607
|
|
|
1622
1608
|
// ../mcp-core/src/tools/note-replace-section.ts
|
|
1623
|
-
import { z as
|
|
1609
|
+
import { z as z34 } from "zod";
|
|
1624
1610
|
function registerNoteReplaceSection(server2, client2) {
|
|
1625
1611
|
server2.registerTool(
|
|
1626
1612
|
"naumu_note_replace_section",
|
|
@@ -1628,11 +1614,11 @@ function registerNoteReplaceSection(server2, client2) {
|
|
|
1628
1614
|
title: "Replace Section",
|
|
1629
1615
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1630
1616
|
description: "Replace the body under a named heading with new markdown; use to rewrite one section of a note while leaving the rest intact. By default the heading row itself is preserved (set `keepHeading: false` to drop it too). 404 if no heading matches.",
|
|
1631
|
-
inputSchema:
|
|
1632
|
-
noteId:
|
|
1633
|
-
headingText:
|
|
1634
|
-
markdown:
|
|
1635
|
-
keepHeading:
|
|
1617
|
+
inputSchema: z34.object({
|
|
1618
|
+
noteId: z34.string().describe("The note (Thought) ID"),
|
|
1619
|
+
headingText: z34.string().min(1).describe("Exact text of the heading anchoring the section"),
|
|
1620
|
+
markdown: z34.string().describe("Replacement markdown for the section body"),
|
|
1621
|
+
keepHeading: z34.boolean().optional().describe("Whether to keep the heading row itself. Default true.")
|
|
1636
1622
|
})
|
|
1637
1623
|
},
|
|
1638
1624
|
async ({ noteId, headingText, markdown, keepHeading }) => {
|
|
@@ -1649,7 +1635,7 @@ function registerNoteReplaceSection(server2, client2) {
|
|
|
1649
1635
|
}
|
|
1650
1636
|
|
|
1651
1637
|
// ../mcp-core/src/tools/note-delete-section.ts
|
|
1652
|
-
import { z as
|
|
1638
|
+
import { z as z35 } from "zod";
|
|
1653
1639
|
function registerNoteDeleteSection(server2, client2) {
|
|
1654
1640
|
server2.registerTool(
|
|
1655
1641
|
"naumu_note_delete_section",
|
|
@@ -1657,9 +1643,9 @@ function registerNoteDeleteSection(server2, client2) {
|
|
|
1657
1643
|
title: "Delete Section",
|
|
1658
1644
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1659
1645
|
description: "\u26A0 DESTRUCTIVE: remove a heading row plus its body (down to the next heading of equal-or-higher level); ONLY use when the user explicitly asks to drop a section. Anything inside that section is gone - there is no per-call undo. If you're unsure which heading they meant, call `naumu_note_read` first to see the current structure. Returns 404 if `headingText` does not exactly match any live heading.",
|
|
1660
|
-
inputSchema:
|
|
1661
|
-
noteId:
|
|
1662
|
-
headingText:
|
|
1646
|
+
inputSchema: z35.object({
|
|
1647
|
+
noteId: z35.string().describe("The note (Thought) ID"),
|
|
1648
|
+
headingText: z35.string().min(1).describe("Exact text of the heading whose section will be deleted")
|
|
1663
1649
|
})
|
|
1664
1650
|
},
|
|
1665
1651
|
async ({ noteId, headingText }) => {
|
|
@@ -1674,7 +1660,7 @@ function registerNoteDeleteSection(server2, client2) {
|
|
|
1674
1660
|
}
|
|
1675
1661
|
|
|
1676
1662
|
// ../mcp-core/src/tools/note-replace.ts
|
|
1677
|
-
import { z as
|
|
1663
|
+
import { z as z36 } from "zod";
|
|
1678
1664
|
function registerNoteReplace(server2, client2) {
|
|
1679
1665
|
server2.registerTool(
|
|
1680
1666
|
"naumu_note_replace",
|
|
@@ -1682,9 +1668,9 @@ function registerNoteReplace(server2, client2) {
|
|
|
1682
1668
|
title: "Replace Note",
|
|
1683
1669
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1684
1670
|
description: "\u26A0 DESTRUCTIVE: replace the entire note content with new markdown; ONLY use when the user explicitly asks to rewrite/replace the whole note. Any concurrent human edits made during the call are silently overwritten. For additive work prefer `naumu_note_append`. For section-level edits use `naumu_note_replace_section`. For inline tweaks use `naumu_note_find_replace`. Read with `naumu_note_read` first if you weren't the last writer.",
|
|
1685
|
-
inputSchema:
|
|
1686
|
-
noteId:
|
|
1687
|
-
markdown:
|
|
1671
|
+
inputSchema: z36.object({
|
|
1672
|
+
noteId: z36.string().describe("The note (Thought) ID"),
|
|
1673
|
+
markdown: z36.string().describe("New markdown content for the entire note")
|
|
1688
1674
|
})
|
|
1689
1675
|
},
|
|
1690
1676
|
async ({ noteId, markdown }) => {
|
|
@@ -1697,7 +1683,7 @@ function registerNoteReplace(server2, client2) {
|
|
|
1697
1683
|
}
|
|
1698
1684
|
|
|
1699
1685
|
// ../mcp-core/src/tools/note-find-replace.ts
|
|
1700
|
-
import { z as
|
|
1686
|
+
import { z as z37 } from "zod";
|
|
1701
1687
|
function registerNoteFindReplace(server2, client2) {
|
|
1702
1688
|
server2.registerTool(
|
|
1703
1689
|
"naumu_note_find_replace",
|
|
@@ -1705,11 +1691,11 @@ function registerNoteFindReplace(server2, client2) {
|
|
|
1705
1691
|
title: "Find/Replace in Note",
|
|
1706
1692
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1707
1693
|
description: "Literal find/replace within a note's text content; use for mid-paragraph tweaks the section-based tools can't target. Marks (bold, italic, code, etc.) are preserved on the surrounding text. \u26A0 The match is literal-substring across every text leaf in the doc; an overly generic `find` (e.g. \" a \") can rewrite the doc unrecognizably. Pick a phrase distinctive enough to land where you mean. By default replaces every occurrence; set `all: false` for first-only. Returns `{ replacements }` so you can sanity-check the count.",
|
|
1708
|
-
inputSchema:
|
|
1709
|
-
noteId:
|
|
1710
|
-
find:
|
|
1711
|
-
replace:
|
|
1712
|
-
all:
|
|
1694
|
+
inputSchema: z37.object({
|
|
1695
|
+
noteId: z37.string().describe("The note (Thought) ID"),
|
|
1696
|
+
find: z37.string().min(1).describe("Substring to search for. Literal - no regex."),
|
|
1697
|
+
replace: z37.string().describe("Replacement string. May be empty to delete the match."),
|
|
1698
|
+
all: z37.boolean().optional().describe("Replace all occurrences (default true). Pass false to replace only the first.")
|
|
1713
1699
|
})
|
|
1714
1700
|
},
|
|
1715
1701
|
async ({ noteId, find, replace, all }) => {
|
|
@@ -1725,144 +1711,24 @@ function registerNoteFindReplace(server2, client2) {
|
|
|
1725
1711
|
);
|
|
1726
1712
|
}
|
|
1727
1713
|
|
|
1728
|
-
// ../mcp-core/src/tools/canvas-add-element.ts
|
|
1729
|
-
import { z as z41 } from "zod";
|
|
1730
|
-
var ELEMENT_TYPES = [
|
|
1731
|
-
"rectangle",
|
|
1732
|
-
"ellipse",
|
|
1733
|
-
"diamond",
|
|
1734
|
-
"text",
|
|
1735
|
-
"line",
|
|
1736
|
-
"arrow",
|
|
1737
|
-
"freehand",
|
|
1738
|
-
"image",
|
|
1739
|
-
"bookmark-card",
|
|
1740
|
-
"entity-embed"
|
|
1741
|
-
];
|
|
1742
|
-
function registerCanvasAddElement(server2, client2) {
|
|
1743
|
-
server2.registerTool(
|
|
1744
|
-
"naumu_canvas_add_element",
|
|
1745
|
-
{
|
|
1746
|
-
title: "Add Canvas Element",
|
|
1747
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1748
|
-
description: "Add a new element to a canvas; use to draw a shape, text, or embed onto a drawing surface. Other participants see your colored cursor at the element's center while it lands. The server fills in `id`, `version`, `fractionalIndex`, and `seed` automatically - pass only `type`, position (`x`, `y`), and size (`width`, `height`); other fields are optional and have sensible defaults (stroke #1e1e1e, no fill, opacity 1, etc.). For text elements, set `label` and `fontSize`. Coordinates are canvas-space (the same units the live editor uses).",
|
|
1749
|
-
inputSchema: z41.object({
|
|
1750
|
-
canvasId: z41.string().describe("The canvas ID"),
|
|
1751
|
-
element: z41.object({
|
|
1752
|
-
type: z41.enum(ELEMENT_TYPES).describe("Element shape"),
|
|
1753
|
-
x: z41.number().describe("Top-left x coordinate in canvas space"),
|
|
1754
|
-
y: z41.number().describe("Top-left y coordinate in canvas space"),
|
|
1755
|
-
width: z41.number().describe("Width in canvas units"),
|
|
1756
|
-
height: z41.number().describe("Height in canvas units"),
|
|
1757
|
-
strokeColor: z41.string().optional().describe("Stroke color (hex). Default #1e1e1e."),
|
|
1758
|
-
fillColor: z41.string().optional().describe('Fill color (hex) or "transparent". Default transparent.'),
|
|
1759
|
-
strokeWidth: z41.number().optional().describe("Stroke width. Default 2."),
|
|
1760
|
-
opacity: z41.number().min(0).max(1).optional().describe("0..1, default 1"),
|
|
1761
|
-
roughness: z41.number().min(0).max(2).optional().describe("Hand-drawn roughness 0..2. Default 1."),
|
|
1762
|
-
label: z41.string().optional().describe("Optional label/text content"),
|
|
1763
|
-
labelFontSize: z41.number().optional().describe("Label font size"),
|
|
1764
|
-
angle: z41.number().optional().describe("Rotation in radians. Default 0.")
|
|
1765
|
-
}).passthrough().describe("Element fields. Pass only what you set - defaults fill the rest.")
|
|
1766
|
-
})
|
|
1767
|
-
},
|
|
1768
|
-
async ({ canvasId, element }) => {
|
|
1769
|
-
const data = await client2.post(`/api/canvases/${canvasId}/elements`, { element });
|
|
1770
|
-
return {
|
|
1771
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
);
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
// ../mcp-core/src/tools/canvas-update-element.ts
|
|
1778
|
-
import { z as z42 } from "zod";
|
|
1779
|
-
function registerCanvasUpdateElement(server2, client2) {
|
|
1780
|
-
server2.registerTool(
|
|
1781
|
-
"naumu_canvas_update_element",
|
|
1782
|
-
{
|
|
1783
|
-
title: "Update Canvas Element",
|
|
1784
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1785
|
-
description: "Patch an existing canvas element; use to move, resize, recolor, or relabel a shape already on the canvas. Only the fields you pass in `changes` are updated - `id`, `version`, `createdBy`, and `isDeleted` are server-managed and ignored if present. Useful for moving (`x`, `y`), resizing (`width`, `height`), recoloring (`strokeColor`, `fillColor`), or relabeling (`label`).",
|
|
1786
|
-
inputSchema: z42.object({
|
|
1787
|
-
canvasId: z42.string().describe("The canvas ID"),
|
|
1788
|
-
elementId: z42.string().describe("The element ID returned by `naumu_canvas_add_element` or `naumu_get_canvas_elements`"),
|
|
1789
|
-
changes: z42.record(z42.string(), z42.unknown()).describe("Partial element fields to merge in. Server bumps `version` automatically.")
|
|
1790
|
-
})
|
|
1791
|
-
},
|
|
1792
|
-
async ({ canvasId, elementId, changes }) => {
|
|
1793
|
-
const data = await client2.patch(
|
|
1794
|
-
`/api/canvases/${canvasId}/elements/${elementId}`,
|
|
1795
|
-
{ changes }
|
|
1796
|
-
);
|
|
1797
|
-
return {
|
|
1798
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1799
|
-
};
|
|
1800
|
-
}
|
|
1801
|
-
);
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// ../mcp-core/src/tools/canvas-remove-element.ts
|
|
1805
|
-
import { z as z43 } from "zod";
|
|
1806
|
-
function registerCanvasRemoveElement(server2, client2) {
|
|
1807
|
-
server2.registerTool(
|
|
1808
|
-
"naumu_canvas_remove_element",
|
|
1809
|
-
{
|
|
1810
|
-
title: "Remove Canvas Element",
|
|
1811
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1812
|
-
description: "Soft-delete a canvas element; use to remove a shape from a canvas. The element is tombstoned (isDeleted=true) so concurrent edits don't resurrect it. 404 if the element id is not present on this canvas.",
|
|
1813
|
-
inputSchema: z43.object({
|
|
1814
|
-
canvasId: z43.string().describe("The canvas ID"),
|
|
1815
|
-
elementId: z43.string().describe("The element ID to delete")
|
|
1816
|
-
})
|
|
1817
|
-
},
|
|
1818
|
-
async ({ canvasId, elementId }) => {
|
|
1819
|
-
const data = await client2.del(`/api/canvases/${canvasId}/elements/${elementId}`);
|
|
1820
|
-
return {
|
|
1821
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
1824
|
-
);
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
1714
|
// ../mcp-core/src/tools/create-note.ts
|
|
1828
|
-
import { z as
|
|
1715
|
+
import { z as z38 } from "zod";
|
|
1829
1716
|
function registerCreateNote(server2, client2) {
|
|
1830
1717
|
server2.registerTool(
|
|
1831
1718
|
"naumu_create_note",
|
|
1832
1719
|
{
|
|
1833
1720
|
title: "Create Note",
|
|
1834
1721
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1835
|
-
description: "Create a new empty note in a graph; use when you need a fresh note to write into. Returns the new note row including its `id` - pass that id to `naumu_note_append` / `naumu_note_replace` to fill in the content. Bots can only create notes in their own graph.",
|
|
1836
|
-
inputSchema:
|
|
1837
|
-
graphId:
|
|
1838
|
-
title:
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
async ({ graphId, title }) => {
|
|
1842
|
-
const data = await client2.post("/api/notes", { graphId, title });
|
|
1843
|
-
return {
|
|
1844
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1845
|
-
};
|
|
1846
|
-
}
|
|
1847
|
-
);
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
// ../mcp-core/src/tools/create-canvas.ts
|
|
1851
|
-
import { z as z45 } from "zod";
|
|
1852
|
-
function registerCreateCanvas(server2, client2) {
|
|
1853
|
-
server2.registerTool(
|
|
1854
|
-
"naumu_create_canvas",
|
|
1855
|
-
{
|
|
1856
|
-
title: "Create Canvas",
|
|
1857
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1858
|
-
description: "Create a new empty canvas in a graph; use when you need a fresh drawing surface. Returns the new canvas row including its `id` - pass that id to `naumu_canvas_add_element` to start drawing. Bots can only create canvases in their own graph.",
|
|
1859
|
-
inputSchema: z45.object({
|
|
1860
|
-
graphId: z45.string().describe("The graph ID to create the canvas in"),
|
|
1861
|
-
title: z45.string().optional().describe("Optional title for the canvas")
|
|
1722
|
+
description: "Create a new empty note in a graph; use when you need a fresh note to write into. Returns the new note row including its `id` - pass that id to `naumu_note_append` / `naumu_note_replace` to fill in the content. Bots can only create notes in their own graph, and a bot-created note is private (participants only) unless `sharedWithSpace` is set true. Bots cannot file notes into topics \u2014 a bot passing `topicIds` is rejected (bots hold no topic membership); use `sharedWithSpace` instead.",
|
|
1723
|
+
inputSchema: z38.object({
|
|
1724
|
+
graphId: z38.string().describe("The graph ID to create the note in"),
|
|
1725
|
+
title: z38.string().optional().describe("Optional title for the note"),
|
|
1726
|
+
sharedWithSpace: z38.boolean().optional().describe("Share the note with everyone in the space. Omit (or false) to keep it private to its participants."),
|
|
1727
|
+
topicIds: z38.array(z38.string()).max(8).optional().describe("File the note into these topics (get ids from naumu_list_topics), making it visible to those topics' members. Not available to bots.")
|
|
1862
1728
|
})
|
|
1863
1729
|
},
|
|
1864
|
-
async ({ graphId, title }) => {
|
|
1865
|
-
const data = await client2.post("/api/
|
|
1730
|
+
async ({ graphId, title, sharedWithSpace, topicIds }) => {
|
|
1731
|
+
const data = await client2.post("/api/notes", { graphId, title, sharedWithSpace, topicIds });
|
|
1866
1732
|
return {
|
|
1867
1733
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1868
1734
|
};
|
|
@@ -1871,7 +1737,7 @@ function registerCreateCanvas(server2, client2) {
|
|
|
1871
1737
|
}
|
|
1872
1738
|
|
|
1873
1739
|
// ../mcp-core/src/tools/list-schema-violations.ts
|
|
1874
|
-
import { z as
|
|
1740
|
+
import { z as z39 } from "zod";
|
|
1875
1741
|
var DEFAULT_EXAMPLE_LIMIT = 5;
|
|
1876
1742
|
var rowsForKind = (violations, kind) => {
|
|
1877
1743
|
const rows = [];
|
|
@@ -1895,14 +1761,14 @@ function registerListSchemaViolations(server2, client2) {
|
|
|
1895
1761
|
"naumu_list_schema_violations",
|
|
1896
1762
|
{
|
|
1897
1763
|
title: "List Schema Violations",
|
|
1898
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1764
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1899
1765
|
description: "Audit a graph against its schema. By default returns a compact summary: total counts plus, for each violation kind, its count and up to 5 example nodes (id/label/type/message) \u2014 small enough not to flood the client. Violation kinds: parent_missing (schema expects a parent edge that does not exist), parent_multiple (more than one parent edge where one is expected), parent_mismatch (parent edge has wrong target type or relation label), parent_not_backbone (an edge uses a backbone/parent relation but is not stored as a backbone edge, so the subtree stays off the hierarchy), unknown_relation (edge uses a relation not in the schema), invalid_connection_target (edge connects to a type the schema does not allow for this source), unknown_type (node carries a type no longer in the schema), disconnected (node heads a group with no backbone path to the main tree). To see every node for one kind, pass `kind` to filter; `limit` caps how many rows are returned (examples in the default summary, or full rows when `kind` is set). Use for audits, import-verification, and CI-style checks after batch writes.",
|
|
1900
|
-
inputSchema:
|
|
1901
|
-
graphId:
|
|
1902
|
-
kind:
|
|
1766
|
+
inputSchema: z39.object({
|
|
1767
|
+
graphId: z39.string().describe("The graph ID"),
|
|
1768
|
+
kind: z39.string().optional().describe(
|
|
1903
1769
|
'Drill into one violation kind (e.g. "parent_not_backbone"). Returns the full list of nodes with that kind, up to `limit`, instead of the summary.'
|
|
1904
1770
|
),
|
|
1905
|
-
limit:
|
|
1771
|
+
limit: z39.number().int().min(1).optional().describe(
|
|
1906
1772
|
"Max rows to return. When `kind` is set, caps the full drill-down list (default: all). Otherwise caps example nodes per kind in the summary (default: 5)."
|
|
1907
1773
|
)
|
|
1908
1774
|
})
|
|
@@ -1954,18 +1820,18 @@ function registerListSchemaViolations(server2, client2) {
|
|
|
1954
1820
|
}
|
|
1955
1821
|
|
|
1956
1822
|
// ../mcp-core/src/tools/list-dense-nodes.ts
|
|
1957
|
-
import { z as
|
|
1823
|
+
import { z as z40 } from "zod";
|
|
1958
1824
|
function registerListDenseNodes(server2, client2) {
|
|
1959
1825
|
server2.registerTool(
|
|
1960
1826
|
"naumu_list_dense_nodes",
|
|
1961
1827
|
{
|
|
1962
1828
|
title: "List Dense Nodes",
|
|
1963
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1964
|
-
description: 'Return nodes whose
|
|
1965
|
-
inputSchema:
|
|
1966
|
-
graphId:
|
|
1967
|
-
minConnections:
|
|
1968
|
-
nodeTypes:
|
|
1829
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1830
|
+
description: 'Return nodes whose child count (children via parent edges; mesh cross-links don\'t count) is \u2265 minConnections, grouped by type; use for /restructure hub detection. Each row includes `same_typed_child_count` - the number of children of the SAME type as the node (the Naumu hub-pattern signal) and `connection_count` - its total children. Sort the response by `same_typed_child_count` descending and route any node with \u226510 same-typed children through a mini-hub split. Pass `nodeTypes` (comma-separated) to restrict to a subset (e.g. ["Type A","Type B"]). Cheap to call - runs a single Cypher aggregation.',
|
|
1831
|
+
inputSchema: z40.object({
|
|
1832
|
+
graphId: z40.string().describe("The graph ID"),
|
|
1833
|
+
minConnections: z40.number().int().min(1).describe("Minimum number of children (parent edges; mesh cross-links excluded). Typical: 10 for hub detection, 11 to count only hubs that exceed the round-4 \u226410 threshold."),
|
|
1834
|
+
nodeTypes: z40.array(z40.string()).optional().describe("Optional list of node types to restrict the scan to.")
|
|
1969
1835
|
})
|
|
1970
1836
|
},
|
|
1971
1837
|
async ({ graphId, minConnections, nodeTypes }) => {
|
|
@@ -1983,19 +1849,19 @@ function registerListDenseNodes(server2, client2) {
|
|
|
1983
1849
|
}
|
|
1984
1850
|
|
|
1985
1851
|
// ../mcp-core/src/tools/list-node-connections.ts
|
|
1986
|
-
import { z as
|
|
1852
|
+
import { z as z41 } from "zod";
|
|
1987
1853
|
function registerListNodeConnections(server2, client2) {
|
|
1988
1854
|
server2.registerTool(
|
|
1989
1855
|
"naumu_list_node_connections",
|
|
1990
1856
|
{
|
|
1991
1857
|
title: "List Node Connections",
|
|
1992
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1858
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1993
1859
|
description: 'Return a single node\'s edges (non-system) with the connected node on the other side; use during /restructure to confirm mini-hub candidates and verify reparenting outcomes. Filter with `edgeType` (relation label) and `direction` ("in" | "out" | "both", default both). Response: `{ node: {id,label,type}, edges: [{relation, direction, isParent, other: {id,label,type}}] }`.',
|
|
1994
|
-
inputSchema:
|
|
1995
|
-
graphId:
|
|
1996
|
-
nodeId:
|
|
1997
|
-
edgeType:
|
|
1998
|
-
direction:
|
|
1860
|
+
inputSchema: z41.object({
|
|
1861
|
+
graphId: z41.string().describe("The graph ID"),
|
|
1862
|
+
nodeId: z41.string().describe("The node ID to inspect"),
|
|
1863
|
+
edgeType: z41.string().optional().describe('Restrict to a specific relation label (e.g. "ASSOCIATED_WITH"). Case-insensitive; non-alphanum chars are normalized.'),
|
|
1864
|
+
direction: z41.enum(["in", "out", "both"]).optional().describe('Edge direction filter - "in" (incoming), "out" (outgoing), "both" (default).')
|
|
1999
1865
|
})
|
|
2000
1866
|
},
|
|
2001
1867
|
async ({ graphId, nodeId, edgeType, direction }) => {
|
|
@@ -2013,7 +1879,7 @@ function registerListNodeConnections(server2, client2) {
|
|
|
2013
1879
|
}
|
|
2014
1880
|
|
|
2015
1881
|
// ../mcp-core/src/tools/reparent.ts
|
|
2016
|
-
import { z as
|
|
1882
|
+
import { z as z42 } from "zod";
|
|
2017
1883
|
function registerReparent(server2, client2) {
|
|
2018
1884
|
server2.registerTool(
|
|
2019
1885
|
"naumu_reparent",
|
|
@@ -2021,11 +1887,11 @@ function registerReparent(server2, client2) {
|
|
|
2021
1887
|
title: "Reparent Node",
|
|
2022
1888
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
2023
1889
|
description: 'Atomically swap a node\'s parent edge; use to move a child under a different parent (e.g. during /restructure to reparent children under newly-created mini-hubs). Deletes any existing `isParent: true` edges on the node and creates a new one to `newParentId` with relation `newRelation`. Preserves the node\'s id, content, attributes, and embedding - does NOT trigger embedding regeneration because only the parent edge changes. Idempotent: if the node already has the requested parent edge, response is `status: "skipped"`. Response shape: `{nodeId, oldParentId, newParentId, newRelation, status: "moved" | "skipped"}`.',
|
|
2024
|
-
inputSchema:
|
|
2025
|
-
graphId:
|
|
2026
|
-
nodeId:
|
|
2027
|
-
newParentId:
|
|
2028
|
-
newRelation:
|
|
1890
|
+
inputSchema: z42.object({
|
|
1891
|
+
graphId: z42.string().describe("The graph ID"),
|
|
1892
|
+
nodeId: z42.string().describe("The child node to reparent"),
|
|
1893
|
+
newParentId: z42.string().describe("The new parent node id"),
|
|
1894
|
+
newRelation: z42.string().describe('The new parent edge relation label (e.g. "PART_OF"). Must be valid per the schema for (child.type, relation, parent.type).')
|
|
2029
1895
|
})
|
|
2030
1896
|
},
|
|
2031
1897
|
async ({ graphId, nodeId, newParentId, newRelation }) => {
|
|
@@ -2041,7 +1907,7 @@ function registerReparent(server2, client2) {
|
|
|
2041
1907
|
}
|
|
2042
1908
|
|
|
2043
1909
|
// ../mcp-core/src/tools/batch-reparent.ts
|
|
2044
|
-
import { z as
|
|
1910
|
+
import { z as z43 } from "zod";
|
|
2045
1911
|
function registerBatchReparent(server2, client2) {
|
|
2046
1912
|
server2.registerTool(
|
|
2047
1913
|
"naumu_batch_reparent",
|
|
@@ -2049,11 +1915,11 @@ function registerBatchReparent(server2, client2) {
|
|
|
2049
1915
|
title: "Batch Reparent Nodes",
|
|
2050
1916
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
2051
1917
|
description: 'Reparent 1-25 nodes onto a shared `newParentId` with the same `newRelation`; use to move a same-typed cluster under a freshly-created mini-hub in /restructure. Same semantics as `naumu_reparent` per-node: atomic swap of the isParent edge, preserves id/content/attributes/embedding, no re-embedding. Idempotent per node (already-parented nodes return `status: "skipped"`). Per-node response array: `[{nodeId, oldParentId, newParentId, status: "moved" | "skipped" | "error", error?}]`.',
|
|
2052
|
-
inputSchema:
|
|
2053
|
-
graphId:
|
|
2054
|
-
newParentId:
|
|
2055
|
-
newRelation:
|
|
2056
|
-
nodeIds:
|
|
1918
|
+
inputSchema: z43.object({
|
|
1919
|
+
graphId: z43.string().describe("The graph ID"),
|
|
1920
|
+
newParentId: z43.string().describe("Parent node id every nodeId in the batch will be parented to"),
|
|
1921
|
+
newRelation: z43.string().describe("Parent edge relation label (must be valid per schema for child.type \u2192 parent.type)"),
|
|
1922
|
+
nodeIds: z43.array(z43.string()).min(1).max(25).describe("1\u201325 child node ids to reparent under `newParentId`")
|
|
2057
1923
|
})
|
|
2058
1924
|
},
|
|
2059
1925
|
async ({ graphId, newParentId, newRelation, nodeIds }) => {
|
|
@@ -2070,7 +1936,7 @@ function registerBatchReparent(server2, client2) {
|
|
|
2070
1936
|
}
|
|
2071
1937
|
|
|
2072
1938
|
// ../mcp-core/src/tools/chatgpt-search.ts
|
|
2073
|
-
import { z as
|
|
1939
|
+
import { z as z44 } from "zod";
|
|
2074
1940
|
|
|
2075
1941
|
// ../mcp-core/src/public-origin.ts
|
|
2076
1942
|
function publicOrigin() {
|
|
@@ -2122,10 +1988,10 @@ function registerChatgptSearch(server2, client2) {
|
|
|
2122
1988
|
"search",
|
|
2123
1989
|
{
|
|
2124
1990
|
title: "Search",
|
|
2125
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1991
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2126
1992
|
description: "Search across all of the knowledge graphs (spaces) you can access and return the most relevant nodes. Returns `{ results: [{ id, title, url }] }`. Pass each result `id` to the `fetch` tool to read the full node. (This is the cross-space entry point for ChatGPT/Deep Research; within a single space, `naumu_search` exposes more controls.)",
|
|
2127
|
-
inputSchema:
|
|
2128
|
-
query:
|
|
1993
|
+
inputSchema: z44.object({
|
|
1994
|
+
query: z44.string().describe('Search query text. Mix synonyms and exact tokens freely (e.g. "auth login SSO 2fa").')
|
|
2129
1995
|
})
|
|
2130
1996
|
},
|
|
2131
1997
|
async ({ query }) => {
|
|
@@ -2157,7 +2023,7 @@ function registerChatgptSearch(server2, client2) {
|
|
|
2157
2023
|
}
|
|
2158
2024
|
|
|
2159
2025
|
// ../mcp-core/src/tools/chatgpt-fetch.ts
|
|
2160
|
-
import { z as
|
|
2026
|
+
import { z as z45 } from "zod";
|
|
2161
2027
|
var NON_ATTRIBUTE_PROPS = /* @__PURE__ */ new Set([
|
|
2162
2028
|
"id",
|
|
2163
2029
|
"label",
|
|
@@ -2220,10 +2086,10 @@ function registerChatgptFetch(server2, client2) {
|
|
|
2220
2086
|
"fetch",
|
|
2221
2087
|
{
|
|
2222
2088
|
title: "Fetch",
|
|
2223
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
2089
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2224
2090
|
description: "Fetch the full contents of a node returned by the `search` tool. Pass the result `id` verbatim (format `<graphId>:<nodeId>`). Returns `{ id, title, text, url }` where `text` is the node content plus its type, attributes, and connections.",
|
|
2225
|
-
inputSchema:
|
|
2226
|
-
id:
|
|
2091
|
+
inputSchema: z45.object({
|
|
2092
|
+
id: z45.string().describe("A resource id from a previous `search` result, in the form `<graphId>:<nodeId>`.")
|
|
2227
2093
|
})
|
|
2228
2094
|
},
|
|
2229
2095
|
async ({ id }) => {
|
|
@@ -2268,6 +2134,7 @@ function registerChatgptFetch(server2, client2) {
|
|
|
2268
2134
|
// ../mcp-core/src/tools/index.ts
|
|
2269
2135
|
var TOOL_REGISTRARS = {
|
|
2270
2136
|
naumu_list_graphs: registerListGraphs,
|
|
2137
|
+
naumu_list_members: registerListMembers,
|
|
2271
2138
|
naumu_create_graph: registerCreateGraph,
|
|
2272
2139
|
naumu_get_schema: registerGetSchema,
|
|
2273
2140
|
naumu_update_schema: registerUpdateSchema,
|
|
@@ -2277,8 +2144,6 @@ var TOOL_REGISTRARS = {
|
|
|
2277
2144
|
naumu_search: registerSearch,
|
|
2278
2145
|
naumu_filter: registerFilter,
|
|
2279
2146
|
naumu_get_node: registerGetNode,
|
|
2280
|
-
naumu_get_view: registerGetView,
|
|
2281
|
-
naumu_list_view_nodes: registerListViewNodes,
|
|
2282
2147
|
naumu_add_node: registerAddNode,
|
|
2283
2148
|
naumu_update_node: registerUpdateNode,
|
|
2284
2149
|
naumu_add_edge: registerAddEdge,
|
|
@@ -2288,13 +2153,11 @@ var TOOL_REGISTRARS = {
|
|
|
2288
2153
|
naumu_ask: registerAsk,
|
|
2289
2154
|
naumu_delegate: registerDelegate,
|
|
2290
2155
|
// naumu_traverse omitted on purpose — backend stub returns 503 (see import note).
|
|
2291
|
-
naumu_list_canvases: registerListCanvases,
|
|
2292
|
-
naumu_get_canvas_elements: registerGetCanvasElements,
|
|
2293
|
-
naumu_get_canvas_image: registerGetCanvasImage,
|
|
2294
2156
|
naumu_post_message: registerPostMessage,
|
|
2295
2157
|
naumu_read_thread: registerReadThread,
|
|
2296
2158
|
naumu_whoami: registerWhoami,
|
|
2297
2159
|
naumu_list_threads: registerListThreads,
|
|
2160
|
+
naumu_list_topics: registerListTopics,
|
|
2298
2161
|
naumu_get_thread: registerGetThread,
|
|
2299
2162
|
naumu_create_thread: registerCreateThread,
|
|
2300
2163
|
naumu_request_attachment_upload: registerRequestAttachmentUpload,
|
|
@@ -2308,11 +2171,7 @@ var TOOL_REGISTRARS = {
|
|
|
2308
2171
|
naumu_note_delete_section: registerNoteDeleteSection,
|
|
2309
2172
|
naumu_note_replace: registerNoteReplace,
|
|
2310
2173
|
naumu_note_find_replace: registerNoteFindReplace,
|
|
2311
|
-
naumu_canvas_add_element: registerCanvasAddElement,
|
|
2312
|
-
naumu_canvas_update_element: registerCanvasUpdateElement,
|
|
2313
|
-
naumu_canvas_remove_element: registerCanvasRemoveElement,
|
|
2314
2174
|
naumu_create_note: registerCreateNote,
|
|
2315
|
-
naumu_create_canvas: registerCreateCanvas,
|
|
2316
2175
|
naumu_list_schema_violations: registerListSchemaViolations,
|
|
2317
2176
|
naumu_list_dense_nodes: registerListDenseNodes,
|
|
2318
2177
|
naumu_list_node_connections: registerListNodeConnections,
|
|
@@ -2325,9 +2184,13 @@ var TOOL_REGISTRARS = {
|
|
|
2325
2184
|
search: registerChatgptSearch,
|
|
2326
2185
|
fetch: registerChatgptFetch
|
|
2327
2186
|
};
|
|
2328
|
-
var
|
|
2187
|
+
var BOT_ONLY_TOOL_NAMES = /* @__PURE__ */ new Set(["naumu_create_thread"]);
|
|
2188
|
+
var ALL_TOOL_NAMES = Object.keys(TOOL_REGISTRARS).filter(
|
|
2189
|
+
(name) => !BOT_ONLY_TOOL_NAMES.has(name)
|
|
2190
|
+
);
|
|
2329
2191
|
function registerAllTools(server2, client2) {
|
|
2330
2192
|
for (const [name, registrar] of Object.entries(TOOL_REGISTRARS)) {
|
|
2193
|
+
if (BOT_ONLY_TOOL_NAMES.has(name)) continue;
|
|
2331
2194
|
if (name === "naumu_whoami") {
|
|
2332
2195
|
registerWhoami(server2, client2, ALL_TOOL_NAMES);
|
|
2333
2196
|
} else {
|