@naumu/mcp 0.14.1 → 0.14.2
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 -0
- package/dist/index.js +376 -272
- package/package.json +2 -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.14.
|
|
44
|
+
var NAUMU_MCP_VERSION = "0.14.2";
|
|
45
45
|
|
|
46
46
|
// ../mcp-core/src/client.ts
|
|
47
47
|
var HEADER_VALUE_MAX_LENGTH = 100;
|
|
@@ -564,6 +564,13 @@ function registerAddConnection(server2, client2) {
|
|
|
564
564
|
|
|
565
565
|
// ../mcp-core/src/tools/add-attribute.ts
|
|
566
566
|
import { z as z8 } from "zod";
|
|
567
|
+
|
|
568
|
+
// ../mcp-core/src/tools/find-by-name.ts
|
|
569
|
+
function findByName(items, name, key) {
|
|
570
|
+
return items.find((item) => key(item) === name) ?? items.find((item) => key(item).toLowerCase() === name.toLowerCase());
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ../mcp-core/src/tools/add-attribute.ts
|
|
567
574
|
function registerAddAttribute(server2, client2) {
|
|
568
575
|
server2.registerTool(
|
|
569
576
|
"naumu_add_attribute",
|
|
@@ -596,7 +603,7 @@ function registerAddAttribute(server2, client2) {
|
|
|
596
603
|
};
|
|
597
604
|
}
|
|
598
605
|
node.attributes = node.attributes ?? [];
|
|
599
|
-
const existing = node.attributes
|
|
606
|
+
const existing = findByName(node.attributes, name, (a) => a.name);
|
|
600
607
|
let action;
|
|
601
608
|
if (!existing) {
|
|
602
609
|
node.attributes.push({ name, type, values, ...description ? { description } : {} });
|
|
@@ -605,8 +612,14 @@ function registerAddAttribute(server2, client2) {
|
|
|
605
612
|
const existingLabels = new Set((existing.values ?? []).map((v) => v.label));
|
|
606
613
|
const added = values.filter((v) => !existingLabels.has(v.label));
|
|
607
614
|
existing.values = [...existing.values ?? [], ...added];
|
|
608
|
-
if (description
|
|
609
|
-
|
|
615
|
+
if (description) existing.description = description;
|
|
616
|
+
const addedSet = new Set(added);
|
|
617
|
+
for (const incoming of values) {
|
|
618
|
+
if (incoming.description === void 0 || addedSet.has(incoming)) continue;
|
|
619
|
+
const stored = (existing.values ?? []).find((v) => v.label === incoming.label);
|
|
620
|
+
if (stored) stored.description = incoming.description;
|
|
621
|
+
}
|
|
622
|
+
action = added.length > 0 ? `Extended attribute "${existing.name}" with ${added.length} new value(s); kept ${existingLabels.size} existing.` : `Attribute "${existing.name}" already had all proposed values; no change.`;
|
|
610
623
|
}
|
|
611
624
|
await client2.post(`/api/graphs/${graphId}/schema`, { definition: schema });
|
|
612
625
|
return {
|
|
@@ -616,8 +629,98 @@ function registerAddAttribute(server2, client2) {
|
|
|
616
629
|
);
|
|
617
630
|
}
|
|
618
631
|
|
|
619
|
-
// ../mcp-core/src/tools/
|
|
632
|
+
// ../mcp-core/src/tools/update-schema-description.ts
|
|
620
633
|
import { z as z9 } from "zod";
|
|
634
|
+
function errorResult(error) {
|
|
635
|
+
return {
|
|
636
|
+
content: [{ type: "text", text: JSON.stringify({ error }, null, 2) }]
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function registerUpdateSchemaDescription(server2, client2) {
|
|
640
|
+
server2.registerTool(
|
|
641
|
+
"naumu_update_schema_description",
|
|
642
|
+
{
|
|
643
|
+
title: "Update Schema Description",
|
|
644
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
645
|
+
description: 'Rewrite the description of ONE existing schema target - the space-wide schema description, a node type, an attribute on that type, or a single value of that attribute. A description is the contrastive decision rule an agent reads when choosing between siblings: say what this target IS and, more importantly, when to pick it over the ones next to it (e.g. "Done - shipped to prod; use In review while the PR is open but not merged"). Generic restatements of the name are worthless; the contrast is the whole point. Call naumu_get_schema first to see the current descriptions and the exact sibling names, then rewrite the one that is ambiguous. Targeting: pass `value` (with its `attribute`) to update a value, `attribute` alone to update an attribute, `nodeType` alone to update the node type itself, and omit `nodeType` entirely to update the space-wide schema description - the guidance that holds across every type (e.g. what a parent connection means in this space). This replaces the existing description outright - it does not append. To create new types/attributes/values, use naumu_add_node_type / naumu_add_attribute.',
|
|
646
|
+
inputSchema: z9.object({
|
|
647
|
+
graphId: z9.string(),
|
|
648
|
+
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
|
+
attribute: z9.string().optional().describe("Attribute name on that type. Omit to target the node type itself. Required when `value` is set."),
|
|
650
|
+
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(300).describe("The new description (1-300 chars). One or two sentences, contrastive against sibling types/attributes/values so an agent can decide between them.")
|
|
652
|
+
})
|
|
653
|
+
},
|
|
654
|
+
async ({ graphId, nodeType, attribute, value, description }) => {
|
|
655
|
+
if (value !== void 0 && attribute === void 0) {
|
|
656
|
+
return errorResult("`value` requires `attribute` - pass the attribute the value belongs to.");
|
|
657
|
+
}
|
|
658
|
+
if (nodeType === void 0 && attribute !== void 0) {
|
|
659
|
+
return errorResult("`attribute` requires `nodeType` - pass the type the attribute is declared on.");
|
|
660
|
+
}
|
|
661
|
+
const current = await client2.get(`/api/graphs/${graphId}/schema`);
|
|
662
|
+
const schema = current.definition ? JSON.parse(current.definition) : { nodes: [] };
|
|
663
|
+
const nodes = schema.nodes ?? [];
|
|
664
|
+
if (nodeType === void 0) {
|
|
665
|
+
schema.description = description;
|
|
666
|
+
await client2.post(`/api/graphs/${graphId}/schema`, { definition: schema });
|
|
667
|
+
return {
|
|
668
|
+
content: [
|
|
669
|
+
{
|
|
670
|
+
type: "text",
|
|
671
|
+
text: JSON.stringify({ message: "Description updated.", updated: { schema: true, description } }, null, 2)
|
|
672
|
+
}
|
|
673
|
+
]
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
const node = findByName(nodes, nodeType, (n) => n.type);
|
|
677
|
+
if (!node) {
|
|
678
|
+
return errorResult(
|
|
679
|
+
`Node type "${nodeType}" not found. Available types: ${nodes.map((n) => n.type).join(", ") || "(none)"}.`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
if (node.readonly) {
|
|
683
|
+
return errorResult(
|
|
684
|
+
`READ_ONLY: "${node.type}" mirrors ${node.source ?? "an external source"} and its schema cannot be edited in Naumu - descriptions on it (and on its attributes and values) are owned by the sync engine and would be discarded. Do not retry; describe one of your own types instead.`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
let updated;
|
|
688
|
+
if (attribute === void 0) {
|
|
689
|
+
node.description = description;
|
|
690
|
+
updated = { type: node.type, description };
|
|
691
|
+
} else {
|
|
692
|
+
const attributes = node.attributes ?? [];
|
|
693
|
+
const attr = findByName(attributes, attribute, (a) => a.name);
|
|
694
|
+
if (!attr) {
|
|
695
|
+
return errorResult(
|
|
696
|
+
`Attribute "${attribute}" not found on node type "${node.type}". Available attributes: ${attributes.map((a) => a.name).join(", ") || "(none)"}.`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
if (value === void 0) {
|
|
700
|
+
attr.description = description;
|
|
701
|
+
updated = { type: node.type, attribute: attr.name, description };
|
|
702
|
+
} else {
|
|
703
|
+
const values = attr.values ?? [];
|
|
704
|
+
const target = findByName(values, value, (v) => v.label);
|
|
705
|
+
if (!target) {
|
|
706
|
+
return errorResult(
|
|
707
|
+
`Value "${value}" not found on attribute "${attr.name}" of node type "${node.type}". Available values: ${values.map((v) => v.label).join(", ") || "(none)"}.`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
target.description = description;
|
|
711
|
+
updated = { type: node.type, attribute: attr.name, value: target.label, description };
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
await client2.post(`/api/graphs/${graphId}/schema`, { definition: schema });
|
|
715
|
+
return {
|
|
716
|
+
content: [{ type: "text", text: JSON.stringify({ message: "Description updated.", updated }, null, 2) }]
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ../mcp-core/src/tools/search.ts
|
|
723
|
+
import { z as z10 } from "zod";
|
|
621
724
|
function registerSearch(server2, client2) {
|
|
622
725
|
server2.registerTool(
|
|
623
726
|
"naumu_search",
|
|
@@ -625,13 +728,13 @@ function registerSearch(server2, client2) {
|
|
|
625
728
|
title: "Search Graph",
|
|
626
729
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
627
730
|
description: 'Hybrid search over graph nodes; use for meaning-based lookup when you don\'t know the exact label. Combines text matching, which looks for the whole query as one contiguous substring (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").',
|
|
628
|
-
inputSchema:
|
|
629
|
-
graphId:
|
|
630
|
-
query:
|
|
731
|
+
inputSchema: z10.object({
|
|
732
|
+
graphId: z10.string().describe("The graph ID"),
|
|
733
|
+
query: z10.string().describe(
|
|
631
734
|
"A short contiguous phrase \u2014 an entity name, label, or ID. The text half matches it verbatim as a case-insensitive substring; the semantic half matches meaning."
|
|
632
735
|
),
|
|
633
|
-
limit:
|
|
634
|
-
nodeTypes:
|
|
736
|
+
limit: z10.number().optional().default(20).describe("Max results to return (default 20, max 200). Adaptive cutoff may return fewer when the top match is weak."),
|
|
737
|
+
nodeTypes: z10.array(z10.string()).optional().describe('Filter to specific node types (e.g. ["Type A", "Type B"])')
|
|
635
738
|
})
|
|
636
739
|
},
|
|
637
740
|
async ({ graphId, query, limit, nodeTypes }) => {
|
|
@@ -648,7 +751,7 @@ function registerSearch(server2, client2) {
|
|
|
648
751
|
}
|
|
649
752
|
|
|
650
753
|
// ../mcp-core/src/tools/filter.ts
|
|
651
|
-
import { z as
|
|
754
|
+
import { z as z11 } from "zod";
|
|
652
755
|
function registerFilter(server2, client2) {
|
|
653
756
|
server2.registerTool(
|
|
654
757
|
"naumu_filter",
|
|
@@ -656,17 +759,17 @@ function registerFilter(server2, client2) {
|
|
|
656
759
|
title: "Filter Graph Nodes",
|
|
657
760
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
658
761
|
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.',
|
|
659
|
-
inputSchema:
|
|
660
|
-
graphId:
|
|
661
|
-
nodeTypes:
|
|
662
|
-
includeAttributes:
|
|
762
|
+
inputSchema: z11.object({
|
|
763
|
+
graphId: z11.string().describe("The graph ID"),
|
|
764
|
+
nodeTypes: z11.array(z11.string()).optional().describe('Filter to specific node types (e.g. ["Type A", "Type B"])'),
|
|
765
|
+
includeAttributes: z11.record(z11.string(), z11.array(z11.string())).optional().describe(
|
|
663
766
|
'Only include nodes where attribute matches one of the values. Example: {"Status": ["Todo", "In Progress"]}'
|
|
664
767
|
),
|
|
665
|
-
excludeAttributes:
|
|
768
|
+
excludeAttributes: z11.record(z11.string(), z11.array(z11.string())).optional().describe(
|
|
666
769
|
'Exclude nodes where attribute matches any of the values. Example: {"Status": ["Done", "Wont do"]}'
|
|
667
770
|
),
|
|
668
|
-
sortBy:
|
|
669
|
-
limit:
|
|
771
|
+
sortBy: z11.enum(["sortKey", "updatedAt", "label"]).optional().default("sortKey").describe('Sort order: "sortKey" (default), "updatedAt" (most recent first), or "label" (alphabetical)'),
|
|
772
|
+
limit: z11.number().optional().default(50).describe("Max results to return (default 50, max 200)")
|
|
670
773
|
})
|
|
671
774
|
},
|
|
672
775
|
async ({ graphId, nodeTypes, includeAttributes, excludeAttributes, sortBy, limit }) => {
|
|
@@ -691,7 +794,7 @@ function registerFilter(server2, client2) {
|
|
|
691
794
|
}
|
|
692
795
|
|
|
693
796
|
// ../mcp-core/src/tools/get-node.ts
|
|
694
|
-
import { z as
|
|
797
|
+
import { z as z12 } from "zod";
|
|
695
798
|
function registerGetNode(server2, client2) {
|
|
696
799
|
server2.registerTool(
|
|
697
800
|
"naumu_get_node",
|
|
@@ -699,9 +802,9 @@ function registerGetNode(server2, client2) {
|
|
|
699
802
|
title: "Get Node",
|
|
700
803
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
701
804
|
description: "Get a single node with all its properties, connections (incoming and outgoing edges with neighbor labels), and tied context: attached notes, conversations (threads), scheduled tasks, and the generated summary when one exists.",
|
|
702
|
-
inputSchema:
|
|
703
|
-
graphId:
|
|
704
|
-
nodeId:
|
|
805
|
+
inputSchema: z12.object({
|
|
806
|
+
graphId: z12.string().describe("The graph ID"),
|
|
807
|
+
nodeId: z12.string().describe("The node ID")
|
|
705
808
|
})
|
|
706
809
|
},
|
|
707
810
|
async ({ graphId, nodeId }) => {
|
|
@@ -714,10 +817,10 @@ function registerGetNode(server2, client2) {
|
|
|
714
817
|
}
|
|
715
818
|
|
|
716
819
|
// ../mcp-core/src/tools/add-node.ts
|
|
717
|
-
import { z as
|
|
820
|
+
import { z as z14 } from "zod";
|
|
718
821
|
|
|
719
822
|
// ../mcp-core/src/tools/update-node.ts
|
|
720
|
-
import { z as
|
|
823
|
+
import { z as z13 } from "zod";
|
|
721
824
|
var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
722
825
|
function isValidIsoDate(s) {
|
|
723
826
|
if (typeof s !== "string") return false;
|
|
@@ -774,13 +877,13 @@ function registerUpdateNode(server2, client2) {
|
|
|
774
877
|
title: "Update Node",
|
|
775
878
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
776
879
|
description: 'Overwrite properties of an existing node; use for simple attribute changes like setting a status, priority, or due date. Only the provided fields will be changed. For content or structural changes, consider `naumu_delegate` instead - it understands the full graph context and can propagate updates to related nodes. Attribute keys AND values must match the schema for the node type; use naumu_get_schema to check valid attribute names, types, and values before updating. Date attributes accept either null (to clear), an ISO date string "YYYY-MM-DD" (single day), or { start: "YYYY-MM-DD", end?: "YYYY-MM-DD" } (inclusive range). Example: { "due_date": { "start": "2026-05-21", "end": "2026-05-23" } } or { "due_date": "2026-05-21" } or { "due_date": null }. Select attributes accept the value label as a string.',
|
|
777
|
-
inputSchema:
|
|
778
|
-
graphId:
|
|
779
|
-
nodeId:
|
|
780
|
-
label:
|
|
781
|
-
type:
|
|
782
|
-
content:
|
|
783
|
-
attributes:
|
|
880
|
+
inputSchema: z13.object({
|
|
881
|
+
graphId: z13.string().describe("The graph ID"),
|
|
882
|
+
nodeId: z13.string().describe("The node ID to update"),
|
|
883
|
+
label: z13.string().optional().describe("New display name"),
|
|
884
|
+
type: z13.string().optional().describe("New node type"),
|
|
885
|
+
content: z13.string().optional().describe("New content / description"),
|
|
886
|
+
attributes: z13.record(z13.string(), z13.unknown()).optional().describe('Schema-defined attributes to set. Examples: {"Status": "In Progress"}, {"due_date": {"start": "2026-05-21", "end": "2026-05-23"}}, {"due_date": "2026-05-21"}, {"due_date": null}.')
|
|
784
887
|
})
|
|
785
888
|
},
|
|
786
889
|
async ({ graphId, nodeId, label, type, content, attributes }) => {
|
|
@@ -892,11 +995,11 @@ function normalizeNodeAttributes(schema, nodeType, attributes) {
|
|
|
892
995
|
}
|
|
893
996
|
return errors.length > 0 ? { ok: false, errors } : { ok: true, attributes: normalized };
|
|
894
997
|
}
|
|
895
|
-
var NodeInput =
|
|
896
|
-
label:
|
|
897
|
-
type:
|
|
898
|
-
content:
|
|
899
|
-
attributes:
|
|
998
|
+
var NodeInput = z14.object({
|
|
999
|
+
label: z14.string().describe("Display name of the node"),
|
|
1000
|
+
type: z14.string().describe("Node type from the graph schema"),
|
|
1001
|
+
content: z14.string().min(1).describe("Rich text content / description. REQUIRED - every node must explain what it is."),
|
|
1002
|
+
attributes: z14.record(z14.string(), z14.unknown()).optional().describe(
|
|
900
1003
|
`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? }.`
|
|
901
1004
|
)
|
|
902
1005
|
});
|
|
@@ -907,9 +1010,9 @@ function registerAddNode(server2, client2) {
|
|
|
907
1010
|
title: "Add Nodes (bulk)",
|
|
908
1011
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
909
1012
|
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 treat a result as the same entity when its `matchedVia` is "semantic" or "both" AND its type matches AND the labels plausibly name the same thing - then reuse/update that node instead of creating one. (The `score` field is a rank-fusion value, not a similarity - do not compare it against a threshold.) 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.',
|
|
910
|
-
inputSchema:
|
|
911
|
-
graphId:
|
|
912
|
-
nodes:
|
|
1013
|
+
inputSchema: z14.object({
|
|
1014
|
+
graphId: z14.string().describe("The graph ID"),
|
|
1015
|
+
nodes: z14.array(NodeInput).min(1).max(25).describe("Batch of 1\u201325 nodes to create. Keep batches small for atomicity.")
|
|
913
1016
|
})
|
|
914
1017
|
},
|
|
915
1018
|
async ({ graphId, nodes }) => {
|
|
@@ -954,12 +1057,12 @@ Use naumu_get_schema to check valid attribute names, types, and values.`
|
|
|
954
1057
|
}
|
|
955
1058
|
|
|
956
1059
|
// ../mcp-core/src/tools/add-edge.ts
|
|
957
|
-
import { z as
|
|
958
|
-
var EdgeInput =
|
|
959
|
-
source:
|
|
960
|
-
target:
|
|
961
|
-
label:
|
|
962
|
-
isParent:
|
|
1060
|
+
import { z as z15 } from "zod";
|
|
1061
|
+
var EdgeInput = z15.object({
|
|
1062
|
+
source: z15.string().describe("Source node ID"),
|
|
1063
|
+
target: z15.string().describe("Target node ID"),
|
|
1064
|
+
label: z15.string().describe("Relationship type (e.g. RELATES_TO, SOLVES, TRACKS)"),
|
|
1065
|
+
isParent: z15.boolean().optional().describe("Whether this is a parent relationship")
|
|
963
1066
|
});
|
|
964
1067
|
function registerAddEdge(server2, client2) {
|
|
965
1068
|
server2.registerTool(
|
|
@@ -968,9 +1071,9 @@ function registerAddEdge(server2, client2) {
|
|
|
968
1071
|
title: "Add Edges (bulk)",
|
|
969
1072
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
970
1073
|
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.",
|
|
971
|
-
inputSchema:
|
|
972
|
-
graphId:
|
|
973
|
-
edges:
|
|
1074
|
+
inputSchema: z15.object({
|
|
1075
|
+
graphId: z15.string().describe("The graph ID"),
|
|
1076
|
+
edges: z15.array(EdgeInput).min(1).max(25).describe("Batch of 1\u201325 edges to create. Keep batches small for atomicity.")
|
|
974
1077
|
})
|
|
975
1078
|
},
|
|
976
1079
|
async ({ graphId, edges }) => {
|
|
@@ -989,7 +1092,7 @@ function registerAddEdge(server2, client2) {
|
|
|
989
1092
|
}
|
|
990
1093
|
|
|
991
1094
|
// ../mcp-core/src/tools/remove-node.ts
|
|
992
|
-
import { z as
|
|
1095
|
+
import { z as z16 } from "zod";
|
|
993
1096
|
function registerRemoveNode(server2, client2) {
|
|
994
1097
|
server2.registerTool(
|
|
995
1098
|
"naumu_remove_node",
|
|
@@ -997,9 +1100,9 @@ function registerRemoveNode(server2, client2) {
|
|
|
997
1100
|
title: "Remove Node",
|
|
998
1101
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
999
1102
|
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.",
|
|
1000
|
-
inputSchema:
|
|
1001
|
-
graphId:
|
|
1002
|
-
nodeId:
|
|
1103
|
+
inputSchema: z16.object({
|
|
1104
|
+
graphId: z16.string().describe("The graph ID"),
|
|
1105
|
+
nodeId: z16.string().describe("The node ID to delete")
|
|
1003
1106
|
})
|
|
1004
1107
|
},
|
|
1005
1108
|
async ({ graphId, nodeId }) => {
|
|
@@ -1012,7 +1115,7 @@ function registerRemoveNode(server2, client2) {
|
|
|
1012
1115
|
}
|
|
1013
1116
|
|
|
1014
1117
|
// ../mcp-core/src/tools/remove-edge.ts
|
|
1015
|
-
import { z as
|
|
1118
|
+
import { z as z17 } from "zod";
|
|
1016
1119
|
function registerRemoveEdge(server2, client2) {
|
|
1017
1120
|
server2.registerTool(
|
|
1018
1121
|
"naumu_remove_edge",
|
|
@@ -1020,11 +1123,11 @@ function registerRemoveEdge(server2, client2) {
|
|
|
1020
1123
|
title: "Remove Edge",
|
|
1021
1124
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1022
1125
|
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.",
|
|
1023
|
-
inputSchema:
|
|
1024
|
-
graphId:
|
|
1025
|
-
source:
|
|
1026
|
-
target:
|
|
1027
|
-
label:
|
|
1126
|
+
inputSchema: z17.object({
|
|
1127
|
+
graphId: z17.string().describe("The graph ID"),
|
|
1128
|
+
source: z17.string().describe("Source node id of the edge to delete"),
|
|
1129
|
+
target: z17.string().describe("Target node id of the edge to delete"),
|
|
1130
|
+
label: z17.string().describe('Relation label of the edge to delete (e.g. "AUTHORED"). Case-insensitive; non-alphanum chars are normalized.')
|
|
1028
1131
|
})
|
|
1029
1132
|
},
|
|
1030
1133
|
async ({ graphId, source, target, label }) => {
|
|
@@ -1041,11 +1144,11 @@ function registerRemoveEdge(server2, client2) {
|
|
|
1041
1144
|
}
|
|
1042
1145
|
|
|
1043
1146
|
// ../mcp-core/src/tools/remove-edges-bulk.ts
|
|
1044
|
-
import { z as
|
|
1045
|
-
var EdgeRef =
|
|
1046
|
-
source:
|
|
1047
|
-
target:
|
|
1048
|
-
label:
|
|
1147
|
+
import { z as z18 } from "zod";
|
|
1148
|
+
var EdgeRef = z18.object({
|
|
1149
|
+
source: z18.string().describe("Source node ID"),
|
|
1150
|
+
target: z18.string().describe("Target node ID"),
|
|
1151
|
+
label: z18.string().describe("Relation label of the edge to delete")
|
|
1049
1152
|
});
|
|
1050
1153
|
function registerRemoveEdgesBulk(server2, client2) {
|
|
1051
1154
|
server2.registerTool(
|
|
@@ -1054,9 +1157,9 @@ function registerRemoveEdgesBulk(server2, client2) {
|
|
|
1054
1157
|
title: "Remove Edges (bulk)",
|
|
1055
1158
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
1056
1159
|
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.",
|
|
1057
|
-
inputSchema:
|
|
1058
|
-
graphId:
|
|
1059
|
-
edges:
|
|
1160
|
+
inputSchema: z18.object({
|
|
1161
|
+
graphId: z18.string().describe("The graph ID"),
|
|
1162
|
+
edges: z18.array(EdgeRef).min(1).max(100).describe("1-100 edges to delete. Atomic per call - all succeed or none do.")
|
|
1060
1163
|
})
|
|
1061
1164
|
},
|
|
1062
1165
|
async ({ graphId, edges }) => {
|
|
@@ -1069,7 +1172,7 @@ function registerRemoveEdgesBulk(server2, client2) {
|
|
|
1069
1172
|
}
|
|
1070
1173
|
|
|
1071
1174
|
// ../mcp-core/src/tools/ask.ts
|
|
1072
|
-
import { z as
|
|
1175
|
+
import { z as z19 } from "zod";
|
|
1073
1176
|
function registerAsk(server2, client2) {
|
|
1074
1177
|
server2.registerTool(
|
|
1075
1178
|
"naumu_ask",
|
|
@@ -1085,10 +1188,10 @@ function registerAsk(server2, client2) {
|
|
|
1085
1188
|
openWorldHint: true
|
|
1086
1189
|
},
|
|
1087
1190
|
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.',
|
|
1088
|
-
inputSchema:
|
|
1089
|
-
graphId:
|
|
1090
|
-
question:
|
|
1091
|
-
topicIds:
|
|
1191
|
+
inputSchema: z19.object({
|
|
1192
|
+
graphId: z19.string().describe("The space (graph) id to ask about."),
|
|
1193
|
+
question: z19.string().max(4e3).describe("The question for @Naumu. Up to 4000 characters."),
|
|
1194
|
+
topicIds: z19.array(z19.string()).max(8).optional().describe(
|
|
1092
1195
|
"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)."
|
|
1093
1196
|
)
|
|
1094
1197
|
})
|
|
@@ -1114,7 +1217,7 @@ function registerAsk(server2, client2) {
|
|
|
1114
1217
|
}
|
|
1115
1218
|
|
|
1116
1219
|
// ../mcp-core/src/tools/delegate.ts
|
|
1117
|
-
import { z as
|
|
1220
|
+
import { z as z20 } from "zod";
|
|
1118
1221
|
function registerDelegate(server2, client2) {
|
|
1119
1222
|
server2.registerTool(
|
|
1120
1223
|
"naumu_delegate",
|
|
@@ -1130,11 +1233,11 @@ function registerDelegate(server2, client2) {
|
|
|
1130
1233
|
openWorldHint: true
|
|
1131
1234
|
},
|
|
1132
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. 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).',
|
|
1133
|
-
inputSchema:
|
|
1134
|
-
graphId:
|
|
1135
|
-
task:
|
|
1136
|
-
threadId:
|
|
1137
|
-
topicIds:
|
|
1236
|
+
inputSchema: z20.object({
|
|
1237
|
+
graphId: z20.string().describe("The space (graph) id to act in."),
|
|
1238
|
+
task: z20.string().describe("What you want @Naumu to do, add, or record."),
|
|
1239
|
+
threadId: z20.string().optional().describe("Continue an existing conversation; omit to start a new one."),
|
|
1240
|
+
topicIds: z20.array(z20.string()).max(8).optional().describe(
|
|
1138
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 keep the default private thread."
|
|
1139
1242
|
)
|
|
1140
1243
|
})
|
|
@@ -1178,7 +1281,7 @@ function registerDelegate(server2, client2) {
|
|
|
1178
1281
|
}
|
|
1179
1282
|
|
|
1180
1283
|
// ../mcp-core/src/tools/post-message.ts
|
|
1181
|
-
import { z as
|
|
1284
|
+
import { z as z21 } from "zod";
|
|
1182
1285
|
function registerPostMessage(server2, client2) {
|
|
1183
1286
|
server2.registerTool(
|
|
1184
1287
|
"naumu_post_message",
|
|
@@ -1193,12 +1296,12 @@ function registerPostMessage(server2, client2) {
|
|
|
1193
1296
|
openWorldHint: false
|
|
1194
1297
|
},
|
|
1195
1298
|
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. Write `content` in markdown (the default format): **bold**, *italic*, `inline code`, fenced code blocks, `- ` bullets, `1. ` ordered lists, and `> ` quotes all render natively; headings and tables are not supported and render as plain text. Mentions are inline pills: `@[Name](id)` mentions a person or bot and `#[label](topic-id)` tags a topic. 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; `@[Naumu](naumu-ai)` addresses the @Naumu agent (a bare `@naumu` in prose also summons it, so only type it when you mean to). A text @-mention renders as a pill but does NOT notify or add a non-participant \u2014 to loop a person in so they get notified, use naumu_add_participants after posting. Set contentFormat to "tiptap" only when you need rich content beyond the markdown subset, passing a Tiptap JSON document with mention nodes (`{ type: "mention", attrs: { id, label } }`). 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. By default this only appends text: @Naumu is NOT summoned unless the thread auto-responds or you mention it, so nothing is committed to the graph. Pass `invokeAgent: true` when you need @Naumu to act on the message (record a work-log entry, file a status update); it summons @Naumu unconditionally, even in paused threads, and @Naumu works in the background - poll naumu_read_thread to see what it did. To get a synthesised answer from @Naumu, use naumu_ask.',
|
|
1196
|
-
inputSchema:
|
|
1197
|
-
threadId:
|
|
1198
|
-
content:
|
|
1199
|
-
contentFormat:
|
|
1200
|
-
attachmentIds:
|
|
1201
|
-
invokeAgent:
|
|
1299
|
+
inputSchema: z21.object({
|
|
1300
|
+
threadId: z21.string().describe("The thread ID to post into. You must be a participant in this thread."),
|
|
1301
|
+
content: z21.string().optional().describe('Message body. Markdown by default (see the tool description for the supported subset and the `@[Name](id)` mention pill syntax); a Tiptap JSON document when contentFormat is "tiptap". Optional when `attachmentIds` is provided.'),
|
|
1302
|
+
contentFormat: z21.enum(["tiptap", "markdown"]).optional().describe('Format of `content`. Defaults to "markdown" (rendered subset plus `@[Name](id)` / `#[label](topic-id)` mention pills). Use "tiptap" for full rich content, e.g. a doc containing `{ type: "mention", attrs: { id: userIdOrEmailOrIdentityId, label: displayName } }`.'),
|
|
1303
|
+
attachmentIds: z21.array(z21.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."),
|
|
1304
|
+
invokeAgent: z21.boolean().optional().describe("Set true to summon @Naumu on this message unconditionally (mention-equivalent), even in threads where auto-response is paused. Required whenever the post must be committed to the graph (work-log entries, status updates). @Naumu processes in the background; poll naumu_read_thread. Defaults to false: plain append, no agent turn.")
|
|
1202
1305
|
})
|
|
1203
1306
|
},
|
|
1204
1307
|
async ({ threadId, content, contentFormat, attachmentIds, invokeAgent }) => {
|
|
@@ -1230,7 +1333,7 @@ function registerPostMessage(server2, client2) {
|
|
|
1230
1333
|
}
|
|
1231
1334
|
|
|
1232
1335
|
// ../mcp-core/src/tools/add-participants.ts
|
|
1233
|
-
import { z as
|
|
1336
|
+
import { z as z22 } from "zod";
|
|
1234
1337
|
function registerAddParticipants(server2, client2) {
|
|
1235
1338
|
server2.registerTool(
|
|
1236
1339
|
"naumu_add_participants",
|
|
@@ -1238,10 +1341,10 @@ function registerAddParticipants(server2, client2) {
|
|
|
1238
1341
|
title: "Add Participants",
|
|
1239
1342
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
1240
1343
|
description: 'Add space members to a Naumu thread as participants. This is how you loop someone into a conversation so they actually get notified: each newly added person gets an "added you to this thread" notification and a "has joined the conversation" system note, and the thread lands in their inbox. Text @-mentions in naumu_post_message do NOT notify or add non-participants \u2014 after posting a handoff or a message addressed to someone, add them here. Pass userIds (UUIDs) from naumu_list_members or naumu_get_thread participantDetails; emails are not accepted. Adding is idempotent \u2014 already-present participants are skipped silently.',
|
|
1241
|
-
inputSchema:
|
|
1242
|
-
threadId:
|
|
1243
|
-
userIds:
|
|
1244
|
-
role:
|
|
1344
|
+
inputSchema: z22.object({
|
|
1345
|
+
threadId: z22.string().describe("The thread ID to add participants to. You must be able to manage this thread."),
|
|
1346
|
+
userIds: z22.array(z22.string()).min(1).max(25).describe("User ids (UUIDs) of space members to add, from naumu_list_members or naumu_get_thread."),
|
|
1347
|
+
role: z22.enum(["editor", "viewer"]).optional().describe("Thread role for the added participants. Defaults to editor (can post); viewer is read-only.")
|
|
1245
1348
|
})
|
|
1246
1349
|
},
|
|
1247
1350
|
async ({ threadId, userIds, role }) => {
|
|
@@ -1270,7 +1373,7 @@ function registerAddParticipants(server2, client2) {
|
|
|
1270
1373
|
}
|
|
1271
1374
|
|
|
1272
1375
|
// ../mcp-core/src/tools/read-thread.ts
|
|
1273
|
-
import { z as
|
|
1376
|
+
import { z as z23 } from "zod";
|
|
1274
1377
|
function registerReadThread(server2, client2) {
|
|
1275
1378
|
server2.registerTool(
|
|
1276
1379
|
"naumu_read_thread",
|
|
@@ -1278,12 +1381,12 @@ function registerReadThread(server2, client2) {
|
|
|
1278
1381
|
title: "Read Thread",
|
|
1279
1382
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1280
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 a message attachment, pass its `attachments[].id` to naumu_get_attachment.',
|
|
1281
|
-
inputSchema:
|
|
1282
|
-
threadId:
|
|
1283
|
-
before:
|
|
1284
|
-
after:
|
|
1285
|
-
afterId:
|
|
1286
|
-
limit:
|
|
1384
|
+
inputSchema: z23.object({
|
|
1385
|
+
threadId: z23.string().describe("The thread ID to read from."),
|
|
1386
|
+
before: z23.number().optional().describe("Unix timestamp in milliseconds. Returns messages strictly older than this. Omit for the newest page."),
|
|
1387
|
+
after: z23.number().optional().describe("Unix timestamp in milliseconds. Returns only messages newer than this, oldest-first. Mutually exclusive with `before`."),
|
|
1388
|
+
afterId: z23.string().optional().describe("Message id that goes with `after` - the `latestId` from naumu_wait_for_activity. Only meaningful alongside `after`. With it, messages sharing the `after` millisecond are compared by id instead of being skipped."),
|
|
1389
|
+
limit: z23.number().int().min(1).max(200).optional().describe("Page size, default 50, max 200.")
|
|
1287
1390
|
})
|
|
1288
1391
|
},
|
|
1289
1392
|
async ({ threadId, before, after, afterId, limit }) => {
|
|
@@ -1311,7 +1414,7 @@ function registerReadThread(server2, client2) {
|
|
|
1311
1414
|
}
|
|
1312
1415
|
|
|
1313
1416
|
// ../mcp-core/src/tools/wait-for-activity.ts
|
|
1314
|
-
import { z as
|
|
1417
|
+
import { z as z24 } from "zod";
|
|
1315
1418
|
function registerWaitForActivity(server2, client2) {
|
|
1316
1419
|
server2.registerTool(
|
|
1317
1420
|
"naumu_wait_for_activity",
|
|
@@ -1321,16 +1424,16 @@ function registerWaitForActivity(server2, client2) {
|
|
|
1321
1424
|
// writes. Repeating the same `after` returns the same messages.
|
|
1322
1425
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1323
1426
|
description: 'Wait for the next messages in a Naumu thread. The call blocks for up to `timeoutSec` seconds server-side and returns the moment something new arrives, so this is the cheap way to attend a thread - use it instead of calling naumu_read_thread on a timer.\n\nReturns `{ messages, latest, latestId, timedOut }`. `messages` is oldest-first and each one carries `senderKind`: "self" (you), "user" (a human), "agent" (a bot or @Naumu), "system" (a system notice). Reply only to "user" messages unless your task says otherwise; ignore "agent" and "system" ones.\n\nTo keep attending, loop: call again with `after` = the `latest` you just received AND `afterId` = the `latestId`. Passing both is what stops two messages written in the same millisecond from being lost. Never reuse the old cursor after receiving messages - you will get the same ones back. If `timedOut` is true and `messages` is empty, nothing happened: call again with the same cursor.\n\nIf a returned message has `status: "processing"`, the reply is still being written - it is a placeholder that gets UPDATED in place, so waiting again will never show you the final text. Re-read that message with naumu_read_thread instead (pass `after` = its timestamp minus 1, or page back to it) until its status is `complete`.\n\nGet your first `after`/`afterId` from the newest message in naumu_read_thread. Before composing a reply call naumu_typing, then post it with naumu_post_message.',
|
|
1324
|
-
inputSchema:
|
|
1325
|
-
threadId:
|
|
1326
|
-
after:
|
|
1427
|
+
inputSchema: z24.object({
|
|
1428
|
+
threadId: z24.string().describe("The thread ID to wait on. You must be a participant."),
|
|
1429
|
+
after: z24.number().describe(
|
|
1327
1430
|
"Unix timestamp in milliseconds. Only messages newer than this count. Use the `latest` value from the previous call, or the newest message timestamp from naumu_read_thread."
|
|
1328
1431
|
),
|
|
1329
|
-
afterId:
|
|
1432
|
+
afterId: z24.string().optional().describe(
|
|
1330
1433
|
"Message id that goes with `after` - the `latestId` from the previous call. Only meaningful alongside `after`. With it, messages sharing the `after` millisecond are compared by id instead of being skipped, so nothing written in the same millisecond is lost."
|
|
1331
1434
|
),
|
|
1332
|
-
timeoutSec:
|
|
1333
|
-
excludeSelf:
|
|
1435
|
+
timeoutSec: z24.number().int().min(1).max(25).default(20).describe("Seconds to block before returning empty, 1 to 25, default 20."),
|
|
1436
|
+
excludeSelf: z24.boolean().default(true).describe(
|
|
1334
1437
|
'Ignore your own messages (senderKind "self") when deciding whether something new arrived. Default true - keep it on so your own reply does not end the next wait immediately.'
|
|
1335
1438
|
)
|
|
1336
1439
|
})
|
|
@@ -1359,7 +1462,7 @@ function registerWaitForActivity(server2, client2) {
|
|
|
1359
1462
|
}
|
|
1360
1463
|
|
|
1361
1464
|
// ../mcp-core/src/tools/whoami.ts
|
|
1362
|
-
import { z as
|
|
1465
|
+
import { z as z25 } from "zod";
|
|
1363
1466
|
function registerWhoami(server2, client2) {
|
|
1364
1467
|
server2.registerTool(
|
|
1365
1468
|
"naumu_whoami",
|
|
@@ -1367,7 +1470,7 @@ function registerWhoami(server2, client2) {
|
|
|
1367
1470
|
title: "Who Am I",
|
|
1368
1471
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1369
1472
|
description: 'Return who the calling key is, so you can bootstrap before the first real operation. A bot identity key returns its Identity row (id, graphId, name, instructions, allowedTools) plus its curated MCP tool manifest. 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. The live tool list is already available from tools/list, so it is not repeated here. No arguments. Always available regardless of the permission grid.',
|
|
1370
|
-
inputSchema:
|
|
1473
|
+
inputSchema: z25.object({})
|
|
1371
1474
|
},
|
|
1372
1475
|
async () => {
|
|
1373
1476
|
try {
|
|
@@ -1387,7 +1490,7 @@ function registerWhoami(server2, client2) {
|
|
|
1387
1490
|
}
|
|
1388
1491
|
|
|
1389
1492
|
// ../mcp-core/src/tools/list-threads.ts
|
|
1390
|
-
import { z as
|
|
1493
|
+
import { z as z26 } from "zod";
|
|
1391
1494
|
function sanitizeThreadParticipants(thread) {
|
|
1392
1495
|
if (!thread || typeof thread !== "object" || !("participantEmails" in thread)) {
|
|
1393
1496
|
return thread;
|
|
@@ -1402,11 +1505,11 @@ function registerListThreads(server2, client2) {
|
|
|
1402
1505
|
title: "List Threads",
|
|
1403
1506
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1404
1507
|
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). Pass `nodeId` alongside `graphId` to list only the conversations tied to that node (attached, or that created/modified it). 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.",
|
|
1405
|
-
inputSchema:
|
|
1406
|
-
graphId:
|
|
1407
|
-
nodeId:
|
|
1408
|
-
cursor:
|
|
1409
|
-
limit:
|
|
1508
|
+
inputSchema: z26.object({
|
|
1509
|
+
graphId: z26.string().optional().describe("Graph (space) ID. Required for user API keys; omit for bot identity keys (defaults to your own graph)."),
|
|
1510
|
+
nodeId: z26.string().optional().describe("Scope the listing to conversations tied to this node. Requires `graphId`."),
|
|
1511
|
+
cursor: z26.number().int().optional().describe("Unix timestamp ms \u2014 returns threads with `lastActivityAt` strictly older than this. Omit for the newest page."),
|
|
1512
|
+
limit: z26.number().int().min(1).max(200).optional().describe("Page size, default 50, max 200.")
|
|
1410
1513
|
})
|
|
1411
1514
|
},
|
|
1412
1515
|
async ({ graphId, nodeId, cursor, limit }) => {
|
|
@@ -1457,7 +1560,7 @@ function registerListThreads(server2, client2) {
|
|
|
1457
1560
|
}
|
|
1458
1561
|
|
|
1459
1562
|
// ../mcp-core/src/tools/list-topics.ts
|
|
1460
|
-
import { z as
|
|
1563
|
+
import { z as z27 } from "zod";
|
|
1461
1564
|
function toFilingDestination(topic) {
|
|
1462
1565
|
if (!topic || typeof topic !== "object") return null;
|
|
1463
1566
|
const t = topic;
|
|
@@ -1478,8 +1581,8 @@ function registerListTopics(server2, client2) {
|
|
|
1478
1581
|
title: "List Topics",
|
|
1479
1582
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1480
1583
|
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.",
|
|
1481
|
-
inputSchema:
|
|
1482
|
-
graphId:
|
|
1584
|
+
inputSchema: z27.object({
|
|
1585
|
+
graphId: z27.string().describe("The space (graph) id to list topics for.")
|
|
1483
1586
|
})
|
|
1484
1587
|
},
|
|
1485
1588
|
async ({ graphId }) => {
|
|
@@ -1502,7 +1605,7 @@ function registerListTopics(server2, client2) {
|
|
|
1502
1605
|
}
|
|
1503
1606
|
|
|
1504
1607
|
// ../mcp-core/src/tools/create-topic.ts
|
|
1505
|
-
import { z as
|
|
1608
|
+
import { z as z28 } from "zod";
|
|
1506
1609
|
var TOPIC_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
1507
1610
|
var TOPIC_NAME_MAX_LENGTH = 50;
|
|
1508
1611
|
var RESERVED_TOPIC_NAMES = [
|
|
@@ -1536,18 +1639,18 @@ function registerCreateTopic(server2, client2) {
|
|
|
1536
1639
|
title: "Create Topic",
|
|
1537
1640
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1538
1641
|
description: 'Create a new topic (filing destination) in a space; use when the topic a thread or note should be filed into does not exist yet. Check `naumu_list_topics` first so you reuse an existing topic instead of creating a near-duplicate. Returns the created topic `{id, name, color, visibilityMode, archived, openToWeb, webParticipation, createdAt, createdBy, memberIds, isMember}` - pass the returned `id` to the `topicIds` param of naumu_delegate, naumu_ask or naumu_create_note to file work into it. Name rules (validated before the call): lowercase letters, numbers and "-" only (channel-slug style, e.g. "core-team"), at most 50 characters, and never one of the reserved names all, everyone, naumu, here, misc, hidden, space, shared-with-you. Names are unique per space, case-insensitively. Requires admin rights on the space: editors and bot identities always get a 403, so do not attempt this on behalf of a bot. `visibilityMode` defaults to "open"; the caller is always added as a member, and `openToWeb` cannot be combined with a "closed" topic. Resolve `graphId` via naumu_list_graphs first.',
|
|
1539
|
-
inputSchema:
|
|
1540
|
-
graphId:
|
|
1541
|
-
name:
|
|
1642
|
+
inputSchema: z28.object({
|
|
1643
|
+
graphId: z28.string().describe("The space (graph) id to create the topic in."),
|
|
1644
|
+
name: z28.string().min(1).describe(
|
|
1542
1645
|
'Topic name in channel-slug form: lowercase letters, numbers and "-" only, max 50 chars, not a reserved name. Unique per space (case-insensitive).'
|
|
1543
1646
|
),
|
|
1544
|
-
color:
|
|
1545
|
-
visibilityMode:
|
|
1647
|
+
color: z28.string().optional().describe("Optional named color token for the topic badge. Omit unless the user asked for a specific color."),
|
|
1648
|
+
visibilityMode: z28.enum(["default", "open", "closed"]).optional().describe(
|
|
1546
1649
|
'Who can see and join the topic. "open" (the default) lets any space member join, "default" is the space default, "closed" is invite-only.'
|
|
1547
1650
|
),
|
|
1548
|
-
openToWeb:
|
|
1549
|
-
webParticipation:
|
|
1550
|
-
memberIds:
|
|
1651
|
+
openToWeb: z28.boolean().optional().describe('Expose the topic publicly on the web. Invalid together with visibilityMode "closed".'),
|
|
1652
|
+
webParticipation: z28.enum(["participate", "view-only"]).optional().describe('What public web visitors may do when openToWeb is true. Defaults to "view-only".'),
|
|
1653
|
+
memberIds: z28.array(z28.string()).optional().describe(
|
|
1551
1654
|
"User ids to add as topic members (get them from naumu_list_members). Ids that are not current space members are silently dropped. The caller is always added regardless."
|
|
1552
1655
|
)
|
|
1553
1656
|
})
|
|
@@ -1600,7 +1703,7 @@ function registerCreateTopic(server2, client2) {
|
|
|
1600
1703
|
}
|
|
1601
1704
|
|
|
1602
1705
|
// ../mcp-core/src/tools/get-thread.ts
|
|
1603
|
-
import { z as
|
|
1706
|
+
import { z as z29 } from "zod";
|
|
1604
1707
|
function sanitizeThreadParticipants2(thread) {
|
|
1605
1708
|
if (!thread || typeof thread !== "object" || !("participantEmails" in thread)) {
|
|
1606
1709
|
return thread;
|
|
@@ -1615,8 +1718,8 @@ function registerGetThread(server2, client2) {
|
|
|
1615
1718
|
title: "Get Thread",
|
|
1616
1719
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1617
1720
|
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.",
|
|
1618
|
-
inputSchema:
|
|
1619
|
-
threadId:
|
|
1721
|
+
inputSchema: z29.object({
|
|
1722
|
+
threadId: z29.string().describe("The thread ID to fetch.")
|
|
1620
1723
|
})
|
|
1621
1724
|
},
|
|
1622
1725
|
async ({ threadId }) => {
|
|
@@ -1638,7 +1741,7 @@ function registerGetThread(server2, client2) {
|
|
|
1638
1741
|
}
|
|
1639
1742
|
|
|
1640
1743
|
// ../mcp-core/src/tools/create-thread.ts
|
|
1641
|
-
import { z as
|
|
1744
|
+
import { z as z30 } from "zod";
|
|
1642
1745
|
function registerCreateThread(server2, client2) {
|
|
1643
1746
|
server2.registerTool(
|
|
1644
1747
|
"naumu_create_thread",
|
|
@@ -1653,23 +1756,23 @@ function registerCreateThread(server2, client2) {
|
|
|
1653
1756
|
openWorldHint: false
|
|
1654
1757
|
},
|
|
1655
1758
|
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.",
|
|
1656
|
-
inputSchema:
|
|
1657
|
-
title:
|
|
1658
|
-
participants:
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
type:
|
|
1662
|
-
userId:
|
|
1759
|
+
inputSchema: z30.object({
|
|
1760
|
+
title: z30.string().min(1).max(200).optional().describe('Thread title shown in the sidebar. If omitted, Naumu generates a default like "Conversation YYYY-MM-DD".'),
|
|
1761
|
+
participants: z30.array(
|
|
1762
|
+
z30.discriminatedUnion("type", [
|
|
1763
|
+
z30.object({
|
|
1764
|
+
type: z30.literal("user"),
|
|
1765
|
+
userId: z30.string().min(1).describe("User UUID \u2014 get these from `naumu_get_thread`/`naumu_read_thread` participant rosters or webhook payloads.")
|
|
1663
1766
|
}),
|
|
1664
|
-
|
|
1665
|
-
type:
|
|
1666
|
-
identityId:
|
|
1767
|
+
z30.object({
|
|
1768
|
+
type: z30.literal("identity"),
|
|
1769
|
+
identityId: z30.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.")
|
|
1667
1770
|
})
|
|
1668
1771
|
])
|
|
1669
1772
|
).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."),
|
|
1670
|
-
initialMessage:
|
|
1671
|
-
visibility:
|
|
1672
|
-
topicIds:
|
|
1773
|
+
initialMessage: z30.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."),
|
|
1774
|
+
visibility: z30.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."),
|
|
1775
|
+
topicIds: z30.array(z30.string()).max(8).optional().describe(
|
|
1673
1776
|
"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."
|
|
1674
1777
|
)
|
|
1675
1778
|
})
|
|
@@ -1698,7 +1801,7 @@ function registerCreateThread(server2, client2) {
|
|
|
1698
1801
|
}
|
|
1699
1802
|
|
|
1700
1803
|
// ../mcp-core/src/tools/request-attachment-upload.ts
|
|
1701
|
-
import { z as
|
|
1804
|
+
import { z as z31 } from "zod";
|
|
1702
1805
|
function registerRequestAttachmentUpload(server2, client2) {
|
|
1703
1806
|
server2.registerTool(
|
|
1704
1807
|
"naumu_request_attachment_upload",
|
|
@@ -1706,15 +1809,15 @@ function registerRequestAttachmentUpload(server2, client2) {
|
|
|
1706
1809
|
title: "Request Attachment Upload",
|
|
1707
1810
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1708
1811
|
description: "Request a presigned S3 upload URL to attach a file - pass exactly one of `threadId`, `noteId`, or `canvasId` for where the upload will land, then PUT the bytes directly to the returned URL. Same flow Naumu users use for file uploads. User API keys and OAuth sessions must also pass `graphId` (a user spans many spaces; resolve it via `naumu_list_graphs`); bot keys resolve it automatically.\n\nThree destinations, three follow-up calls:\n\u2022 Thread: presign with `threadId` \u2192 PUT the bytes \u2192 call `naumu_post_message` with the returned `attachmentId` in `attachmentIds`.\n\u2022 Note: presign with `noteId` \u2192 PUT the bytes \u2192 call `naumu_note_append` (or any note write tool) with `` in the markdown. That single call binds the upload, embeds it inline as the note's canonical media node, and files it into the space's Files & Media library - the same syntax works for images, video, audio, and other files, dispatched by the upload's MIME type.\n\u2022 Canvas: presign with `canvasId` \u2192 PUT the bytes \u2192 call `naumu_persist_canvas_attachment` to extend the upload past its default 1-hour TTL. Placing the persisted attachment onto the canvas itself still happens in the app UI.\n\nTo attach media to a graph node, upload into a thread that originated or modified that node - files surface on the node via its threads; there is no separate node-attachment flow.\n\nPer-MIME size caps apply: 10MB for types the agent reads whole (image, text, PDF, office docs), otherwise the space plan's umbrella cap (50MB free, 500MB team, 1GB max). Audio takes the plan cap, NOT the 10MB agent-read budget - it is transcribed on upload and only the transcript reaches an agent, so a multi-hour recording is a legitimate attachment. Over-cap requests are refused with HTTP 413 before any URL is minted, and the size is re-checked at bind time against the object that actually landed - so an oversized upload is refused there too, not silently accepted.\n\nReturns `{ attachmentId, uploadUrl, method, requiredHeaders, expiresAt }`. Use these EXACTLY:\n\u2022 `method` is \"PUT\".\n\u2022 Send every header in `requiredHeaders`. It carries BOTH `Content-Type` and `Content-Length`, and both are load-bearing: `Content-Length` is signed INTO the URL, so the body must be exactly that many bytes or S3 answers 403. PUT the whole file as one fixed-length body - do not stream it, do not use chunked transfer encoding, and do not send a different byte count than the `fileSize` you declared here.\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. Bind it (post the message, embed the note reference, or persist the canvas attachment) before then or the upload orphans.\n\nServer-side checks at bind time enforce that the attachment was uploaded by you, in this graph, for this thread/note/canvas - you cannot reuse an upload across destinations.",
|
|
1709
|
-
inputSchema:
|
|
1710
|
-
graphId:
|
|
1711
|
-
threadId:
|
|
1712
|
-
noteId:
|
|
1713
|
-
canvasId:
|
|
1714
|
-
fileName:
|
|
1715
|
-
fileType:
|
|
1716
|
-
fileSize:
|
|
1717
|
-
audioDurationSec:
|
|
1812
|
+
inputSchema: z31.object({
|
|
1813
|
+
graphId: z31.string().optional().describe("Graph (space) UUID the destination lives in. REQUIRED for user API keys and OAuth sessions - a user spans many spaces, so nothing can infer it; resolve it once via `naumu_list_graphs` and reuse it. Bot keys may omit it: a bot is pinned to one graph and the tool resolves it automatically."),
|
|
1814
|
+
threadId: z31.string().optional().describe("Destination thread - presign for a thread when the upload will be attached to a chat message via `naumu_post_message`'s `attachmentIds`. You must be a participant. Exactly one of `threadId`, `noteId`, or `canvasId` is required. The pending attachment is keyed to this thread - you cannot reuse it for a different one."),
|
|
1815
|
+
noteId: z31.string().optional().describe("Destination note (Thought) - presign for a note when the upload will be embedded via `` in a note write tool call. Exactly one of `threadId`, `noteId`, or `canvasId` is required. The pending attachment is keyed to this note - you cannot reuse it for a different one."),
|
|
1816
|
+
canvasId: z31.string().optional().describe("Destination canvas - presign for a canvas when the upload will be placed on a canvas; follow up with `naumu_persist_canvas_attachment` to extend its TTL. Exactly one of `threadId`, `noteId`, or `canvasId` is required. The pending attachment is keyed to this canvas - you cannot reuse it for a different one."),
|
|
1817
|
+
fileName: z31.string().min(1).describe("Original filename (with extension). Used as the display name and for the S3 object suffix. Special characters are sanitized server-side."),
|
|
1818
|
+
fileType: z31.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."),
|
|
1819
|
+
fileSize: z31.number().int().positive().describe("File size in bytes, exact. Validated against per-MIME caps before the URL is issued - exceeding the cap returns a 413 (not a 400; 400 means a different rejection). This number is also signed into the upload URL as `Content-Length`, so it is a commitment, not an estimate: PUT exactly this many bytes. Audio is validated against the plan umbrella cap (50MB free / 500MB team / 1GB max), not the 10MB agent-read budget."),
|
|
1820
|
+
audioDurationSec: z31.number().positive().optional().describe("For audio attachments, duration in seconds. Send it whenever you know it: audio carries an 8-hour ceiling on top of the size cap, and nothing measures the file server-side, so that ceiling is checked ONLY against a length you report. Omitting it skips the check rather than failing it - the size cap is what still binds you. Omit it rather than guessing: a wrong value is worse than none, and a deliberate under-report is a policy violation, not a workaround.")
|
|
1718
1821
|
}).refine(
|
|
1719
1822
|
(data) => [data.threadId, data.noteId, data.canvasId].filter((v) => v !== void 0).length === 1,
|
|
1720
1823
|
{ message: "Exactly one of threadId, noteId, or canvasId is required - pick the single destination this upload is for." }
|
|
@@ -1762,7 +1865,7 @@ function registerRequestAttachmentUpload(server2, client2) {
|
|
|
1762
1865
|
}
|
|
1763
1866
|
|
|
1764
1867
|
// ../mcp-core/src/tools/persist-canvas-attachment.ts
|
|
1765
|
-
import { z as
|
|
1868
|
+
import { z as z32 } from "zod";
|
|
1766
1869
|
function registerPersistCanvasAttachment(server2, client2) {
|
|
1767
1870
|
server2.registerTool(
|
|
1768
1871
|
"naumu_persist_canvas_attachment",
|
|
@@ -1770,8 +1873,8 @@ function registerPersistCanvasAttachment(server2, client2) {
|
|
|
1770
1873
|
title: "Persist Canvas Attachment",
|
|
1771
1874
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1772
1875
|
description: "Extend a canvas-bound pending attachment's TTL from its default 1 hour to 30 days; call right after `naumu_request_attachment_upload` (with `canvasId` set) and the S3 PUT, so the upload survives long enough to be used. This only persists the upload - actually placing it onto the canvas still happens in the app UI, there is no MCP canvas-editing tool yet. Safe to call more than once for the same attachmentId.",
|
|
1773
|
-
inputSchema:
|
|
1774
|
-
attachmentId:
|
|
1876
|
+
inputSchema: z32.object({
|
|
1877
|
+
attachmentId: z32.string().min(1).describe("The `attachmentId` returned by `naumu_request_attachment_upload` for this canvas.")
|
|
1775
1878
|
})
|
|
1776
1879
|
},
|
|
1777
1880
|
async ({ attachmentId }) => {
|
|
@@ -1792,7 +1895,7 @@ function registerPersistCanvasAttachment(server2, client2) {
|
|
|
1792
1895
|
}
|
|
1793
1896
|
|
|
1794
1897
|
// ../mcp-core/src/tools/get-attachment.ts
|
|
1795
|
-
import { z as
|
|
1898
|
+
import { z as z33 } from "zod";
|
|
1796
1899
|
var DOWNLOAD_URL_TTL_SECONDS = 900;
|
|
1797
1900
|
var MAX_INLINE_PREVIEW_BYTES = 4 * 1024 * 1024;
|
|
1798
1901
|
function normalizeAttachmentId(input) {
|
|
@@ -1813,8 +1916,8 @@ function registerGetAttachment(server2, client2) {
|
|
|
1813
1916
|
title: "Get Attachment",
|
|
1814
1917
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1815
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.",
|
|
1816
|
-
inputSchema:
|
|
1817
|
-
attachmentId:
|
|
1919
|
+
inputSchema: z33.object({
|
|
1920
|
+
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.")
|
|
1818
1921
|
})
|
|
1819
1922
|
},
|
|
1820
1923
|
async ({ attachmentId }) => {
|
|
@@ -1864,7 +1967,7 @@ function registerGetAttachment(server2, client2) {
|
|
|
1864
1967
|
}
|
|
1865
1968
|
|
|
1866
1969
|
// ../mcp-core/src/tools/add-reaction.ts
|
|
1867
|
-
import { z as
|
|
1970
|
+
import { z as z34 } from "zod";
|
|
1868
1971
|
function registerAddReaction(server2, client2) {
|
|
1869
1972
|
server2.registerTool(
|
|
1870
1973
|
"naumu_add_reaction",
|
|
@@ -1872,10 +1975,10 @@ function registerAddReaction(server2, client2) {
|
|
|
1872
1975
|
title: "Add Reaction",
|
|
1873
1976
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1874
1977
|
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.',
|
|
1875
|
-
inputSchema:
|
|
1876
|
-
threadId:
|
|
1877
|
-
messageId:
|
|
1878
|
-
emoji:
|
|
1978
|
+
inputSchema: z34.object({
|
|
1979
|
+
threadId: z34.string().describe("Thread containing the message. You must be a participant."),
|
|
1980
|
+
messageId: z34.string().describe("The message to react to."),
|
|
1981
|
+
emoji: z34.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.')
|
|
1879
1982
|
})
|
|
1880
1983
|
},
|
|
1881
1984
|
async ({ threadId, messageId, emoji }) => {
|
|
@@ -1899,7 +2002,7 @@ function registerAddReaction(server2, client2) {
|
|
|
1899
2002
|
}
|
|
1900
2003
|
|
|
1901
2004
|
// ../mcp-core/src/tools/remove-reaction.ts
|
|
1902
|
-
import { z as
|
|
2005
|
+
import { z as z35 } from "zod";
|
|
1903
2006
|
function registerRemoveReaction(server2, client2) {
|
|
1904
2007
|
server2.registerTool(
|
|
1905
2008
|
"naumu_remove_reaction",
|
|
@@ -1907,10 +2010,10 @@ function registerRemoveReaction(server2, client2) {
|
|
|
1907
2010
|
title: "Remove Reaction",
|
|
1908
2011
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1909
2012
|
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.",
|
|
1910
|
-
inputSchema:
|
|
1911
|
-
threadId:
|
|
1912
|
-
messageId:
|
|
1913
|
-
emoji:
|
|
2013
|
+
inputSchema: z35.object({
|
|
2014
|
+
threadId: z35.string().describe("Thread containing the message. You must be a participant."),
|
|
2015
|
+
messageId: z35.string().describe("The message to remove your reaction from."),
|
|
2016
|
+
emoji: z35.string().min(1).describe("Emoji character to remove (must match what you originally reacted with).")
|
|
1914
2017
|
})
|
|
1915
2018
|
},
|
|
1916
2019
|
async ({ threadId, messageId, emoji }) => {
|
|
@@ -1934,7 +2037,7 @@ function registerRemoveReaction(server2, client2) {
|
|
|
1934
2037
|
}
|
|
1935
2038
|
|
|
1936
2039
|
// ../mcp-core/src/tools/naumu-typing.ts
|
|
1937
|
-
import { z as
|
|
2040
|
+
import { z as z36 } from "zod";
|
|
1938
2041
|
function registerNaumuTyping(server2, client2) {
|
|
1939
2042
|
server2.registerTool(
|
|
1940
2043
|
"naumu_typing",
|
|
@@ -1945,9 +2048,9 @@ function registerNaumuTyping(server2, client2) {
|
|
|
1945
2048
|
// repeating the same state is a no-op renew, so idempotent.
|
|
1946
2049
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1947
2050
|
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.',
|
|
1948
|
-
inputSchema:
|
|
1949
|
-
threadId:
|
|
1950
|
-
state:
|
|
2051
|
+
inputSchema: z36.object({
|
|
2052
|
+
threadId: z36.string().describe("The thread ID to set typing in. You must be a participant."),
|
|
2053
|
+
state: z36.enum(["start", "stop"]).describe('"start" acquires/renews the lease; "stop" ends it and clears the pill immediately.')
|
|
1951
2054
|
})
|
|
1952
2055
|
},
|
|
1953
2056
|
async ({ threadId, state }) => {
|
|
@@ -1968,7 +2071,7 @@ function registerNaumuTyping(server2, client2) {
|
|
|
1968
2071
|
}
|
|
1969
2072
|
|
|
1970
2073
|
// ../mcp-core/src/tools/note-read.ts
|
|
1971
|
-
import { z as
|
|
2074
|
+
import { z as z37 } from "zod";
|
|
1972
2075
|
function registerNoteRead(server2, client2) {
|
|
1973
2076
|
server2.registerTool(
|
|
1974
2077
|
"naumu_note_read",
|
|
@@ -1976,8 +2079,8 @@ function registerNoteRead(server2, client2) {
|
|
|
1976
2079
|
title: "Read Note",
|
|
1977
2080
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1978
2081
|
description: "Read the current contents of a note as markdown, with its title and `connections` (the graph nodes the note is tied to); 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.",
|
|
1979
|
-
inputSchema:
|
|
1980
|
-
noteId:
|
|
2082
|
+
inputSchema: z37.object({
|
|
2083
|
+
noteId: z37.string().describe("The note (Thought) ID")
|
|
1981
2084
|
})
|
|
1982
2085
|
},
|
|
1983
2086
|
async ({ noteId }) => {
|
|
@@ -1990,7 +2093,7 @@ function registerNoteRead(server2, client2) {
|
|
|
1990
2093
|
}
|
|
1991
2094
|
|
|
1992
2095
|
// ../mcp-core/src/tools/note-append.ts
|
|
1993
|
-
import { z as
|
|
2096
|
+
import { z as z38 } from "zod";
|
|
1994
2097
|
function registerNoteAppend(server2, client2) {
|
|
1995
2098
|
server2.registerTool(
|
|
1996
2099
|
"naumu_note_append",
|
|
@@ -1998,9 +2101,9 @@ function registerNoteAppend(server2, client2) {
|
|
|
1998
2101
|
title: "Append to Note",
|
|
1999
2102
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
2000
2103
|
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. To embed media, write `` as a sole-line image - the id must come from `naumu_request_attachment_upload` presigned with this same `noteId`. The one syntax covers images, video, audio, and other files, dispatched by the upload's MIME type into the note's canonical media node; an unresolvable or wrong-note id fails the whole write with a 400 naming the bad id. An id already embedded in the note can be repeated safely without re-uploading.",
|
|
2001
|
-
inputSchema:
|
|
2002
|
-
noteId:
|
|
2003
|
-
markdown:
|
|
2104
|
+
inputSchema: z38.object({
|
|
2105
|
+
noteId: z38.string().describe("The note (Thought) ID to append to"),
|
|
2106
|
+
markdown: z38.string().min(1).describe("Markdown content to append at the end of the note")
|
|
2004
2107
|
})
|
|
2005
2108
|
},
|
|
2006
2109
|
async ({ noteId, markdown }) => {
|
|
@@ -2013,7 +2116,7 @@ function registerNoteAppend(server2, client2) {
|
|
|
2013
2116
|
}
|
|
2014
2117
|
|
|
2015
2118
|
// ../mcp-core/src/tools/note-insert.ts
|
|
2016
|
-
import { z as
|
|
2119
|
+
import { z as z39 } from "zod";
|
|
2017
2120
|
function registerNoteInsert(server2, client2) {
|
|
2018
2121
|
server2.registerTool(
|
|
2019
2122
|
"naumu_note_insert",
|
|
@@ -2021,10 +2124,10 @@ function registerNoteInsert(server2, client2) {
|
|
|
2021
2124
|
title: "Insert After Heading",
|
|
2022
2125
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
2023
2126
|
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. To embed media, write `` as a sole-line image - the id must come from `naumu_request_attachment_upload` presigned with this same `noteId`. The one syntax covers images, video, audio, and other files, dispatched by the upload's MIME type; an unresolvable or wrong-note id fails the whole write with a 400 naming the bad id.",
|
|
2024
|
-
inputSchema:
|
|
2025
|
-
noteId:
|
|
2026
|
-
headingText:
|
|
2027
|
-
markdown:
|
|
2127
|
+
inputSchema: z39.object({
|
|
2128
|
+
noteId: z39.string().describe("The note (Thought) ID"),
|
|
2129
|
+
headingText: z39.string().min(1).describe("Exact text of the heading whose section the new content follows"),
|
|
2130
|
+
markdown: z39.string().min(1).describe("Markdown content to insert at the end of that section")
|
|
2028
2131
|
})
|
|
2029
2132
|
},
|
|
2030
2133
|
async ({ noteId, headingText, markdown }) => {
|
|
@@ -2040,7 +2143,7 @@ function registerNoteInsert(server2, client2) {
|
|
|
2040
2143
|
}
|
|
2041
2144
|
|
|
2042
2145
|
// ../mcp-core/src/tools/note-replace-section.ts
|
|
2043
|
-
import { z as
|
|
2146
|
+
import { z as z40 } from "zod";
|
|
2044
2147
|
function registerNoteReplaceSection(server2, client2) {
|
|
2045
2148
|
server2.registerTool(
|
|
2046
2149
|
"naumu_note_replace_section",
|
|
@@ -2048,11 +2151,11 @@ function registerNoteReplaceSection(server2, client2) {
|
|
|
2048
2151
|
title: "Replace Section",
|
|
2049
2152
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
2050
2153
|
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. To embed media, write `` as a sole-line image - the id must come from `naumu_request_attachment_upload` presigned with this same `noteId`. The one syntax covers images, video, audio, and other files, dispatched by the upload's MIME type; an unresolvable or wrong-note id fails the whole write with a 400 naming the bad id. An id already embedded elsewhere in the note can be repeated safely without re-uploading.",
|
|
2051
|
-
inputSchema:
|
|
2052
|
-
noteId:
|
|
2053
|
-
headingText:
|
|
2054
|
-
markdown:
|
|
2055
|
-
keepHeading:
|
|
2154
|
+
inputSchema: z40.object({
|
|
2155
|
+
noteId: z40.string().describe("The note (Thought) ID"),
|
|
2156
|
+
headingText: z40.string().min(1).describe("Exact text of the heading anchoring the section"),
|
|
2157
|
+
markdown: z40.string().describe("Replacement markdown for the section body"),
|
|
2158
|
+
keepHeading: z40.boolean().optional().describe("Whether to keep the heading row itself. Default true.")
|
|
2056
2159
|
})
|
|
2057
2160
|
},
|
|
2058
2161
|
async ({ noteId, headingText, markdown, keepHeading }) => {
|
|
@@ -2069,7 +2172,7 @@ function registerNoteReplaceSection(server2, client2) {
|
|
|
2069
2172
|
}
|
|
2070
2173
|
|
|
2071
2174
|
// ../mcp-core/src/tools/note-delete-section.ts
|
|
2072
|
-
import { z as
|
|
2175
|
+
import { z as z41 } from "zod";
|
|
2073
2176
|
function registerNoteDeleteSection(server2, client2) {
|
|
2074
2177
|
server2.registerTool(
|
|
2075
2178
|
"naumu_note_delete_section",
|
|
@@ -2077,9 +2180,9 @@ function registerNoteDeleteSection(server2, client2) {
|
|
|
2077
2180
|
title: "Delete Section",
|
|
2078
2181
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
2079
2182
|
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.",
|
|
2080
|
-
inputSchema:
|
|
2081
|
-
noteId:
|
|
2082
|
-
headingText:
|
|
2183
|
+
inputSchema: z41.object({
|
|
2184
|
+
noteId: z41.string().describe("The note (Thought) ID"),
|
|
2185
|
+
headingText: z41.string().min(1).describe("Exact text of the heading whose section will be deleted")
|
|
2083
2186
|
})
|
|
2084
2187
|
},
|
|
2085
2188
|
async ({ noteId, headingText }) => {
|
|
@@ -2094,7 +2197,7 @@ function registerNoteDeleteSection(server2, client2) {
|
|
|
2094
2197
|
}
|
|
2095
2198
|
|
|
2096
2199
|
// ../mcp-core/src/tools/note-replace.ts
|
|
2097
|
-
import { z as
|
|
2200
|
+
import { z as z42 } from "zod";
|
|
2098
2201
|
function registerNoteReplace(server2, client2) {
|
|
2099
2202
|
server2.registerTool(
|
|
2100
2203
|
"naumu_note_replace",
|
|
@@ -2102,9 +2205,9 @@ function registerNoteReplace(server2, client2) {
|
|
|
2102
2205
|
title: "Replace Note",
|
|
2103
2206
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
2104
2207
|
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. To embed media, write `` as a sole-line image - the id must come from `naumu_request_attachment_upload` presigned with this same `noteId`. The one syntax covers images, video, audio, and other files, dispatched by the upload's MIME type; an unresolvable or wrong-note id fails the whole write with a 400 naming the bad id. Ids already embedded in the note (read them back via `naumu_note_read`) can be repeated safely without re-uploading.",
|
|
2105
|
-
inputSchema:
|
|
2106
|
-
noteId:
|
|
2107
|
-
markdown:
|
|
2208
|
+
inputSchema: z42.object({
|
|
2209
|
+
noteId: z42.string().describe("The note (Thought) ID"),
|
|
2210
|
+
markdown: z42.string().describe("New markdown content for the entire note")
|
|
2108
2211
|
})
|
|
2109
2212
|
},
|
|
2110
2213
|
async ({ noteId, markdown }) => {
|
|
@@ -2117,7 +2220,7 @@ function registerNoteReplace(server2, client2) {
|
|
|
2117
2220
|
}
|
|
2118
2221
|
|
|
2119
2222
|
// ../mcp-core/src/tools/note-find-replace.ts
|
|
2120
|
-
import { z as
|
|
2223
|
+
import { z as z43 } from "zod";
|
|
2121
2224
|
function registerNoteFindReplace(server2, client2) {
|
|
2122
2225
|
server2.registerTool(
|
|
2123
2226
|
"naumu_note_find_replace",
|
|
@@ -2125,11 +2228,11 @@ function registerNoteFindReplace(server2, client2) {
|
|
|
2125
2228
|
title: "Find/Replace in Note",
|
|
2126
2229
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
2127
2230
|
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. `replace` can embed media by containing `` as a sole-line image - the id must come from `naumu_request_attachment_upload` presigned with this same `noteId`; an unresolvable or wrong-note id fails the whole write with a 400 naming the bad id.",
|
|
2128
|
-
inputSchema:
|
|
2129
|
-
noteId:
|
|
2130
|
-
find:
|
|
2131
|
-
replace:
|
|
2132
|
-
all:
|
|
2231
|
+
inputSchema: z43.object({
|
|
2232
|
+
noteId: z43.string().describe("The note (Thought) ID"),
|
|
2233
|
+
find: z43.string().min(1).describe("Substring to search for. Literal - no regex."),
|
|
2234
|
+
replace: z43.string().describe("Replacement string. May be empty to delete the match."),
|
|
2235
|
+
all: z43.boolean().optional().describe("Replace all occurrences (default true). Pass false to replace only the first.")
|
|
2133
2236
|
})
|
|
2134
2237
|
},
|
|
2135
2238
|
async ({ noteId, find, replace, all }) => {
|
|
@@ -2146,18 +2249,18 @@ function registerNoteFindReplace(server2, client2) {
|
|
|
2146
2249
|
}
|
|
2147
2250
|
|
|
2148
2251
|
// ../mcp-core/src/tools/note-batch.ts
|
|
2149
|
-
import { z as
|
|
2252
|
+
import { z as z44 } from "zod";
|
|
2150
2253
|
var MAX_BATCH_OPS = 20;
|
|
2151
|
-
var opSchema =
|
|
2152
|
-
op:
|
|
2153
|
-
markdown:
|
|
2154
|
-
heading:
|
|
2254
|
+
var opSchema = z44.object({
|
|
2255
|
+
op: z44.enum(["append", "insertAfter", "replaceSection", "deleteSection", "replace", "findReplace"]).describe("Which edit to perform."),
|
|
2256
|
+
markdown: z44.string().optional().describe("Markdown payload. Required for append, insertAfter, replaceSection, replace."),
|
|
2257
|
+
heading: z44.string().optional().describe(
|
|
2155
2258
|
'Target heading text. Required for insertAfter, replaceSection, deleteSection. Accepts the markdown form ("## Title") or the bare text.'
|
|
2156
2259
|
),
|
|
2157
|
-
keepHeading:
|
|
2158
|
-
find:
|
|
2159
|
-
replace:
|
|
2160
|
-
all:
|
|
2260
|
+
keepHeading: z44.boolean().optional().describe("replaceSection only: keep the heading row and replace just its body (default true)."),
|
|
2261
|
+
find: z44.string().optional().describe("findReplace only: literal substring to search for."),
|
|
2262
|
+
replace: z44.string().optional().describe("findReplace only: replacement string. May be empty to delete the match."),
|
|
2263
|
+
all: z44.boolean().optional().describe("findReplace only: replace every occurrence (default true).")
|
|
2161
2264
|
}).describe("One edit in the batch.");
|
|
2162
2265
|
function registerNoteBatch(server2, client2) {
|
|
2163
2266
|
server2.registerTool(
|
|
@@ -2166,9 +2269,9 @@ function registerNoteBatch(server2, client2) {
|
|
|
2166
2269
|
title: "Batch Edit Note",
|
|
2167
2270
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
2168
2271
|
description: "Apply several edits to ONE note in a single write. This is the preferred way to make more than one change to the same note: the whole list runs in one document transaction, so readers see one update event instead of one per edit, and you pay one round trip instead of N. Each entry mirrors a single-op note tool - `append` {markdown}, `insertAfter` {heading, markdown}, `replaceSection` {heading, markdown, keepHeading?}, `deleteSection` {heading}, `replace` {markdown}, `findReplace` {find, replace, all?} - and they run in the order given, each seeing the result of the one before. Max " + MAX_BATCH_OPS + " ops. All-or-nothing: the batch is rehearsed first, so if any op fails (most often a heading that does not exist) the note is left completely untouched and the error names the failing index. Media works as in the single-op tools: write `` on its own line, with an id presigned by `naumu_request_attachment_upload` for this same `noteId`.",
|
|
2169
|
-
inputSchema:
|
|
2170
|
-
noteId:
|
|
2171
|
-
ops:
|
|
2272
|
+
inputSchema: z44.object({
|
|
2273
|
+
noteId: z44.string().describe("The note (Thought) ID to edit"),
|
|
2274
|
+
ops: z44.array(opSchema).min(1).max(MAX_BATCH_OPS).describe(`Edits to apply, in order. Max ${MAX_BATCH_OPS}.`)
|
|
2172
2275
|
})
|
|
2173
2276
|
},
|
|
2174
2277
|
async ({ noteId, ops }) => {
|
|
@@ -2181,7 +2284,7 @@ function registerNoteBatch(server2, client2) {
|
|
|
2181
2284
|
}
|
|
2182
2285
|
|
|
2183
2286
|
// ../mcp-core/src/tools/create-note.ts
|
|
2184
|
-
import { z as
|
|
2287
|
+
import { z as z45 } from "zod";
|
|
2185
2288
|
function registerCreateNote(server2, client2) {
|
|
2186
2289
|
server2.registerTool(
|
|
2187
2290
|
"naumu_create_note",
|
|
@@ -2189,12 +2292,12 @@ function registerCreateNote(server2, client2) {
|
|
|
2189
2292
|
title: "Create Note",
|
|
2190
2293
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
2191
2294
|
description: "Create a note in a graph, optionally with its full content already in place. Pass `markdown` to create the note and its body in a single call - the preferred path for imports and for any content you already hold. Returns the new note row including its `id`; use `naumu_note_append` / `naumu_note_replace` for LATER edits, not to fill in content you could have passed here. The returned row is the note as it stood BEFORE the body write landed, so its content may read as empty - the write still succeeded, do not retry the call or re-append the body. `attachment://` refs are not accepted in create-time `markdown`: upload the file after the note exists and embed it with a note write tool. 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 - a bot passing `topicIds` is rejected (bots hold no topic membership); use `sharedWithSpace` instead.",
|
|
2192
|
-
inputSchema:
|
|
2193
|
-
graphId:
|
|
2194
|
-
title:
|
|
2195
|
-
markdown:
|
|
2196
|
-
sharedWithSpace:
|
|
2197
|
-
topicIds:
|
|
2295
|
+
inputSchema: z45.object({
|
|
2296
|
+
graphId: z45.string().describe("The graph ID to create the note in"),
|
|
2297
|
+
title: z45.string().optional().describe("Optional title for the note"),
|
|
2298
|
+
markdown: z45.string().optional().describe("Full initial note content as markdown. Provide it here to create the note with its content in a single call - preferred for imports; do not restate large content through extra edit calls."),
|
|
2299
|
+
sharedWithSpace: z45.boolean().optional().describe("Share the note with everyone in the space. Omit (or false) to keep it private to its participants."),
|
|
2300
|
+
topicIds: z45.array(z45.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.")
|
|
2198
2301
|
})
|
|
2199
2302
|
},
|
|
2200
2303
|
async ({ graphId, title, markdown, sharedWithSpace, topicIds }) => {
|
|
@@ -2207,7 +2310,7 @@ function registerCreateNote(server2, client2) {
|
|
|
2207
2310
|
}
|
|
2208
2311
|
|
|
2209
2312
|
// ../mcp-core/src/tools/list-schema-violations.ts
|
|
2210
|
-
import { z as
|
|
2313
|
+
import { z as z46 } from "zod";
|
|
2211
2314
|
var DEFAULT_EXAMPLE_LIMIT = 5;
|
|
2212
2315
|
var rowsForKind = (violations, kind) => {
|
|
2213
2316
|
const rows = [];
|
|
@@ -2233,12 +2336,12 @@ function registerListSchemaViolations(server2, client2) {
|
|
|
2233
2336
|
title: "List Schema Violations",
|
|
2234
2337
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2235
2338
|
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.",
|
|
2236
|
-
inputSchema:
|
|
2237
|
-
graphId:
|
|
2238
|
-
kind:
|
|
2339
|
+
inputSchema: z46.object({
|
|
2340
|
+
graphId: z46.string().describe("The graph ID"),
|
|
2341
|
+
kind: z46.string().optional().describe(
|
|
2239
2342
|
'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.'
|
|
2240
2343
|
),
|
|
2241
|
-
limit:
|
|
2344
|
+
limit: z46.number().int().min(1).optional().describe(
|
|
2242
2345
|
"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)."
|
|
2243
2346
|
)
|
|
2244
2347
|
})
|
|
@@ -2290,7 +2393,7 @@ function registerListSchemaViolations(server2, client2) {
|
|
|
2290
2393
|
}
|
|
2291
2394
|
|
|
2292
2395
|
// ../mcp-core/src/tools/list-dense-nodes.ts
|
|
2293
|
-
import { z as
|
|
2396
|
+
import { z as z47 } from "zod";
|
|
2294
2397
|
function registerListDenseNodes(server2, client2) {
|
|
2295
2398
|
server2.registerTool(
|
|
2296
2399
|
"naumu_list_dense_nodes",
|
|
@@ -2298,10 +2401,10 @@ function registerListDenseNodes(server2, client2) {
|
|
|
2298
2401
|
title: "List Dense Nodes",
|
|
2299
2402
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2300
2403
|
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.',
|
|
2301
|
-
inputSchema:
|
|
2302
|
-
graphId:
|
|
2303
|
-
minConnections:
|
|
2304
|
-
nodeTypes:
|
|
2404
|
+
inputSchema: z47.object({
|
|
2405
|
+
graphId: z47.string().describe("The graph ID"),
|
|
2406
|
+
minConnections: z47.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."),
|
|
2407
|
+
nodeTypes: z47.array(z47.string()).optional().describe("Optional list of node types to restrict the scan to.")
|
|
2305
2408
|
})
|
|
2306
2409
|
},
|
|
2307
2410
|
async ({ graphId, minConnections, nodeTypes }) => {
|
|
@@ -2319,7 +2422,7 @@ function registerListDenseNodes(server2, client2) {
|
|
|
2319
2422
|
}
|
|
2320
2423
|
|
|
2321
2424
|
// ../mcp-core/src/tools/list-node-connections.ts
|
|
2322
|
-
import { z as
|
|
2425
|
+
import { z as z48 } from "zod";
|
|
2323
2426
|
function registerListNodeConnections(server2, client2) {
|
|
2324
2427
|
server2.registerTool(
|
|
2325
2428
|
"naumu_list_node_connections",
|
|
@@ -2327,11 +2430,11 @@ function registerListNodeConnections(server2, client2) {
|
|
|
2327
2430
|
title: "List Node Connections",
|
|
2328
2431
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2329
2432
|
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}}] }`.',
|
|
2330
|
-
inputSchema:
|
|
2331
|
-
graphId:
|
|
2332
|
-
nodeId:
|
|
2333
|
-
edgeType:
|
|
2334
|
-
direction:
|
|
2433
|
+
inputSchema: z48.object({
|
|
2434
|
+
graphId: z48.string().describe("The graph ID"),
|
|
2435
|
+
nodeId: z48.string().describe("The node ID to inspect"),
|
|
2436
|
+
edgeType: z48.string().optional().describe('Restrict to a specific relation label (e.g. "ASSOCIATED_WITH"). Case-insensitive; non-alphanum chars are normalized.'),
|
|
2437
|
+
direction: z48.enum(["in", "out", "both"]).optional().describe('Edge direction filter - "in" (incoming), "out" (outgoing), "both" (default).')
|
|
2335
2438
|
})
|
|
2336
2439
|
},
|
|
2337
2440
|
async ({ graphId, nodeId, edgeType, direction }) => {
|
|
@@ -2349,7 +2452,7 @@ function registerListNodeConnections(server2, client2) {
|
|
|
2349
2452
|
}
|
|
2350
2453
|
|
|
2351
2454
|
// ../mcp-core/src/tools/reparent.ts
|
|
2352
|
-
import { z as
|
|
2455
|
+
import { z as z49 } from "zod";
|
|
2353
2456
|
function registerReparent(server2, client2) {
|
|
2354
2457
|
server2.registerTool(
|
|
2355
2458
|
"naumu_reparent",
|
|
@@ -2357,11 +2460,11 @@ function registerReparent(server2, client2) {
|
|
|
2357
2460
|
title: "Reparent Node",
|
|
2358
2461
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
2359
2462
|
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"}`.',
|
|
2360
|
-
inputSchema:
|
|
2361
|
-
graphId:
|
|
2362
|
-
nodeId:
|
|
2363
|
-
newParentId:
|
|
2364
|
-
newRelation:
|
|
2463
|
+
inputSchema: z49.object({
|
|
2464
|
+
graphId: z49.string().describe("The graph ID"),
|
|
2465
|
+
nodeId: z49.string().describe("The child node to reparent"),
|
|
2466
|
+
newParentId: z49.string().describe("The new parent node id"),
|
|
2467
|
+
newRelation: z49.string().describe('The new parent edge relation label (e.g. "PART_OF"). Must be valid per the schema for (child.type, relation, parent.type).')
|
|
2365
2468
|
})
|
|
2366
2469
|
},
|
|
2367
2470
|
async ({ graphId, nodeId, newParentId, newRelation }) => {
|
|
@@ -2377,7 +2480,7 @@ function registerReparent(server2, client2) {
|
|
|
2377
2480
|
}
|
|
2378
2481
|
|
|
2379
2482
|
// ../mcp-core/src/tools/batch-reparent.ts
|
|
2380
|
-
import { z as
|
|
2483
|
+
import { z as z50 } from "zod";
|
|
2381
2484
|
function registerBatchReparent(server2, client2) {
|
|
2382
2485
|
server2.registerTool(
|
|
2383
2486
|
"naumu_batch_reparent",
|
|
@@ -2385,11 +2488,11 @@ function registerBatchReparent(server2, client2) {
|
|
|
2385
2488
|
title: "Batch Reparent Nodes",
|
|
2386
2489
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
2387
2490
|
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?}]`.',
|
|
2388
|
-
inputSchema:
|
|
2389
|
-
graphId:
|
|
2390
|
-
newParentId:
|
|
2391
|
-
newRelation:
|
|
2392
|
-
nodeIds:
|
|
2491
|
+
inputSchema: z50.object({
|
|
2492
|
+
graphId: z50.string().describe("The graph ID"),
|
|
2493
|
+
newParentId: z50.string().describe("Parent node id every nodeId in the batch will be parented to"),
|
|
2494
|
+
newRelation: z50.string().describe("Parent edge relation label (must be valid per schema for child.type \u2192 parent.type)"),
|
|
2495
|
+
nodeIds: z50.array(z50.string()).min(1).max(25).describe("1\u201325 child node ids to reparent under `newParentId`")
|
|
2393
2496
|
})
|
|
2394
2497
|
},
|
|
2395
2498
|
async ({ graphId, newParentId, newRelation, nodeIds }) => {
|
|
@@ -2406,7 +2509,7 @@ function registerBatchReparent(server2, client2) {
|
|
|
2406
2509
|
}
|
|
2407
2510
|
|
|
2408
2511
|
// ../mcp-core/src/tools/chatgpt-search.ts
|
|
2409
|
-
import { z as
|
|
2512
|
+
import { z as z51 } from "zod";
|
|
2410
2513
|
|
|
2411
2514
|
// ../mcp-core/src/public-origin.ts
|
|
2412
2515
|
function publicOrigin() {
|
|
@@ -2460,8 +2563,8 @@ function registerChatgptSearch(server2, client2) {
|
|
|
2460
2563
|
title: "Search",
|
|
2461
2564
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2462
2565
|
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.)",
|
|
2463
|
-
inputSchema:
|
|
2464
|
-
query:
|
|
2566
|
+
inputSchema: z51.object({
|
|
2567
|
+
query: z51.string().describe(
|
|
2465
2568
|
"A short contiguous phrase \u2014 an entity name, label, or ID. The text half matches it verbatim as a case-insensitive substring; the semantic half matches meaning."
|
|
2466
2569
|
)
|
|
2467
2570
|
})
|
|
@@ -2495,7 +2598,7 @@ function registerChatgptSearch(server2, client2) {
|
|
|
2495
2598
|
}
|
|
2496
2599
|
|
|
2497
2600
|
// ../mcp-core/src/tools/chatgpt-fetch.ts
|
|
2498
|
-
import { z as
|
|
2601
|
+
import { z as z52 } from "zod";
|
|
2499
2602
|
var NON_ATTRIBUTE_PROPS = /* @__PURE__ */ new Set([
|
|
2500
2603
|
"id",
|
|
2501
2604
|
"label",
|
|
@@ -2589,8 +2692,8 @@ function registerChatgptFetch(server2, client2) {
|
|
|
2589
2692
|
title: "Fetch",
|
|
2590
2693
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2591
2694
|
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.",
|
|
2592
|
-
inputSchema:
|
|
2593
|
-
id:
|
|
2695
|
+
inputSchema: z52.object({
|
|
2696
|
+
id: z52.string().describe("A resource id from a previous `search` result, in the form `<graphId>:<nodeId>`.")
|
|
2594
2697
|
})
|
|
2595
2698
|
},
|
|
2596
2699
|
async ({ id }) => {
|
|
@@ -2633,7 +2736,7 @@ function registerChatgptFetch(server2, client2) {
|
|
|
2633
2736
|
}
|
|
2634
2737
|
|
|
2635
2738
|
// ../mcp-core/src/tools/admission-status.ts
|
|
2636
|
-
import { z as
|
|
2739
|
+
import { z as z53 } from "zod";
|
|
2637
2740
|
function registerAdmissionStatus(server2, client2) {
|
|
2638
2741
|
server2.registerTool(
|
|
2639
2742
|
"naumu_admission_status",
|
|
@@ -2641,8 +2744,8 @@ function registerAdmissionStatus(server2, client2) {
|
|
|
2641
2744
|
title: "Admission Status",
|
|
2642
2745
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2643
2746
|
description: "Show who can auto-join a Naumu space (graph) and who is waiting for approval: the whitelisted emails (people who join the moment they sign in with that email), the auto-join domain wildcards, and the count of pending join requests. When there are pending requests, this also returns the full list (who requested, their email, git-email hint, and message) so you can act on them with naumu_resolve_join_request. Use it during repo init to review or seed access, or whenever the user asks who has access to a space or who is asking to join.",
|
|
2644
|
-
inputSchema:
|
|
2645
|
-
graphId:
|
|
2747
|
+
inputSchema: z53.object({
|
|
2748
|
+
graphId: z53.string().describe("The space (graph) ID to inspect admission for. You must be a member of this space.")
|
|
2646
2749
|
})
|
|
2647
2750
|
},
|
|
2648
2751
|
async ({ graphId }) => {
|
|
@@ -2672,7 +2775,7 @@ function registerAdmissionStatus(server2, client2) {
|
|
|
2672
2775
|
}
|
|
2673
2776
|
|
|
2674
2777
|
// ../mcp-core/src/tools/whitelist-members.ts
|
|
2675
|
-
import { z as
|
|
2778
|
+
import { z as z54 } from "zod";
|
|
2676
2779
|
function registerWhitelistMembers(server2, client2) {
|
|
2677
2780
|
server2.registerTool(
|
|
2678
2781
|
"naumu_whitelist_members",
|
|
@@ -2685,10 +2788,10 @@ function registerWhitelistMembers(server2, client2) {
|
|
|
2685
2788
|
openWorldHint: false
|
|
2686
2789
|
},
|
|
2687
2790
|
description: "Whitelist emails so those people auto-join a Naumu space (graph) the moment they sign in with that email. Use this during repo init: after you scrub git history, present the curated list of collaborators to the user, and get their explicit confirmation, call this with the confirmed emails. It is silent - it sends no invite emails, it just pre-authorizes those addresses. Returns which entries were created and which were skipped (already whitelisted or already members). Set repoInit true when this call is part of the repo init flow.",
|
|
2688
|
-
inputSchema:
|
|
2689
|
-
graphId:
|
|
2690
|
-
emails:
|
|
2691
|
-
repoInit:
|
|
2791
|
+
inputSchema: z54.object({
|
|
2792
|
+
graphId: z54.string().describe("The space (graph) ID to whitelist emails for. You must be a member of this space."),
|
|
2793
|
+
emails: z54.array(z54.string()).min(1).describe("The emails to whitelist. Each becomes an exact-match auto-join entry. Present these to the user and get confirmation before calling."),
|
|
2794
|
+
repoInit: z54.boolean().optional().describe("Set true when this whitelist is being seeded as part of the repo init flow, so onboarding is tracked correctly.")
|
|
2692
2795
|
})
|
|
2693
2796
|
},
|
|
2694
2797
|
async ({ graphId, emails, repoInit }) => {
|
|
@@ -2712,7 +2815,7 @@ function registerWhitelistMembers(server2, client2) {
|
|
|
2712
2815
|
}
|
|
2713
2816
|
|
|
2714
2817
|
// ../mcp-core/src/tools/resolve-admission.ts
|
|
2715
|
-
import { z as
|
|
2818
|
+
import { z as z55 } from "zod";
|
|
2716
2819
|
function registerResolveAdmission(server2, client2) {
|
|
2717
2820
|
server2.registerTool(
|
|
2718
2821
|
"naumu_resolve_admission",
|
|
@@ -2725,9 +2828,9 @@ function registerResolveAdmission(server2, client2) {
|
|
|
2725
2828
|
openWorldHint: false
|
|
2726
2829
|
},
|
|
2727
2830
|
description: "The call a coding agent makes right after connecting when a repo's .naumu references a space the user is not yet a member of. It evaluates whether the user can join and does it: outcome is joined-whitelist or joined-wildcard (the user is now a member - proceed), already-member (nothing to do), request-created (a join request was just filed and is awaiting a member's approval), or request-pending (a request was already open). When the response also carries reason 'seat-limit' on a request-created/request-pending outcome, the user WOULD have auto-joined via a whitelist/domain match but the space is at its seat limit - so their access is pending an admin approving them or upgrading the plan; relay that specific reason honestly, do not just say 'no match'. Pass gitEmailHint from `git config user.email` so a matching whitelist or domain rule can admit them. Relay the outcome to the user honestly: say plainly whether they joined or are waiting for approval - never imply access that is still pending.",
|
|
2728
|
-
inputSchema:
|
|
2729
|
-
graphId:
|
|
2730
|
-
gitEmailHint:
|
|
2831
|
+
inputSchema: z55.object({
|
|
2832
|
+
graphId: z55.string().describe("The space (graph) ID referenced by the repo .naumu file that the user wants to join."),
|
|
2833
|
+
gitEmailHint: z55.string().optional().describe("The email from `git config user.email`, used to match whitelist entries and auto-join domains.")
|
|
2731
2834
|
})
|
|
2732
2835
|
},
|
|
2733
2836
|
async ({ graphId, gitEmailHint }) => {
|
|
@@ -2751,7 +2854,7 @@ function registerResolveAdmission(server2, client2) {
|
|
|
2751
2854
|
}
|
|
2752
2855
|
|
|
2753
2856
|
// ../mcp-core/src/tools/resolve-join-request.ts
|
|
2754
|
-
import { z as
|
|
2857
|
+
import { z as z56 } from "zod";
|
|
2755
2858
|
function registerResolveJoinRequest(server2, client2) {
|
|
2756
2859
|
server2.registerTool(
|
|
2757
2860
|
"naumu_resolve_join_request",
|
|
@@ -2764,10 +2867,10 @@ function registerResolveJoinRequest(server2, client2) {
|
|
|
2764
2867
|
openWorldHint: false
|
|
2765
2868
|
},
|
|
2766
2869
|
description: "For a member resolving a pending join request surfaced by naumu_admission_status. Approve to add the requester to the space as a member, or deny to reject the request. Get the requestId from naumu_admission_status's pending list, and confirm the decision with the user before calling since approving grants access.",
|
|
2767
|
-
inputSchema:
|
|
2768
|
-
graphId:
|
|
2769
|
-
requestId:
|
|
2770
|
-
action:
|
|
2870
|
+
inputSchema: z56.object({
|
|
2871
|
+
graphId: z56.string().describe("The space (graph) ID the request is for. You must be a member of this space."),
|
|
2872
|
+
requestId: z56.string().describe("The pending join request ID, taken from naumu_admission_status."),
|
|
2873
|
+
action: z56.enum(["approve", "deny"]).describe("approve adds the requester as a member; deny rejects the request.")
|
|
2771
2874
|
})
|
|
2772
2875
|
},
|
|
2773
2876
|
async ({ graphId, requestId, action }) => {
|
|
@@ -2823,6 +2926,7 @@ var TOOL_REGISTRARS = {
|
|
|
2823
2926
|
naumu_add_node_type: registerAddNodeType,
|
|
2824
2927
|
naumu_add_connection: registerAddConnection,
|
|
2825
2928
|
naumu_add_attribute: registerAddAttribute,
|
|
2929
|
+
naumu_update_schema_description: registerUpdateSchemaDescription,
|
|
2826
2930
|
naumu_search: registerSearch,
|
|
2827
2931
|
naumu_filter: registerFilter,
|
|
2828
2932
|
naumu_get_node: registerGetNode,
|