@naumu/mcp 0.14.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +79 -11
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ function parseRetryAfterSeconds(retryAfter) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// ../mcp-core/src/version.ts
|
|
44
|
-
var NAUMU_MCP_VERSION = "0.
|
|
44
|
+
var NAUMU_MCP_VERSION = "0.16.0";
|
|
45
45
|
|
|
46
46
|
// ../mcp-core/src/client.ts
|
|
47
47
|
var HEADER_VALUE_MAX_LENGTH = 100;
|
|
@@ -385,6 +385,54 @@ function registerGetSchema(server2, client2) {
|
|
|
385
385
|
|
|
386
386
|
// ../mcp-core/src/tools/update-schema.ts
|
|
387
387
|
import { z as z5 } from "zod";
|
|
388
|
+
|
|
389
|
+
// ../mcp-core/src/tools/reserved-names.ts
|
|
390
|
+
var RESERVED_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
|
|
391
|
+
// RESERVED_ATTRIBUTE_KEYS
|
|
392
|
+
"id",
|
|
393
|
+
"label",
|
|
394
|
+
"type",
|
|
395
|
+
"content",
|
|
396
|
+
"embedding",
|
|
397
|
+
"visibility",
|
|
398
|
+
"source",
|
|
399
|
+
"graphid",
|
|
400
|
+
// CASED_SYSTEM_NODE_KEYS (lowercased)
|
|
401
|
+
"sortkey",
|
|
402
|
+
"createdat",
|
|
403
|
+
"updatedat",
|
|
404
|
+
"createdby",
|
|
405
|
+
"createdbyuserid",
|
|
406
|
+
"editableby",
|
|
407
|
+
"lastmodifiedat",
|
|
408
|
+
"lastmodifiedby",
|
|
409
|
+
"lastactivityat",
|
|
410
|
+
"heatscore",
|
|
411
|
+
"heatobserved",
|
|
412
|
+
"heatexpected",
|
|
413
|
+
"heatsurprise",
|
|
414
|
+
"heatcomputedat",
|
|
415
|
+
"heatconvsignal",
|
|
416
|
+
"heatconvcomputedat",
|
|
417
|
+
"__indexcolor",
|
|
418
|
+
// Canvas / force-layout scratch props
|
|
419
|
+
"x",
|
|
420
|
+
"y",
|
|
421
|
+
"vx",
|
|
422
|
+
"vy",
|
|
423
|
+
"fx",
|
|
424
|
+
"fy",
|
|
425
|
+
"index"
|
|
426
|
+
]);
|
|
427
|
+
function isReservedAttributeName(name) {
|
|
428
|
+
return RESERVED_ATTRIBUTE_NAMES.has(name.toLowerCase());
|
|
429
|
+
}
|
|
430
|
+
function reservedAttributeNameError(name, where) {
|
|
431
|
+
const scope = where ? ` (type "${where}")` : "";
|
|
432
|
+
return `Attribute name "${name}" is reserved by Naumu (system property)${scope}. Choose a different name.`;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ../mcp-core/src/tools/update-schema.ts
|
|
388
436
|
var ConnectionSchema = z5.object({
|
|
389
437
|
relation: z5.string().describe("UPPER_SNAKE_CASE relation name (e.g. WORKS_AT, BUILT_BY, BELONGS_TO)"),
|
|
390
438
|
target_node: z5.string().optional().describe("Target type name. Omit when polymorphic=true."),
|
|
@@ -396,7 +444,12 @@ var AttributeValueSchema = z5.object({
|
|
|
396
444
|
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.')
|
|
397
445
|
});
|
|
398
446
|
var AttributeSchema = z5.object({
|
|
399
|
-
|
|
447
|
+
// Reserved system property names (`source`, `type`, `createdAt`, `x`, ...)
|
|
448
|
+
// are rejected here so the agent gets a field-level error instead of a
|
|
449
|
+
// blanket 400 from the backend. See ./reserved-names.ts.
|
|
450
|
+
name: z5.string().describe('Attribute key (e.g. "stage", "status", "category")').refine((n) => !isReservedAttributeName(n), {
|
|
451
|
+
error: (iss) => reservedAttributeNameError(String(iss.input))
|
|
452
|
+
}),
|
|
400
453
|
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.'),
|
|
401
454
|
values: z5.array(AttributeValueSchema).describe("Allowed enum values for select/multiselect; pass [] for string/number/date."),
|
|
402
455
|
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.")
|
|
@@ -416,7 +469,7 @@ var NodeTypeSchema = z5.object({
|
|
|
416
469
|
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.')
|
|
417
470
|
});
|
|
418
471
|
var SchemaDefinitionSchema = z5.object({
|
|
419
|
-
description: z5.string().optional().describe("Schema-level description / domain summary."),
|
|
472
|
+
description: z5.string().optional().describe("Schema-level description / domain summary. Omit to keep the current space-wide description; send an empty string to clear it. Max 4,000 chars."),
|
|
420
473
|
nodes: z5.array(NodeTypeSchema).describe("All node types in the schema.")
|
|
421
474
|
});
|
|
422
475
|
function registerUpdateSchema(server2, client2) {
|
|
@@ -594,6 +647,12 @@ function registerAddAttribute(server2, client2) {
|
|
|
594
647
|
})
|
|
595
648
|
},
|
|
596
649
|
async ({ graphId, node_type, name, type, values, description }) => {
|
|
650
|
+
if (isReservedAttributeName(name)) {
|
|
651
|
+
return {
|
|
652
|
+
content: [{ type: "text", text: JSON.stringify({ error: reservedAttributeNameError(name, node_type) }, null, 2) }],
|
|
653
|
+
isError: true
|
|
654
|
+
};
|
|
655
|
+
}
|
|
597
656
|
const current = await client2.get(`/api/graphs/${graphId}/schema`);
|
|
598
657
|
const schema = current.definition ? JSON.parse(current.definition) : { nodes: [] };
|
|
599
658
|
const node = schema.nodes.find((n) => n.type === node_type);
|
|
@@ -631,6 +690,8 @@ function registerAddAttribute(server2, client2) {
|
|
|
631
690
|
|
|
632
691
|
// ../mcp-core/src/tools/update-schema-description.ts
|
|
633
692
|
import { z as z9 } from "zod";
|
|
693
|
+
var SCHEMA_DESCRIPTION_MAX_LENGTH = 4e3;
|
|
694
|
+
var TARGET_DESCRIPTION_MAX_LENGTH = 300;
|
|
634
695
|
function errorResult(error) {
|
|
635
696
|
return {
|
|
636
697
|
content: [{ type: "text", text: JSON.stringify({ error }, null, 2) }]
|
|
@@ -648,7 +709,10 @@ function registerUpdateSchemaDescription(server2, client2) {
|
|
|
648
709
|
nodeType: z9.string().optional().describe("Existing node type whose description (or whose attribute/value description) is being rewritten. Omit to rewrite the space-wide schema description instead."),
|
|
649
710
|
attribute: z9.string().optional().describe("Attribute name on that type. Omit to target the node type itself. Required when `value` is set."),
|
|
650
711
|
value: z9.string().optional().describe("Label of one value of `attribute`. When set, the description is written on that value instead of the attribute."),
|
|
651
|
-
description: z9.string().trim().min(1).max(
|
|
712
|
+
description: z9.string().trim().min(1).max(SCHEMA_DESCRIPTION_MAX_LENGTH).describe("The new description. For a type, attribute, or value: 1-300 chars, one or two sentences, contrastive against sibling types/attributes/values so an agent can decide between them. For the space-wide schema description (nodeType omitted): up to 4,000 chars of guidance that holds across every type.")
|
|
713
|
+
}).refine((data) => data.nodeType === void 0 || data.description.length <= TARGET_DESCRIPTION_MAX_LENGTH, {
|
|
714
|
+
path: ["description"],
|
|
715
|
+
message: "per-type, attribute and value descriptions are limited to 300 characters; the space-wide description to 4,000"
|
|
652
716
|
})
|
|
653
717
|
},
|
|
654
718
|
async ({ graphId, nodeType, attribute, value, description }) => {
|
|
@@ -950,7 +1014,7 @@ Use naumu_get_schema to check valid attributes and values for this node type.`
|
|
|
950
1014
|
}
|
|
951
1015
|
|
|
952
1016
|
// ../mcp-core/src/tools/add-node.ts
|
|
953
|
-
var RESERVED_KEYS = /* @__PURE__ */ new Set(["id", "label", "type", "content"]);
|
|
1017
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["id", "label", "type", "content", "source"]);
|
|
954
1018
|
function normalizeNodeAttributes(schema, nodeType, attributes) {
|
|
955
1019
|
const attrMap = /* @__PURE__ */ new Map();
|
|
956
1020
|
const nodeDef = schema.nodes.find((n) => n.type === nodeType);
|
|
@@ -1232,23 +1296,27 @@ function registerDelegate(server2, client2) {
|
|
|
1232
1296
|
idempotentHint: false,
|
|
1233
1297
|
openWorldHint: true
|
|
1234
1298
|
},
|
|
1235
|
-
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.
|
|
1299
|
+
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. A new thread is visible to the whole space by default (it lands in the space\'s #misc feed), so delegated work reads as a shared work log. File it into specific topics instead by passing `topicIds` (see naumu_list_topics), or pass `private: true` to keep it between you and @Naumu. 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).',
|
|
1236
1300
|
inputSchema: z20.object({
|
|
1237
1301
|
graphId: z20.string().describe("The space (graph) id to act in."),
|
|
1238
1302
|
task: z20.string().describe("What you want @Naumu to do, add, or record."),
|
|
1239
1303
|
threadId: z20.string().optional().describe("Continue an existing conversation; omit to start a new one."),
|
|
1240
1304
|
topicIds: z20.array(z20.string()).max(8).optional().describe(
|
|
1241
|
-
"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
|
|
1305
|
+
"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 share the thread with the whole space instead."
|
|
1306
|
+
),
|
|
1307
|
+
private: z20.boolean().optional().describe(
|
|
1308
|
+
"Keep a NEW thread between you and @Naumu instead of sharing it with the space. Default false. Only used at creation; ignored when threadId or topicIds are provided."
|
|
1242
1309
|
)
|
|
1243
1310
|
})
|
|
1244
1311
|
},
|
|
1245
|
-
async ({ graphId, task, threadId, topicIds }) => {
|
|
1312
|
+
async ({ graphId, task, threadId, topicIds, private: isPrivate }) => {
|
|
1246
1313
|
try {
|
|
1247
1314
|
let resolvedThreadId = threadId;
|
|
1248
1315
|
if (!resolvedThreadId) {
|
|
1316
|
+
const hasTopics = Boolean(topicIds && topicIds.length > 0);
|
|
1249
1317
|
const thread = await client2.post("/api/threads", {
|
|
1250
1318
|
graphId,
|
|
1251
|
-
...
|
|
1319
|
+
...hasTopics ? { topicIds } : { sharedWithSpace: !isPrivate }
|
|
1252
1320
|
});
|
|
1253
1321
|
resolvedThreadId = thread.id;
|
|
1254
1322
|
}
|
|
@@ -1380,7 +1448,7 @@ function registerReadThread(server2, client2) {
|
|
|
1380
1448
|
{
|
|
1381
1449
|
title: "Read Thread",
|
|
1382
1450
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1383
|
-
description: 'Read messages from a Naumu thread. Returns paginated history ordered newest-first, or oldest-first when you pass `after`; 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, or `after` (timestamp ms) to catch up on what arrived since you last looked. To wait for the next message instead of re-reading on a timer, use naumu_wait_for_activity. Default page size 50, max 200. Agent messages carry `memoryScope.line`, a one-line statement of which Memory scope the answer used; surface it under the answer. To read or download
|
|
1451
|
+
description: 'Read messages from a Naumu thread. Returns paginated history ordered newest-first, or oldest-first when you pass `after`; 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, or `after` (timestamp ms) to catch up on what arrived since you last looked. To wait for the next message instead of re-reading on a timer, use naumu_wait_for_activity. Default page size 50, max 200. Agent messages carry `memoryScope.line`, a one-line statement of which Memory scope the answer used; surface it under the answer. Audio attachments are transcribed by Naumu at post time: read `attachments[].transcription` (plus `transcriptionSummary` and `transcriptionStatus`) straight from the message instead of downloading and transcribing the file. To read or download any other attachment, pass its `attachments[].id` to naumu_get_attachment.',
|
|
1384
1452
|
inputSchema: z23.object({
|
|
1385
1453
|
threadId: z23.string().describe("The thread ID to read from."),
|
|
1386
1454
|
before: z23.number().optional().describe("Unix timestamp in milliseconds. Returns messages strictly older than this. Omit for the newest page."),
|
|
@@ -1915,7 +1983,7 @@ function registerGetAttachment(server2, client2) {
|
|
|
1915
1983
|
{
|
|
1916
1984
|
title: "Get Attachment",
|
|
1917
1985
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1918
|
-
description: "Read a chat attachment. Pass the `attachmentId` from a message's `attachments[].id` in naumu_read_thread (its `url` field works too - the id is extracted from it), and this resolves it into a short-lived download URL for the actual bytes.\n\nReturns a JSON text block with `{ attachmentId, downloadUrl, expiresInSeconds, note }`. Use `downloadUrl` EXACTLY as given:\n\u2022 GET it with no Authorization header - the URL itself is the auth, and adding one makes S3 answer 403.\n\u2022 It expires about 15 minutes after this call. Fetch it now; re-call this tool for a fresh URL rather than holding one.\n\u2022 Do not log it, quote it back to the user, or store it - it is a bearer capability for its whole lifetime.\n\nWhen the attachment is an image (or a video or PDF that has a generated poster), a downsized preview is also returned inline as an image block, so a visual attachment can often be understood without fetching anything. The preview is a thumbnail, not the original - fetch `downloadUrl` when you need full resolution or the exact file.\n\nAccess is checked the same way it is for a person: you only resolve attachments in threads you can already read.",
|
|
1986
|
+
description: "Read a chat attachment. Pass the `attachmentId` from a message's `attachments[].id` in naumu_read_thread (its `url` field works too - the id is extracted from it), and this resolves it into a short-lived download URL for the actual bytes.\n\nReturns a JSON text block with `{ attachmentId, downloadUrl, expiresInSeconds, note }`. Use `downloadUrl` EXACTLY as given:\n\u2022 GET it with no Authorization header - the URL itself is the auth, and adding one makes S3 answer 403.\n\u2022 It expires about 15 minutes after this call. Fetch it now; re-call this tool for a fresh URL rather than holding one.\n\u2022 Do not log it, quote it back to the user, or store it - it is a bearer capability for its whole lifetime.\n\nWhen the attachment is an image (or a video or PDF that has a generated poster), a downsized preview is also returned inline as an image block, so a visual attachment can often be understood without fetching anything. The preview is a thumbnail, not the original - fetch `downloadUrl` when you need full resolution or the exact file.\n\nAudio attachments do not need this tool for their contents: Naumu transcribes them at post time and naumu_read_thread returns the text as `attachments[].transcription` on the message - use that instead of downloading and transcribing the file.\n\nAccess is checked the same way it is for a person: you only resolve attachments in threads you can already read.",
|
|
1919
1987
|
inputSchema: z33.object({
|
|
1920
1988
|
attachmentId: z33.string().min(1).describe("Attachment id, from `attachments[].id` on a message returned by naumu_read_thread. A full or relative download URL is also accepted - the id is extracted from its last path segment.")
|
|
1921
1989
|
})
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "ai.naumu/mcp",
|
|
4
4
|
"title": "Naumu",
|
|
5
5
|
"description": "Search, extend, and act on your team's Naumu knowledge graph: notes, threads, and nodes.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.16.0",
|
|
7
7
|
"websiteUrl": "https://naumu.ai",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/naumu-ai/mcp",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"registryType": "npm",
|
|
28
28
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
29
29
|
"identifier": "@naumu/mcp",
|
|
30
|
-
"version": "0.
|
|
30
|
+
"version": "0.16.0",
|
|
31
31
|
"runtimeHint": "npx",
|
|
32
32
|
"transport": { "type": "stdio" },
|
|
33
33
|
"environmentVariables": [
|