@danielmarbach/mnemonic-mcp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +86 -3
- package/build/index.js +369 -90
- package/build/index.js.map +1 -1
- package/build/structured-content.d.ts +275 -1310
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js +2 -1
- package/build/structured-content.js.map +1 -1
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -304,9 +304,10 @@ function makeId(title) {
|
|
|
304
304
|
const projectParam = z
|
|
305
305
|
.string()
|
|
306
306
|
.optional()
|
|
307
|
-
.describe("
|
|
308
|
-
"
|
|
309
|
-
"
|
|
307
|
+
.describe("Absolute path to the project working directory (get from `detect_project`). " +
|
|
308
|
+
"Sets project context for routing and search boosting. " +
|
|
309
|
+
"Pass this even when storing with scope='global' — it controls project association, not storage location. " +
|
|
310
|
+
"Omit only for truly cross-project or personal memories.");
|
|
310
311
|
async function resolveProject(cwd) {
|
|
311
312
|
if (!cwd)
|
|
312
313
|
return undefined;
|
|
@@ -727,9 +728,19 @@ const server = new McpServer({
|
|
|
727
728
|
// ── detect_project ────────────────────────────────────────────────────────────
|
|
728
729
|
server.registerTool("detect_project", {
|
|
729
730
|
title: "Detect Project",
|
|
730
|
-
description: "
|
|
731
|
-
"
|
|
732
|
-
"
|
|
731
|
+
description: "Resolve a working directory to a stable project identity derived from its git remote URL.\n\n" +
|
|
732
|
+
"Use this when:\n" +
|
|
733
|
+
"- Starting a session in a project — call this first to get the `cwd` value needed by all other project-scoped tools\n" +
|
|
734
|
+
"- You need to confirm which project a directory belongs to\n\n" +
|
|
735
|
+
"Do not use this when:\n" +
|
|
736
|
+
"- You already know the project context from an earlier call in this session\n\n" +
|
|
737
|
+
"Returns: project id, name, source (git-remote/folder), and any identity override. " +
|
|
738
|
+
"Follow up with `project_memory_summary` or `recall` to orient on what is already known.",
|
|
739
|
+
annotations: {
|
|
740
|
+
readOnlyHint: true,
|
|
741
|
+
idempotentHint: true,
|
|
742
|
+
openWorldHint: false,
|
|
743
|
+
},
|
|
733
744
|
outputSchema: ProjectIdentityResultSchema,
|
|
734
745
|
inputSchema: z.object({
|
|
735
746
|
cwd: z.string().describe("Absolute path to the working directory"),
|
|
@@ -768,7 +779,18 @@ server.registerTool("detect_project", {
|
|
|
768
779
|
// ── get_project_identity ───────────────────────────────────────────────────────
|
|
769
780
|
server.registerTool("get_project_identity", {
|
|
770
781
|
title: "Get Project Identity",
|
|
771
|
-
description: "Show the effective project identity for a working directory, including any configured remote override
|
|
782
|
+
description: "Show the effective project identity for a working directory, including any configured remote override.\n\n" +
|
|
783
|
+
"Use this when:\n" +
|
|
784
|
+
"- You need to check whether a fork is using `upstream` vs `origin` for identity\n" +
|
|
785
|
+
"- Debugging project-scoping issues (notes not appearing for the expected project)\n\n" +
|
|
786
|
+
"Do not use this when:\n" +
|
|
787
|
+
"- You just need the project id — use `detect_project` instead\n\n" +
|
|
788
|
+
"Returns: effective project id/name, default project, and any identity override. Read-only.",
|
|
789
|
+
annotations: {
|
|
790
|
+
readOnlyHint: true,
|
|
791
|
+
idempotentHint: true,
|
|
792
|
+
openWorldHint: false,
|
|
793
|
+
},
|
|
772
794
|
inputSchema: z.object({
|
|
773
795
|
cwd: z.string().describe("Absolute path to the project working directory"),
|
|
774
796
|
}),
|
|
@@ -804,7 +826,19 @@ server.registerTool("get_project_identity", {
|
|
|
804
826
|
// ── set_project_identity ───────────────────────────────────────────────────────
|
|
805
827
|
server.registerTool("set_project_identity", {
|
|
806
828
|
title: "Set Project Identity",
|
|
807
|
-
description: "Override which git remote defines project identity for a repo
|
|
829
|
+
description: "Override which git remote defines project identity for a repo.\n\n" +
|
|
830
|
+
"Use this when:\n" +
|
|
831
|
+
"- Working in a fork and notes should associate with the upstream project, not the fork's origin\n\n" +
|
|
832
|
+
"Do not use this when:\n" +
|
|
833
|
+
"- The default `origin` remote already points to the canonical repo\n\n" +
|
|
834
|
+
"Side effects: writes to main vault config.json, git commits and pushes. " +
|
|
835
|
+
"Changes how all future memory operations resolve this project's identity.",
|
|
836
|
+
annotations: {
|
|
837
|
+
readOnlyHint: false,
|
|
838
|
+
destructiveHint: false,
|
|
839
|
+
idempotentHint: true,
|
|
840
|
+
openWorldHint: false,
|
|
841
|
+
},
|
|
808
842
|
inputSchema: z.object({
|
|
809
843
|
cwd: z.string().describe("Absolute path to the project working directory"),
|
|
810
844
|
remoteName: z.string().min(1).describe("Git remote name to use as the canonical project identity, such as `upstream`")
|
|
@@ -876,7 +910,16 @@ server.registerTool("set_project_identity", {
|
|
|
876
910
|
// ── list_migrations ───────────────────────────────────────────────────────────
|
|
877
911
|
server.registerTool("list_migrations", {
|
|
878
912
|
title: "List Migrations",
|
|
879
|
-
description: "List available migrations and show which ones are pending for
|
|
913
|
+
description: "List available schema migrations and show which ones are pending for each vault.\n\n" +
|
|
914
|
+
"Use this when:\n" +
|
|
915
|
+
"- Checking if vaults need migration after a mnemonic upgrade\n" +
|
|
916
|
+
"- Before running `execute_migration` to see what would change\n\n" +
|
|
917
|
+
"Returns: vault schema versions, pending migration count, and available migration descriptions. Read-only.",
|
|
918
|
+
annotations: {
|
|
919
|
+
readOnlyHint: true,
|
|
920
|
+
idempotentHint: true,
|
|
921
|
+
openWorldHint: false,
|
|
922
|
+
},
|
|
880
923
|
inputSchema: z.object({}),
|
|
881
924
|
outputSchema: MigrationListResultSchema,
|
|
882
925
|
}, async () => {
|
|
@@ -919,12 +962,24 @@ server.registerTool("list_migrations", {
|
|
|
919
962
|
// ── execute_migration ─────────────────────────────────────────────────────────
|
|
920
963
|
server.registerTool("execute_migration", {
|
|
921
964
|
title: "Execute Migration",
|
|
922
|
-
description: "Execute a named migration on vault notes"
|
|
965
|
+
description: "Execute a named schema migration on vault notes.\n\n" +
|
|
966
|
+
"Use this when:\n" +
|
|
967
|
+
"- `list_migrations` shows pending migrations that need to be applied\n\n" +
|
|
968
|
+
"Do not use this when:\n" +
|
|
969
|
+
"- You haven't run `list_migrations` first to check what's pending\n\n" +
|
|
970
|
+
"Always run with `dryRun: true` first to preview changes, then re-run with `dryRun: false` to apply. " +
|
|
971
|
+
"Side effects: modifies note files, git commits and pushes per vault.",
|
|
972
|
+
annotations: {
|
|
973
|
+
readOnlyHint: false,
|
|
974
|
+
destructiveHint: false,
|
|
975
|
+
idempotentHint: true,
|
|
976
|
+
openWorldHint: false,
|
|
977
|
+
},
|
|
923
978
|
inputSchema: z.object({
|
|
924
|
-
migrationName: z.string().describe("Name of the migration to execute"),
|
|
925
|
-
dryRun: z.boolean().default(true).describe("If true, show what would change without actually modifying notes"),
|
|
979
|
+
migrationName: z.string().describe("Name of the migration to execute (get names from `list_migrations`)"),
|
|
980
|
+
dryRun: z.boolean().default(true).describe("If true, show what would change without actually modifying notes. Always try dry-run first."),
|
|
926
981
|
backup: z.boolean().default(true).describe("If true, warn about backing up before real migration"),
|
|
927
|
-
cwd: projectParam.optional().describe("Optional: limit to project vault
|
|
982
|
+
cwd: projectParam.optional().describe("Optional: limit migration to a specific project vault"),
|
|
928
983
|
}),
|
|
929
984
|
outputSchema: MigrationExecuteResultSchema,
|
|
930
985
|
}, async ({ migrationName, dryRun, backup, cwd }) => {
|
|
@@ -992,23 +1047,40 @@ server.registerTool("execute_migration", {
|
|
|
992
1047
|
// ── remember ──────────────────────────────────────────────────────────────────
|
|
993
1048
|
server.registerTool("remember", {
|
|
994
1049
|
title: "Remember",
|
|
995
|
-
description: "Store a new memory
|
|
996
|
-
"
|
|
997
|
-
"
|
|
1050
|
+
description: "Store a new memory as a markdown note with embeddings for semantic search.\n\n" +
|
|
1051
|
+
"Use this when:\n" +
|
|
1052
|
+
"- A decision, preference, bug fix, or useful context should survive beyond this session\n" +
|
|
1053
|
+
"- You learn something the user had to explain that isn't obvious from the codebase\n\n" +
|
|
1054
|
+
"Do not use this when:\n" +
|
|
1055
|
+
"- A note on this topic already exists — use `recall` to check first, then `update` instead\n" +
|
|
1056
|
+
"- Multiple related notes have accumulated — use `consolidate` to merge them\n\n" +
|
|
1057
|
+
"After storing: consider whether this note relates to any note you recalled earlier in this session. " +
|
|
1058
|
+
"If so, call `relate` to link them while you still have context.\n\n" +
|
|
1059
|
+
"Side effects: writes note + embedding files, git commits. Returns persistence status.",
|
|
1060
|
+
annotations: {
|
|
1061
|
+
readOnlyHint: false,
|
|
1062
|
+
destructiveHint: false,
|
|
1063
|
+
idempotentHint: false,
|
|
1064
|
+
openWorldHint: true,
|
|
1065
|
+
},
|
|
998
1066
|
inputSchema: z.object({
|
|
999
|
-
title: z.string().describe("Short
|
|
1000
|
-
content: z.string().describe("
|
|
1001
|
-
|
|
1067
|
+
title: z.string().describe("Short, searchable title — e.g. 'JWT RS256 migration rationale', not 'auth stuff'"),
|
|
1068
|
+
content: z.string().describe("Markdown content. Write summary-first: put the main fact, decision, or outcome in the opening sentences, " +
|
|
1069
|
+
"then follow with supporting detail. Embeddings weight early content more heavily."),
|
|
1070
|
+
tags: z.array(z.string()).optional().default([]).describe("Searchable tags — use terms you'd search for later"),
|
|
1002
1071
|
lifecycle: z
|
|
1003
1072
|
.enum(NOTE_LIFECYCLES)
|
|
1004
1073
|
.optional()
|
|
1005
|
-
.describe("
|
|
1006
|
-
|
|
1074
|
+
.describe("Choose 'temporary' for working-state notes (plans, WIP, investigation state) that lose value once work completes. " +
|
|
1075
|
+
"Choose 'permanent' for durable knowledge (decisions, constraints, lessons, bug causes). Default: permanent."),
|
|
1076
|
+
summary: z.string().optional().describe("Git commit message summary (not stored in note). Imperative mood, 50-72 chars, explain 'why' not 'what'. " +
|
|
1077
|
+
"Example: 'Add JWT RS256 migration decision for distributed auth'"),
|
|
1007
1078
|
cwd: projectParam,
|
|
1008
1079
|
scope: z
|
|
1009
1080
|
.enum(WRITE_SCOPES)
|
|
1010
1081
|
.optional()
|
|
1011
|
-
.describe("Where to store
|
|
1082
|
+
.describe("Where to store: 'project' = shared project vault (.mnemonic/), " +
|
|
1083
|
+
"'global' = private main vault. When omitted, uses the project's saved policy or defaults to 'project'."),
|
|
1012
1084
|
}),
|
|
1013
1085
|
outputSchema: RememberResultSchema,
|
|
1014
1086
|
}, async ({ title, content, tags, lifecycle, summary, cwd, scope }) => {
|
|
@@ -1086,12 +1158,23 @@ server.registerTool("remember", {
|
|
|
1086
1158
|
// ── set_project_memory_policy ─────────────────────────────────────────────────
|
|
1087
1159
|
server.registerTool("set_project_memory_policy", {
|
|
1088
1160
|
title: "Set Project Memory Policy",
|
|
1089
|
-
description: "
|
|
1090
|
-
"
|
|
1161
|
+
description: "Set the default write scope and consolidation mode for a project.\n\n" +
|
|
1162
|
+
"Use this when:\n" +
|
|
1163
|
+
"- The user wants all memories for a project to go to a specific vault by default\n" +
|
|
1164
|
+
"- The current 'ask' behavior is inconvenient and should be automated\n\n" +
|
|
1165
|
+
"Do not use this when:\n" +
|
|
1166
|
+
"- You just need to store a single memory with a specific scope — pass `scope` to `remember` instead\n\n" +
|
|
1167
|
+
"Side effects: writes to main vault config.json, git commits and pushes.",
|
|
1168
|
+
annotations: {
|
|
1169
|
+
readOnlyHint: false,
|
|
1170
|
+
destructiveHint: false,
|
|
1171
|
+
idempotentHint: true,
|
|
1172
|
+
openWorldHint: false,
|
|
1173
|
+
},
|
|
1091
1174
|
inputSchema: z.object({
|
|
1092
1175
|
cwd: z.string().describe("Absolute path to the project working directory"),
|
|
1093
|
-
defaultScope: z.enum(PROJECT_POLICY_SCOPES).describe("Default storage
|
|
1094
|
-
consolidationMode: z.enum(CONSOLIDATION_MODES).optional().describe("Default consolidation mode: 'supersedes' preserves history (default), 'delete' removes sources"),
|
|
1176
|
+
defaultScope: z.enum(PROJECT_POLICY_SCOPES).describe("Default storage: 'project' = shared .mnemonic/ vault, 'global' = private main vault, 'ask' = prompt each time"),
|
|
1177
|
+
consolidationMode: z.enum(CONSOLIDATION_MODES).optional().describe("Default consolidation mode: 'supersedes' preserves history (default), 'delete' removes sources immediately"),
|
|
1095
1178
|
}),
|
|
1096
1179
|
outputSchema: PolicyResultSchema,
|
|
1097
1180
|
}, async ({ cwd, defaultScope, consolidationMode }) => {
|
|
@@ -1133,7 +1216,16 @@ server.registerTool("set_project_memory_policy", {
|
|
|
1133
1216
|
// ── get_project_memory_policy ─────────────────────────────────────────────────
|
|
1134
1217
|
server.registerTool("get_project_memory_policy", {
|
|
1135
1218
|
title: "Get Project Memory Policy",
|
|
1136
|
-
description: "Show the
|
|
1219
|
+
description: "Show the saved default write scope and consolidation mode for a project.\n\n" +
|
|
1220
|
+
"Use this when:\n" +
|
|
1221
|
+
"- Checking what the current storage default is before changing it\n" +
|
|
1222
|
+
"- Debugging why memories are landing in an unexpected vault\n\n" +
|
|
1223
|
+
"Returns: saved policy or 'no policy set' with default behavior explanation. Read-only.",
|
|
1224
|
+
annotations: {
|
|
1225
|
+
readOnlyHint: true,
|
|
1226
|
+
idempotentHint: true,
|
|
1227
|
+
openWorldHint: false,
|
|
1228
|
+
},
|
|
1137
1229
|
inputSchema: z.object({
|
|
1138
1230
|
cwd: z.string().describe("Absolute path to the project working directory"),
|
|
1139
1231
|
}),
|
|
@@ -1175,24 +1267,37 @@ server.registerTool("get_project_memory_policy", {
|
|
|
1175
1267
|
// ── recall ────────────────────────────────────────────────────────────────────
|
|
1176
1268
|
server.registerTool("recall", {
|
|
1177
1269
|
title: "Recall",
|
|
1178
|
-
description: "Semantic search over memories
|
|
1179
|
-
"
|
|
1180
|
-
"
|
|
1270
|
+
description: "Semantic search over stored memories using embeddings.\n\n" +
|
|
1271
|
+
"Use this when:\n" +
|
|
1272
|
+
"- Starting a session — search broadly (e.g. 'project overview architecture decisions') to orient on prior context\n" +
|
|
1273
|
+
"- Before calling `remember` — check if a note on this topic already exists (use `update` instead if so)\n" +
|
|
1274
|
+
"- The user mentions something unfamiliar — you may already have context stored\n" +
|
|
1275
|
+
"- You need to find a specific decision, bug fix, or piece of tribal knowledge\n\n" +
|
|
1276
|
+
"Do not use this when:\n" +
|
|
1277
|
+
"- You know the exact note id — use `get` instead\n" +
|
|
1278
|
+
"- You want a structural overview — use `project_memory_summary` instead\n\n" +
|
|
1279
|
+
"When `cwd` is provided, searches both project vault and main vault with project notes boosted +0.15. " +
|
|
1181
1280
|
"Without `cwd`, searches only the main vault. " +
|
|
1182
|
-
"
|
|
1281
|
+
"Stale embeddings are backfilled on demand before searching.\n\n" +
|
|
1282
|
+
"After results: check the `related:` line on each result — call `get` with those ids to pull in linked context.",
|
|
1283
|
+
annotations: {
|
|
1284
|
+
readOnlyHint: true,
|
|
1285
|
+
idempotentHint: true,
|
|
1286
|
+
openWorldHint: true,
|
|
1287
|
+
},
|
|
1183
1288
|
inputSchema: z.object({
|
|
1184
|
-
query: z.string().describe("
|
|
1289
|
+
query: z.string().describe("Natural language search query — describe what you're looking for"),
|
|
1185
1290
|
cwd: projectParam,
|
|
1186
1291
|
limit: z.number().int().min(1).max(20).optional().default(DEFAULT_RECALL_LIMIT),
|
|
1187
1292
|
minSimilarity: z.number().min(0).max(1).optional().default(DEFAULT_MIN_SIMILARITY),
|
|
1188
|
-
tags: z.array(z.string()).optional().describe("
|
|
1293
|
+
tags: z.array(z.string()).optional().describe("Filter results to notes with all of these tags"),
|
|
1189
1294
|
scope: z
|
|
1190
1295
|
.enum(["project", "global", "all"])
|
|
1191
1296
|
.optional()
|
|
1192
1297
|
.default("all")
|
|
1193
|
-
.describe("'project' = only project memories, " +
|
|
1298
|
+
.describe("'project' = only this project's memories, " +
|
|
1194
1299
|
"'global' = only unscoped memories, " +
|
|
1195
|
-
"'all' = project
|
|
1300
|
+
"'all' = both, with project notes boosted (default)"),
|
|
1196
1301
|
}),
|
|
1197
1302
|
outputSchema: RecallResultSchema,
|
|
1198
1303
|
}, async ({ query, cwd, limit, minSimilarity, tags, scope }) => {
|
|
@@ -1279,17 +1384,32 @@ server.registerTool("recall", {
|
|
|
1279
1384
|
// ── update ────────────────────────────────────────────────────────────────────
|
|
1280
1385
|
server.registerTool("update", {
|
|
1281
1386
|
title: "Update Memory",
|
|
1282
|
-
description: "Update the content, title, or
|
|
1387
|
+
description: "Update the content, title, tags, or lifecycle of an existing memory by id.\n\n" +
|
|
1388
|
+
"Use this when:\n" +
|
|
1389
|
+
"- A stored memory is outdated — a decision was revisited, a dependency upgraded, a pattern changed\n" +
|
|
1390
|
+
"- You `recall` something and notice it's stale or partially wrong\n" +
|
|
1391
|
+
"- You want to add detail to an existing note rather than creating a duplicate\n\n" +
|
|
1392
|
+
"Do not use this when:\n" +
|
|
1393
|
+
"- No note exists on this topic yet — use `remember` instead\n" +
|
|
1394
|
+
"- Multiple notes need merging — use `consolidate` instead\n\n" +
|
|
1395
|
+
"Side effects: re-embeds the note, git commits. Only provided fields are changed; omitted fields keep their current values. " +
|
|
1396
|
+
"Project metadata is not changed by this tool.",
|
|
1397
|
+
annotations: {
|
|
1398
|
+
readOnlyHint: false,
|
|
1399
|
+
destructiveHint: false,
|
|
1400
|
+
idempotentHint: false,
|
|
1401
|
+
openWorldHint: true,
|
|
1402
|
+
},
|
|
1283
1403
|
inputSchema: z.object({
|
|
1284
|
-
id: z.string().describe("Memory id to update"),
|
|
1285
|
-
content: z.string().optional(),
|
|
1286
|
-
title: z.string().optional(),
|
|
1287
|
-
tags: z.array(z.string()).optional(),
|
|
1404
|
+
id: z.string().describe("Memory id to update (get this from `recall` or `list`)"),
|
|
1405
|
+
content: z.string().optional().describe("New content (replaces existing). Write summary-first."),
|
|
1406
|
+
title: z.string().optional().describe("New title"),
|
|
1407
|
+
tags: z.array(z.string()).optional().describe("New tags (replaces existing tag list)"),
|
|
1288
1408
|
lifecycle: z
|
|
1289
1409
|
.enum(NOTE_LIFECYCLES)
|
|
1290
1410
|
.optional()
|
|
1291
|
-
.describe("
|
|
1292
|
-
summary: z.string().optional().describe("
|
|
1411
|
+
.describe("Change lifecycle. Preserve the existing value unless you're intentionally switching it."),
|
|
1412
|
+
summary: z.string().optional().describe("Git commit message summary explaining what changed and why. Not stored in the note."),
|
|
1293
1413
|
cwd: projectParam,
|
|
1294
1414
|
}),
|
|
1295
1415
|
outputSchema: UpdateResultSchema,
|
|
@@ -1365,9 +1485,23 @@ server.registerTool("update", {
|
|
|
1365
1485
|
// ── forget ────────────────────────────────────────────────────────────────────
|
|
1366
1486
|
server.registerTool("forget", {
|
|
1367
1487
|
title: "Forget",
|
|
1368
|
-
description: "
|
|
1488
|
+
description: "Permanently delete a memory and its embedding. Cleans up dangling relationship references in other notes.\n\n" +
|
|
1489
|
+
"Use this when:\n" +
|
|
1490
|
+
"- A memory is fully superseded and keeping it would cause confusion\n" +
|
|
1491
|
+
"- The content is factually wrong and should not appear in search results\n\n" +
|
|
1492
|
+
"Do not use this when:\n" +
|
|
1493
|
+
"- The memory is just outdated — use `update` to correct it instead\n" +
|
|
1494
|
+
"- The memory has historical value — use `relate` with type 'supersedes' to mark it replaced\n" +
|
|
1495
|
+
"- You want to merge notes — use `consolidate` instead\n\n" +
|
|
1496
|
+
"Side effects: deletes note + embedding files, removes relationship references from other notes, git commits per vault.",
|
|
1497
|
+
annotations: {
|
|
1498
|
+
readOnlyHint: false,
|
|
1499
|
+
destructiveHint: true,
|
|
1500
|
+
idempotentHint: true,
|
|
1501
|
+
openWorldHint: false,
|
|
1502
|
+
},
|
|
1369
1503
|
inputSchema: z.object({
|
|
1370
|
-
id: z.string().describe("Memory id to delete"),
|
|
1504
|
+
id: z.string().describe("Memory id to permanently delete"),
|
|
1371
1505
|
cwd: projectParam,
|
|
1372
1506
|
}),
|
|
1373
1507
|
outputSchema: ForgetResultSchema,
|
|
@@ -1408,8 +1542,19 @@ server.registerTool("forget", {
|
|
|
1408
1542
|
// ── get ───────────────────────────────────────────────────────────────────────
|
|
1409
1543
|
server.registerTool("get", {
|
|
1410
1544
|
title: "Get Memory",
|
|
1411
|
-
description: "Fetch one or more notes by exact id. Returns full
|
|
1412
|
-
"
|
|
1545
|
+
description: "Fetch one or more notes by exact id. Returns full content, metadata, tags, lifecycle, and relationships.\n\n" +
|
|
1546
|
+
"Use this when:\n" +
|
|
1547
|
+
"- You know the exact note id (from `recall` results, `related:` links, or `list` output)\n" +
|
|
1548
|
+
"- You need full content that `recall` or `list` only summarized\n\n" +
|
|
1549
|
+
"Do not use this when:\n" +
|
|
1550
|
+
"- You're searching by topic — use `recall` instead\n" +
|
|
1551
|
+
"- You only need location metadata — use `where_is_memory` instead\n\n" +
|
|
1552
|
+
"Read-only. Supports batch fetching of multiple ids in one call.",
|
|
1553
|
+
annotations: {
|
|
1554
|
+
readOnlyHint: true,
|
|
1555
|
+
idempotentHint: true,
|
|
1556
|
+
openWorldHint: false,
|
|
1557
|
+
},
|
|
1413
1558
|
inputSchema: z.object({
|
|
1414
1559
|
ids: z.array(z.string()).min(1).describe("One or more memory ids to fetch"),
|
|
1415
1560
|
cwd: projectParam,
|
|
@@ -1463,9 +1608,18 @@ server.registerTool("get", {
|
|
|
1463
1608
|
// ── where_is_memory ───────────────────────────────────────────────────────────
|
|
1464
1609
|
server.registerTool("where_is_memory", {
|
|
1465
1610
|
title: "Where Is Memory",
|
|
1466
|
-
description: "Show a memory's project association and
|
|
1467
|
-
"
|
|
1468
|
-
"
|
|
1611
|
+
description: "Show a memory's project association and storage location without fetching full content.\n\n" +
|
|
1612
|
+
"Use this when:\n" +
|
|
1613
|
+
"- Checking whether a note lives in the project vault or main vault before a `move_memory`\n" +
|
|
1614
|
+
"- Debugging storage routing — a note landed in the wrong vault\n\n" +
|
|
1615
|
+
"Do not use this when:\n" +
|
|
1616
|
+
"- You need the note's content — use `get` instead\n\n" +
|
|
1617
|
+
"Read-only. Returns: title, project association, vault, last updated, relationship count.",
|
|
1618
|
+
annotations: {
|
|
1619
|
+
readOnlyHint: true,
|
|
1620
|
+
idempotentHint: true,
|
|
1621
|
+
openWorldHint: false,
|
|
1622
|
+
},
|
|
1469
1623
|
inputSchema: z.object({
|
|
1470
1624
|
id: z.string().describe("Memory id to locate"),
|
|
1471
1625
|
cwd: projectParam,
|
|
@@ -1503,7 +1657,22 @@ server.registerTool("where_is_memory", {
|
|
|
1503
1657
|
// ── list ──────────────────────────────────────────────────────────────────────
|
|
1504
1658
|
server.registerTool("list", {
|
|
1505
1659
|
title: "List Memories",
|
|
1506
|
-
description: "List stored memories
|
|
1660
|
+
description: "List stored memories with filtering by scope, vault, and tags.\n\n" +
|
|
1661
|
+
"Use this when:\n" +
|
|
1662
|
+
"- Browsing all memories for a project or globally\n" +
|
|
1663
|
+
"- Filtering by tags to find related notes\n" +
|
|
1664
|
+
"- Checking what exists before deciding whether to `remember` or `update`\n\n" +
|
|
1665
|
+
"Do not use this when:\n" +
|
|
1666
|
+
"- Searching by topic — use `recall` for semantic search\n" +
|
|
1667
|
+
"- You want a high-level project overview — use `project_memory_summary` instead\n" +
|
|
1668
|
+
"- You want recent changes — use `recent_memories` instead\n\n" +
|
|
1669
|
+
"Read-only. Returns id, title, tags, lifecycle for each note. " +
|
|
1670
|
+
"Use optional flags to include previews, storage location, timestamps, or relationships.",
|
|
1671
|
+
annotations: {
|
|
1672
|
+
readOnlyHint: true,
|
|
1673
|
+
idempotentHint: true,
|
|
1674
|
+
openWorldHint: false,
|
|
1675
|
+
},
|
|
1507
1676
|
inputSchema: z.object({
|
|
1508
1677
|
cwd: projectParam,
|
|
1509
1678
|
scope: z
|
|
@@ -1515,12 +1684,12 @@ server.registerTool("list", {
|
|
|
1515
1684
|
.enum(["project-vault", "main-vault", "any"])
|
|
1516
1685
|
.optional()
|
|
1517
1686
|
.default("any")
|
|
1518
|
-
.describe("Filter by actual storage location instead of project association"),
|
|
1519
|
-
tags: z.array(z.string()).optional().describe("
|
|
1520
|
-
includeRelations: z.boolean().optional().default(false).describe("Include related memory ids
|
|
1521
|
-
includePreview: z.boolean().optional().default(false).describe("Include a short content preview for each
|
|
1522
|
-
includeStorage: z.boolean().optional().default(false).describe("
|
|
1523
|
-
includeUpdated: z.boolean().optional().default(false).describe("Include
|
|
1687
|
+
.describe("Filter by actual storage location (vault) instead of project association"),
|
|
1688
|
+
tags: z.array(z.string()).optional().describe("Filter to notes matching all of these tags"),
|
|
1689
|
+
includeRelations: z.boolean().optional().default(false).describe("Include related memory ids and relationship types"),
|
|
1690
|
+
includePreview: z.boolean().optional().default(false).describe("Include a short content preview for each note"),
|
|
1691
|
+
includeStorage: z.boolean().optional().default(false).describe("Show which vault each note is stored in"),
|
|
1692
|
+
includeUpdated: z.boolean().optional().default(false).describe("Include last-updated timestamp for each note"),
|
|
1524
1693
|
}),
|
|
1525
1694
|
outputSchema: ListResultSchema,
|
|
1526
1695
|
}, async ({ cwd, scope, storedIn, tags, includeRelations, includePreview, includeStorage, includeUpdated }) => {
|
|
@@ -1569,7 +1738,20 @@ server.registerTool("list", {
|
|
|
1569
1738
|
// ── recent_memories ───────────────────────────────────────────────────────────
|
|
1570
1739
|
server.registerTool("recent_memories", {
|
|
1571
1740
|
title: "Recent Memories",
|
|
1572
|
-
description: "Show the most recently updated memories
|
|
1741
|
+
description: "Show the most recently updated memories, sorted by last modification time.\n\n" +
|
|
1742
|
+
"Use this when:\n" +
|
|
1743
|
+
"- Orienting at session start to see what was last worked on\n" +
|
|
1744
|
+
"- Catching stale notes that may need updating\n" +
|
|
1745
|
+
"- Reviewing what changed since last session\n\n" +
|
|
1746
|
+
"Do not use this when:\n" +
|
|
1747
|
+
"- Searching by topic — use `recall` instead\n" +
|
|
1748
|
+
"- You want a thematic overview — use `project_memory_summary` instead\n\n" +
|
|
1749
|
+
"Read-only. Includes content previews and storage location by default.",
|
|
1750
|
+
annotations: {
|
|
1751
|
+
readOnlyHint: true,
|
|
1752
|
+
idempotentHint: true,
|
|
1753
|
+
openWorldHint: false,
|
|
1754
|
+
},
|
|
1573
1755
|
inputSchema: z.object({
|
|
1574
1756
|
cwd: projectParam,
|
|
1575
1757
|
scope: z.enum(["project", "global", "all"]).optional().default("all"),
|
|
@@ -1621,7 +1803,20 @@ server.registerTool("recent_memories", {
|
|
|
1621
1803
|
// ── memory_graph ──────────────────────────────────────────────────────────────
|
|
1622
1804
|
server.registerTool("memory_graph", {
|
|
1623
1805
|
title: "Memory Graph",
|
|
1624
|
-
description: "Show memory relationships
|
|
1806
|
+
description: "Show memory relationships as a compact adjacency list.\n\n" +
|
|
1807
|
+
"Use this when:\n" +
|
|
1808
|
+
"- Spotting dense clusters of related notes — these are consolidation candidates\n" +
|
|
1809
|
+
"- Understanding how notes connect before a `consolidate` operation\n" +
|
|
1810
|
+
"- Visualizing the knowledge structure for a project\n\n" +
|
|
1811
|
+
"Do not use this when:\n" +
|
|
1812
|
+
"- You need note content — use `get` instead\n" +
|
|
1813
|
+
"- You want a thematic overview — use `project_memory_summary` instead\n\n" +
|
|
1814
|
+
"Read-only. Only shows notes that have at least one relationship.",
|
|
1815
|
+
annotations: {
|
|
1816
|
+
readOnlyHint: true,
|
|
1817
|
+
idempotentHint: true,
|
|
1818
|
+
openWorldHint: false,
|
|
1819
|
+
},
|
|
1625
1820
|
inputSchema: z.object({
|
|
1626
1821
|
cwd: projectParam,
|
|
1627
1822
|
scope: z.enum(["project", "global", "all"]).optional().default("all"),
|
|
@@ -1682,7 +1877,19 @@ server.registerTool("memory_graph", {
|
|
|
1682
1877
|
// ── project_memory_summary ────────────────────────────────────────────────────
|
|
1683
1878
|
server.registerTool("project_memory_summary", {
|
|
1684
1879
|
title: "Project Memory Summary",
|
|
1685
|
-
description: "
|
|
1880
|
+
description: "Get a rich overview of everything mnemonic knows about a project: policy, themes, note counts, storage layout, and recent changes.\n\n" +
|
|
1881
|
+
"Use this when:\n" +
|
|
1882
|
+
"- Starting a session — call this after `detect_project` to orient on prior context before doing work\n" +
|
|
1883
|
+
"- Checking how many notes exist and how they're distributed across vaults\n\n" +
|
|
1884
|
+
"Do not use this when:\n" +
|
|
1885
|
+
"- Searching for a specific topic — use `recall` instead\n" +
|
|
1886
|
+
"- You want just the recent notes — use `recent_memories` instead\n\n" +
|
|
1887
|
+
"Read-only. Groups notes by theme (decisions, architecture, tooling, bugs, etc.) with examples.",
|
|
1888
|
+
annotations: {
|
|
1889
|
+
readOnlyHint: true,
|
|
1890
|
+
idempotentHint: true,
|
|
1891
|
+
openWorldHint: false,
|
|
1892
|
+
},
|
|
1686
1893
|
inputSchema: z.object({
|
|
1687
1894
|
cwd: z.string().describe("Absolute path to the project working directory"),
|
|
1688
1895
|
maxPerTheme: z.number().int().min(1).max(5).optional().default(3),
|
|
@@ -1763,14 +1970,24 @@ server.registerTool("project_memory_summary", {
|
|
|
1763
1970
|
// ── sync ──────────────────────────────────────────────────────────────────────
|
|
1764
1971
|
server.registerTool("sync", {
|
|
1765
1972
|
title: "Sync",
|
|
1766
|
-
description: "
|
|
1767
|
-
"
|
|
1768
|
-
"
|
|
1769
|
-
"
|
|
1770
|
-
"
|
|
1973
|
+
description: "Pull remote changes, push local commits, and backfill missing embeddings.\n\n" +
|
|
1974
|
+
"Use this when:\n" +
|
|
1975
|
+
"- Starting a session and you want the latest notes from collaborators\n" +
|
|
1976
|
+
"- Embedding model changed — use `force: true` to rebuild all embeddings\n" +
|
|
1977
|
+
"- Notes were added by direct file edit or git pull and need embedding backfill\n\n" +
|
|
1978
|
+
"Do not use this when:\n" +
|
|
1979
|
+
"- You just called `remember` or `update` — those tools handle their own embedding and commit\n\n" +
|
|
1980
|
+
"Always syncs the main vault. When `cwd` is provided, also syncs the project vault (.mnemonic/). " +
|
|
1981
|
+
"Side effects: git fetch/pull/push, Ollama embedding API calls.",
|
|
1982
|
+
annotations: {
|
|
1983
|
+
readOnlyHint: false,
|
|
1984
|
+
destructiveHint: false,
|
|
1985
|
+
idempotentHint: true,
|
|
1986
|
+
openWorldHint: true,
|
|
1987
|
+
},
|
|
1771
1988
|
inputSchema: z.object({
|
|
1772
1989
|
cwd: projectParam,
|
|
1773
|
-
force: z.boolean().optional().default(false).describe("Rebuild all embeddings even if current model already
|
|
1990
|
+
force: z.boolean().optional().default(false).describe("Rebuild all embeddings even if the current model already generated them"),
|
|
1774
1991
|
}),
|
|
1775
1992
|
outputSchema: SyncResultSchema,
|
|
1776
1993
|
}, async ({ cwd, force }) => {
|
|
@@ -1833,10 +2050,25 @@ server.registerTool("sync", {
|
|
|
1833
2050
|
// ── move_memory ───────────────────────────────────────────────────────────────
|
|
1834
2051
|
server.registerTool("move_memory", {
|
|
1835
2052
|
title: "Move Memory",
|
|
1836
|
-
description: "Move a memory between the main vault and the
|
|
2053
|
+
description: "Move a memory between the main vault and the project vault without changing its id.\n\n" +
|
|
2054
|
+
"Use this when:\n" +
|
|
2055
|
+
"- A note was stored in the wrong vault and needs to be relocated\n" +
|
|
2056
|
+
"- A private note (main vault) should become shared (project vault) or vice versa\n\n" +
|
|
2057
|
+
"Do not use this when:\n" +
|
|
2058
|
+
"- You want to change the note's content — use `update` instead\n" +
|
|
2059
|
+
"- You want to delete the note — use `forget` instead\n\n" +
|
|
2060
|
+
"When moving to project vault, project metadata is rewritten from `cwd`. " +
|
|
2061
|
+
"When moving to main vault, existing project association is preserved. " +
|
|
2062
|
+
"Side effects: writes to target vault, deletes from source vault, git commits per vault.",
|
|
2063
|
+
annotations: {
|
|
2064
|
+
readOnlyHint: false,
|
|
2065
|
+
destructiveHint: false,
|
|
2066
|
+
idempotentHint: true,
|
|
2067
|
+
openWorldHint: false,
|
|
2068
|
+
},
|
|
1837
2069
|
inputSchema: z.object({
|
|
1838
2070
|
id: z.string().describe("Memory id to move"),
|
|
1839
|
-
target: z.enum(["main-vault", "project-vault"]).describe("Destination storage
|
|
2071
|
+
target: z.enum(["main-vault", "project-vault"]).describe("Destination: 'main-vault' for private storage, 'project-vault' for shared project storage"),
|
|
1840
2072
|
cwd: projectParam,
|
|
1841
2073
|
}),
|
|
1842
2074
|
outputSchema: MoveResultSchema,
|
|
@@ -1926,15 +2158,30 @@ const RELATIONSHIP_TYPES = [
|
|
|
1926
2158
|
];
|
|
1927
2159
|
server.registerTool("relate", {
|
|
1928
2160
|
title: "Relate Memories",
|
|
1929
|
-
description: "Create a typed relationship between two memories
|
|
1930
|
-
"
|
|
1931
|
-
"
|
|
1932
|
-
"
|
|
2161
|
+
description: "Create a typed, bidirectional relationship between two memories.\n\n" +
|
|
2162
|
+
"Use this when:\n" +
|
|
2163
|
+
"- You just stored a note that connects to something you recalled earlier in this session\n" +
|
|
2164
|
+
"- Two notes cover the same topic, one explains the other, or one replaces the other\n\n" +
|
|
2165
|
+
"Do not use this when:\n" +
|
|
2166
|
+
"- The connection is weak or forced — don't over-link, one or two meaningful edges per note is better\n" +
|
|
2167
|
+
"- You want to remove a relationship — use `unrelate` instead\n\n" +
|
|
2168
|
+
"Relationship types:\n" +
|
|
2169
|
+
"- 'related-to': same topic or area\n" +
|
|
2170
|
+
"- 'explains': clarifies why the other note's decision was made\n" +
|
|
2171
|
+
"- 'example-of': concrete instance of a general pattern\n" +
|
|
2172
|
+
"- 'supersedes': replaces a previous decision or approach\n\n" +
|
|
2173
|
+
"Side effects: modifies both notes' frontmatter, git commits per vault. Notes may be in different vaults.",
|
|
2174
|
+
annotations: {
|
|
2175
|
+
readOnlyHint: false,
|
|
2176
|
+
destructiveHint: false,
|
|
2177
|
+
idempotentHint: true,
|
|
2178
|
+
openWorldHint: false,
|
|
2179
|
+
},
|
|
1933
2180
|
inputSchema: z.object({
|
|
1934
|
-
fromId: z.string().describe("
|
|
1935
|
-
toId: z.string().describe("
|
|
1936
|
-
type: z.enum(RELATIONSHIP_TYPES).default("related-to"),
|
|
1937
|
-
bidirectional: z.boolean().optional().default(true),
|
|
2181
|
+
fromId: z.string().describe("Source memory id"),
|
|
2182
|
+
toId: z.string().describe("Target memory id"),
|
|
2183
|
+
type: z.enum(RELATIONSHIP_TYPES).default("related-to").describe("Relationship type: 'related-to' (same topic), 'explains' (clarifies why), 'example-of' (instance of pattern), 'supersedes' (replaces)"),
|
|
2184
|
+
bidirectional: z.boolean().optional().default(true).describe("Add relationship in both directions (default: true)"),
|
|
1938
2185
|
cwd: projectParam,
|
|
1939
2186
|
}),
|
|
1940
2187
|
outputSchema: RelateResultSchema,
|
|
@@ -2007,11 +2254,22 @@ server.registerTool("relate", {
|
|
|
2007
2254
|
// ── unrelate ──────────────────────────────────────────────────────────────────
|
|
2008
2255
|
server.registerTool("unrelate", {
|
|
2009
2256
|
title: "Remove Relationship",
|
|
2010
|
-
description: "Remove
|
|
2257
|
+
description: "Remove an existing relationship between two memories.\n\n" +
|
|
2258
|
+
"Use this when:\n" +
|
|
2259
|
+
"- A relationship was created incorrectly or is no longer relevant\n\n" +
|
|
2260
|
+
"Do not use this when:\n" +
|
|
2261
|
+
"- You want to delete the note entirely — use `forget` instead\n\n" +
|
|
2262
|
+
"Side effects: modifies both notes' frontmatter (when bidirectional), git commits per vault.",
|
|
2263
|
+
annotations: {
|
|
2264
|
+
readOnlyHint: false,
|
|
2265
|
+
destructiveHint: true,
|
|
2266
|
+
idempotentHint: true,
|
|
2267
|
+
openWorldHint: false,
|
|
2268
|
+
},
|
|
2011
2269
|
inputSchema: z.object({
|
|
2012
|
-
fromId: z.string().describe("
|
|
2013
|
-
toId: z.string().describe("
|
|
2014
|
-
bidirectional: z.boolean().optional().default(true),
|
|
2270
|
+
fromId: z.string().describe("Source memory id"),
|
|
2271
|
+
toId: z.string().describe("Target memory id"),
|
|
2272
|
+
bidirectional: z.boolean().optional().default(true).describe("Remove relationship in both directions (default: true)"),
|
|
2015
2273
|
cwd: projectParam,
|
|
2016
2274
|
}),
|
|
2017
2275
|
outputSchema: RelateResultSchema,
|
|
@@ -2074,9 +2332,28 @@ server.registerTool("unrelate", {
|
|
|
2074
2332
|
// ── consolidate ───────────────────────────────────────────────────────────────
|
|
2075
2333
|
server.registerTool("consolidate", {
|
|
2076
2334
|
title: "Consolidate Memories",
|
|
2077
|
-
description: "Analyze memories for
|
|
2078
|
-
"
|
|
2079
|
-
"
|
|
2335
|
+
description: "Analyze memories for duplicates and clusters, or merge multiple notes into one.\n\n" +
|
|
2336
|
+
"Use this when:\n" +
|
|
2337
|
+
"- 3+ notes on the same topic have accumulated from incremental captures\n" +
|
|
2338
|
+
"- A feature or bug arc is complete and related notes can be synthesized\n" +
|
|
2339
|
+
"- `memory_graph` shows a dense cluster of tightly-related nodes\n\n" +
|
|
2340
|
+
"Do not use this when:\n" +
|
|
2341
|
+
"- Only one note needs updating — use `update` instead\n" +
|
|
2342
|
+
"- Notes are on different topics — don't force a merge\n\n" +
|
|
2343
|
+
"Workflow: start with 'dry-run' or 'suggest-merges' to see recommendations, then 'execute-merge' with a mergePlan. " +
|
|
2344
|
+
"Optionally follow up with 'prune-superseded' to clean up old notes.\n\n" +
|
|
2345
|
+
"Modes: 'supersedes' preserves source notes with a supersedes relationship (default). " +
|
|
2346
|
+
"'delete' removes source notes immediately. " +
|
|
2347
|
+
"When all sources are temporary, prefer 'delete' so scaffolding is cleaned up.\n\n" +
|
|
2348
|
+
"Read-only strategies: detect-duplicates, find-clusters, suggest-merges, dry-run. " +
|
|
2349
|
+
"Mutating strategies: execute-merge, prune-superseded. " +
|
|
2350
|
+
"Gathers notes from both main and project vaults.",
|
|
2351
|
+
annotations: {
|
|
2352
|
+
readOnlyHint: false,
|
|
2353
|
+
destructiveHint: true,
|
|
2354
|
+
idempotentHint: false,
|
|
2355
|
+
openWorldHint: true,
|
|
2356
|
+
},
|
|
2080
2357
|
inputSchema: z.object({
|
|
2081
2358
|
cwd: projectParam,
|
|
2082
2359
|
strategy: z
|
|
@@ -2088,29 +2365,31 @@ server.registerTool("consolidate", {
|
|
|
2088
2365
|
"prune-superseded",
|
|
2089
2366
|
"dry-run",
|
|
2090
2367
|
])
|
|
2091
|
-
.describe("
|
|
2368
|
+
.describe("What to do: 'dry-run' = full analysis without changes, 'detect-duplicates' = find similar pairs, " +
|
|
2369
|
+
"'find-clusters' = group by theme and relationships, 'suggest-merges' = actionable merge recommendations, " +
|
|
2370
|
+
"'execute-merge' = perform a merge (requires mergePlan), 'prune-superseded' = delete notes marked as superseded"),
|
|
2092
2371
|
mode: z
|
|
2093
2372
|
.enum(CONSOLIDATION_MODES)
|
|
2094
2373
|
.optional()
|
|
2095
|
-
.describe("Override the project's default
|
|
2374
|
+
.describe("Override the project's default: 'supersedes' preserves history, 'delete' removes sources immediately"),
|
|
2096
2375
|
threshold: z
|
|
2097
2376
|
.number()
|
|
2098
2377
|
.min(0)
|
|
2099
2378
|
.max(1)
|
|
2100
2379
|
.optional()
|
|
2101
2380
|
.default(0.85)
|
|
2102
|
-
.describe("
|
|
2381
|
+
.describe("Cosine similarity threshold for duplicate detection (0.85 default)"),
|
|
2103
2382
|
mergePlan: z
|
|
2104
2383
|
.object({
|
|
2105
|
-
sourceIds: z.array(z.string()).min(2).describe("
|
|
2384
|
+
sourceIds: z.array(z.string()).min(2).describe("Ids of notes to merge into one consolidated note"),
|
|
2106
2385
|
targetTitle: z.string().describe("Title for the consolidated note"),
|
|
2107
|
-
content: z.string().optional().describe("Custom body for the consolidated note
|
|
2108
|
-
description: z.string().optional().describe("
|
|
2109
|
-
summary: z.string().optional().describe("
|
|
2386
|
+
content: z.string().optional().describe("Custom body for the consolidated note — distill durable knowledge rather than dumping all source content verbatim"),
|
|
2387
|
+
description: z.string().optional().describe("Context explaining the consolidation rationale (stored in the note)"),
|
|
2388
|
+
summary: z.string().optional().describe("Git commit message summary (not stored in note)"),
|
|
2110
2389
|
tags: z.array(z.string()).optional().describe("Tags for the consolidated note (defaults to union of source tags)"),
|
|
2111
2390
|
})
|
|
2112
2391
|
.optional()
|
|
2113
|
-
.describe("Required for execute-merge strategy"),
|
|
2392
|
+
.describe("Required for 'execute-merge' strategy. Get sourceIds from 'suggest-merges' output."),
|
|
2114
2393
|
}),
|
|
2115
2394
|
outputSchema: ConsolidateResultSchema,
|
|
2116
2395
|
}, async ({ cwd, strategy, mode, threshold, mergePlan }) => {
|