@exaudeus/memory-mcp 1.9.0 → 1.9.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/dist/index.js CHANGED
@@ -215,7 +215,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
215
215
  // --- Retrieval ---
216
216
  {
217
217
  name: 'brief',
218
- description: 'Session start. Returns user identity, preferences, gotchas overview, stale entries. Call once at the beginning of a conversation. Example: brief(lobe: "android")',
218
+ description: `Session-start briefing for a project. Returns everything stored via learn/gotcha/convention/prefer — preferences, gotchas, stale entries, counts. Call once at conversation start. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}"}`,
219
219
  inputSchema: {
220
220
  type: 'object',
221
221
  properties: {
@@ -226,18 +226,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
226
226
  },
227
227
  {
228
228
  name: 'recall',
229
- description: 'Pre-task lookup. Describe what you are about to do and get relevant knowledge (semantic + keyword search). Example: recall(lobe: "android", context: "writing a Kotlin reducer for the messaging feature")',
229
+ description: `Search your stored memory entries (from learn/gotcha/convention/prefer) architecture notes, conventions, gotchas, decisions — by relevance using semantic + keyword matching. Does NOT search the codebase itself. Use BEFORE starting a task to surface prior knowledge. Usage: {"context": "auth token refresh", "lobe": "${currentLobeNames[0] ?? 'my-project'}"}`,
230
230
  inputSchema: {
231
231
  type: 'object',
232
232
  properties: {
233
233
  lobe: lobeProperty,
234
234
  context: {
235
235
  type: 'string',
236
- description: 'What you are about to do, in natural language.',
236
+ description: 'The topic or area you need knowledge about. Describe in natural language — e.g. "auth token refresh", "how modules communicate", "payment webhook handler".',
237
237
  },
238
238
  maxResults: {
239
239
  type: 'number',
240
- description: 'Max results (default: 10)',
240
+ description: 'Max results (default: 10).',
241
241
  default: 10,
242
242
  },
243
243
  },
@@ -246,14 +246,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
246
246
  },
247
247
  {
248
248
  name: 'gotchas',
249
- description: 'Get stored gotchas for a codebase area. Example: gotchas(lobe: "android", area: "auth")',
249
+ description: `Retrieve stored gotchas (pitfalls, traps, surprising behaviors) for a project. Check BEFORE making changes. Optionally filter by area. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}", "area": "auth"} or {"lobe": "${currentLobeNames[0] ?? 'my-project'}"} for all.`,
250
250
  inputSchema: {
251
251
  type: 'object',
252
252
  properties: {
253
253
  lobe: lobeProperty,
254
254
  area: {
255
255
  type: 'string',
256
- description: 'Optional keyword filter for a specific area (e.g. "auth", "build", "navigation").',
256
+ description: 'Optional keyword filter (e.g. "auth", "build", "navigation").',
257
257
  },
258
258
  },
259
259
  required: [],
@@ -261,14 +261,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
261
261
  },
262
262
  {
263
263
  name: 'conventions',
264
- description: 'Get stored conventions for a codebase area. Example: conventions(lobe: "android", area: "testing")',
264
+ description: `Retrieve stored conventions (coding patterns, naming rules, architectural standards) for a project. Check BEFORE writing code. Optionally filter by area. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}", "area": "testing"} or {"lobe": "${currentLobeNames[0] ?? 'my-project'}"} for all.`,
265
265
  inputSchema: {
266
266
  type: 'object',
267
267
  properties: {
268
268
  lobe: lobeProperty,
269
269
  area: {
270
270
  type: 'string',
271
- description: 'Optional keyword filter for a specific area (e.g. "testing", "naming", "architecture").',
271
+ description: 'Optional keyword filter (e.g. "testing", "naming", "architecture").',
272
272
  },
273
273
  },
274
274
  required: [],
@@ -277,14 +277,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
277
277
  // --- Storage ---
278
278
  {
279
279
  name: 'gotcha',
280
- description: 'Store a gotcha — a pitfall, surprising behavior, or trap. Write naturally; title is auto-extracted. Example: gotcha(lobe: "android", observation: "Gradle cache must be cleaned after Tuist changes or builds silently use stale artifacts")',
280
+ description: `Store a pitfall, surprising behavior, or trap you discovered in the codebase. Persists across sessions, surfaces in brief() and recall(). Write naturally; first sentence becomes the title. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}", "observation": "Gradle cache must be cleaned after Tuist changes or builds silently use stale artifacts"}`,
281
281
  inputSchema: {
282
282
  type: 'object',
283
283
  properties: {
284
284
  lobe: lobeProperty,
285
285
  observation: {
286
286
  type: 'string',
287
- description: 'The gotcha write naturally. First sentence becomes the title.',
287
+ description: 'The gotcha. Write naturally first sentence becomes the title.',
288
288
  },
289
289
  durabilityDecision: {
290
290
  type: 'string',
@@ -298,14 +298,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
298
298
  },
299
299
  {
300
300
  name: 'convention',
301
- description: 'Store a convention — a pattern, rule, or standard the codebase follows. Example: convention(lobe: "android", observation: "All ViewModels use StateFlow for UI state. LiveData is banned.")',
301
+ description: `Store a coding pattern or standard the codebase follows. Persists across sessions, surfaces in brief() and recall(). Write naturally; first sentence becomes the title. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}", "observation": "All ViewModels use StateFlow for UI state. LiveData is banned."}`,
302
302
  inputSchema: {
303
303
  type: 'object',
304
304
  properties: {
305
305
  lobe: lobeProperty,
306
306
  observation: {
307
307
  type: 'string',
308
- description: 'The convention write naturally. First sentence becomes the title.',
308
+ description: 'The convention. Write naturally first sentence becomes the title.',
309
309
  },
310
310
  durabilityDecision: {
311
311
  type: 'string',
@@ -319,14 +319,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
319
319
  },
320
320
  {
321
321
  name: 'learn',
322
- description: 'Store a general observation — architecture decisions, dependency info, or any insight. Example: learn(lobe: "android", observation: "The messaging feature uses MVVM with a FlowCoordinator for navigation state")',
322
+ description: `Store codebase knowledge — architecture, module boundaries, dependencies, or any insight not covered by gotcha/convention. Catch-all for durable project knowledge. Persists across sessions, surfaces in brief() and recall(). Write naturally; first sentence becomes the title. Usage: {"lobe": "${currentLobeNames[0] ?? 'my-project'}", "observation": "Payments module depends on auth for tokens only no other cross-module dependency"}`,
323
323
  inputSchema: {
324
324
  type: 'object',
325
325
  properties: {
326
326
  lobe: lobeProperty,
327
327
  observation: {
328
328
  type: 'string',
329
- description: 'The observation write naturally. First sentence becomes the title.',
329
+ description: 'The observation. Write naturally first sentence becomes the title.',
330
330
  },
331
331
  durabilityDecision: {
332
332
  type: 'string',
@@ -340,17 +340,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
340
340
  },
341
341
  {
342
342
  name: 'prefer',
343
- description: 'Store a user preference or working style rule. Stored with high trust. Example: prefer(rule: "Always suggest the simplest solution first")',
343
+ description: `Store a user preference or working-style rule — when the user corrects you or states how they want things done. Highest trust, surfaced in every brief(). Omit lobe for global. Usage: {"rule": "Never use !! operator"} or {"rule": "Use Anvil for DI", "lobe": "${currentLobeNames[0] ?? 'my-project'}"}`,
344
344
  inputSchema: {
345
345
  type: 'object',
346
346
  properties: {
347
347
  rule: {
348
348
  type: 'string',
349
- description: 'The preference or rule write naturally.',
349
+ description: 'The preference or rule. Write naturally.',
350
350
  },
351
351
  lobe: {
352
352
  ...lobeProperty,
353
- description: `Optional. Lobe to scope this preference to. Omit for global preferences. Available: ${currentLobeNames.join(', ')}`,
353
+ description: `Optional. Scope this preference to a specific lobe. Omit for global. Available: ${currentLobeNames.join(', ')}`,
354
354
  },
355
355
  },
356
356
  required: ['rule'],
@@ -359,17 +359,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
359
359
  // --- Maintenance ---
360
360
  {
361
361
  name: 'fix',
362
- description: 'Fix or delete an entry. With correction: replaces content. Without: deletes. Example: fix(id: "gotcha-3f7a", correction: "updated text") or fix(id: "gotcha-3f7a")',
362
+ description: `Correct or delete a stored memory entry. IDs appear in brief/recall/gotchas/conventions results. Pass correction to update; omit correction to delete. Usage: {"id": "gotcha-3f7a", "correction": "Updated: cache must be cleaned after ANY dependency change"} or {"id": "gotcha-3f7a"} to delete.`,
363
363
  inputSchema: {
364
364
  type: 'object',
365
365
  properties: {
366
366
  id: {
367
367
  type: 'string',
368
- description: 'Entry ID (e.g. gotcha-3f7a, arch-5c9b).',
368
+ description: 'Entry ID (e.g. gotcha-3f7a, conv-5c9b, gen-a2d1, pref-8e4f).',
369
369
  },
370
370
  correction: {
371
371
  type: 'string',
372
- description: 'New text. Omit to delete the entry.',
372
+ description: 'New text to replace the entry content. Omit entirely to delete the entry.',
373
373
  },
374
374
  lobe: {
375
375
  ...lobeProperty,
@@ -384,7 +384,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
384
384
  // but are hidden from tool discovery. Agents use the new v2 tools above.
385
385
  {
386
386
  name: 'memory_bootstrap',
387
- description: 'First-time setup: scan repo structure, README, and build system to seed initial knowledge. Run once per new codebase. If the lobe does not exist yet, provide "root" to auto-add it to memory-config.json and proceed without a manual restart.',
387
+ description: 'First-time setup: scans repo structure, README, and build system to seed initial memory. Run once per project. Provide "root" to auto-create a new lobe.',
388
388
  inputSchema: {
389
389
  type: 'object',
390
390
  properties: {
@@ -1547,20 +1547,50 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1547
1547
  }
1548
1548
  catch (error) {
1549
1549
  const message = error instanceof Error ? error.message : String(error);
1550
- // Provide helpful hints for common Zod validation errors
1550
+ // Provide helpful, human-readable hints for Zod validation errors.
1551
+ // Raw Zod JSON is cryptic for agents — translate to actionable guidance.
1552
+ const lobeNames = configManager.getLobeNames();
1553
+ const lobeList = lobeNames.join(', ');
1554
+ let friendlyMessage = message;
1555
+ // Detect Zod validation errors (they contain path arrays in JSON)
1556
+ if (message.includes('"path"') && message.includes('"message"')) {
1557
+ // Build a friendlyMessage from the Zod issues
1558
+ try {
1559
+ const issues = JSON.parse(message);
1560
+ const fields = issues.map(i => `${i.path.join('.')}: ${i.message}`).join(', ');
1561
+ friendlyMessage = `Invalid arguments — ${fields}`;
1562
+ }
1563
+ catch {
1564
+ // If parsing fails, keep original message
1565
+ }
1566
+ }
1567
+ // Tool-specific hints based on which field failed
1551
1568
  let hint = '';
1552
- if (message.includes('"lobe"') && message.includes('Required')) {
1553
- const lobeNames = configManager.getLobeNames();
1554
- hint = `\n\nHint: lobe is required. Use memory_list_lobes to see available lobes. Available: ${lobeNames.join(', ')}`;
1569
+ const v2ToolHints = {
1570
+ brief: `brief takes an optional lobe. Example: {"lobe": "${lobeNames[0] ?? 'default'}"}. Available lobes: ${lobeList}`,
1571
+ recall: `recall requires "context" (describe what you are working on). Example: {"context": "implementing auth flow", "lobe": "${lobeNames[0] ?? 'default'}"}. Available lobes: ${lobeList}`,
1572
+ gotcha: `gotcha requires "lobe" and "observation". Example: {"lobe": "${lobeNames[0] ?? 'default'}", "observation": "Gradle cache breaks after Tuist changes"}`,
1573
+ convention: `convention requires "lobe" and "observation". Example: {"lobe": "${lobeNames[0] ?? 'default'}", "observation": "All ViewModels use StateFlow"}`,
1574
+ learn: `learn requires "lobe" and "observation". Example: {"lobe": "${lobeNames[0] ?? 'default'}", "observation": "Messaging uses MVVM with FlowCoordinator"}`,
1575
+ prefer: `prefer requires "rule". Example: {"rule": "Always suggest the simplest solution first"}`,
1576
+ fix: `fix requires "id". Optional: "correction" (new text; omit to delete). Example: {"id": "gotcha-3f7a", "correction": "updated text"}`,
1577
+ gotchas: `gotchas takes optional "lobe" and "area". Example: {"lobe": "${lobeNames[0] ?? 'default'}", "area": "auth"}. Available lobes: ${lobeList}`,
1578
+ conventions: `conventions takes optional "lobe" and "area". Example: {"lobe": "${lobeNames[0] ?? 'default'}", "area": "testing"}. Available lobes: ${lobeList}`,
1579
+ };
1580
+ if (name in v2ToolHints) {
1581
+ hint = `\n\nUsage: ${v2ToolHints[name]}`;
1582
+ }
1583
+ else if (friendlyMessage.includes('"lobe"') || friendlyMessage.includes('lobe:')) {
1584
+ hint = `\n\nHint: lobe is required. Available: ${lobeList}`;
1555
1585
  }
1556
1586
  else if (message.includes('"topic"') || message.includes('"entries"')) {
1557
- hint = '\n\nHint: memory_store requires: topic (architecture|conventions|gotchas|recent-work|modules/<name>) and entries (Array<{title, fact}>). Example: entries: [{title: "Build cache", fact: "Must clean build after Tuist changes"}]. Use modules/<name> for custom namespaces.';
1587
+ hint = '\n\nHint: memory_store requires: topic (architecture|conventions|gotchas|general|recent-work|modules/<name>) and entries (Array<{title, fact}>).';
1558
1588
  }
1559
1589
  else if (message.includes('"scope"')) {
1560
- hint = '\n\nHint: memory_query requires: lobe, scope (architecture|conventions|gotchas|recent-work|modules/<name>|* for all)';
1590
+ hint = '\n\nHint: memory_query requires: scope (architecture|conventions|gotchas|*). Example: memory_query(scope: "*")';
1561
1591
  }
1562
1592
  return {
1563
- content: [{ type: 'text', text: `Error: ${message}${hint}` }],
1593
+ content: [{ type: 'text', text: `${friendlyMessage}${hint}` }],
1564
1594
  isError: true,
1565
1595
  };
1566
1596
  }
package/dist/normalize.js CHANGED
@@ -3,17 +3,10 @@
3
3
  // Agents frequently guess wrong param names. This module resolves common aliases
4
4
  // and applies defaults to avoid wasted round-trips from validation errors.
5
5
  // Pure functions — no side effects, no state.
6
- /** Canonical param name aliases — maps guessed names to their correct form */
7
- const PARAM_ALIASES = {
6
+ /** Global param aliases — apply to all tools regardless of name */
7
+ const GLOBAL_ALIASES = {
8
8
  // memory_store aliases
9
9
  refs: 'references',
10
- // memory_query aliases
11
- query: 'filter',
12
- search: 'filter',
13
- keyword: 'filter',
14
- // memory_context aliases
15
- description: 'context',
16
- task: 'context',
17
10
  // tag aliases
18
11
  tag: 'tags',
19
12
  labels: 'tags',
@@ -22,6 +15,28 @@ const PARAM_ALIASES = {
22
15
  workspace: 'lobe',
23
16
  repo: 'lobe',
24
17
  };
18
+ /** Tool-specific param aliases — only apply when the tool name matches.
19
+ * Prevents `query` → `filter` rewriting from breaking `recall(query: "...")`. */
20
+ const TOOL_ALIASES = {
21
+ // Legacy tools: "query" means "filter"
22
+ memory_query: { query: 'filter', search: 'filter', keyword: 'filter' },
23
+ memory_store: { scope: 'topic' },
24
+ // Legacy tools: "description"/"task" mean "context"
25
+ memory_context: { description: 'context', task: 'context' },
26
+ // v2 retrieval tools: "query"/"search"/"description"/"task" mean "context"
27
+ recall: { query: 'context', search: 'context', description: 'context', task: 'context' },
28
+ // v2 storage tools: "content"/"note"/"fact" mean "observation"
29
+ gotcha: { content: 'observation', note: 'observation', fact: 'observation', message: 'observation' },
30
+ convention: { content: 'observation', note: 'observation', fact: 'observation', message: 'observation' },
31
+ learn: { content: 'observation', note: 'observation', fact: 'observation', message: 'observation' },
32
+ // v2 prefer: "preference"/"pref" mean "rule"
33
+ prefer: { preference: 'rule', pref: 'rule' },
34
+ // v2 fix: "text"/"content"/"replacement" mean "correction"
35
+ fix: { text: 'correction', content: 'correction', replacement: 'correction' },
36
+ // v2 retrieval: "filter"/"keyword" mean "area"
37
+ gotchas: { filter: 'area', keyword: 'area', query: 'area', search: 'area' },
38
+ conventions: { filter: 'area', keyword: 'area', query: 'area', search: 'area' },
39
+ };
25
40
  /** Wildcard scope aliases — agents guess many variations instead of "*" */
26
41
  const SCOPE_WILDCARDS = new Set([
27
42
  'all', 'everything', 'any', '*', 'global', 'project', 'repo',
@@ -30,17 +45,22 @@ const SCOPE_WILDCARDS = new Set([
30
45
  /** Normalize args before Zod validation: resolve aliases, default workspace, fix wildcards */
31
46
  export function normalizeArgs(toolName, raw, lobeNames) {
32
47
  const args = { ...(raw ?? {}) };
33
- // 1. Resolve param aliases (move aliased keys to canonical names)
34
- for (const [alias, canonical] of Object.entries(PARAM_ALIASES)) {
48
+ // 1. Resolve global param aliases
49
+ for (const [alias, canonical] of Object.entries(GLOBAL_ALIASES)) {
35
50
  if (alias in args && !(canonical in args)) {
36
51
  args[canonical] = args[alias];
37
52
  delete args[alias];
38
53
  }
39
54
  }
40
- // 2. For memory_store: accept "scope" as alias for "topic"
41
- if (toolName === 'memory_store' && 'scope' in args && !('topic' in args)) {
42
- args['topic'] = args['scope'];
43
- delete args['scope'];
55
+ // 2. Resolve tool-specific param aliases
56
+ const toolSpecific = TOOL_ALIASES[toolName];
57
+ if (toolSpecific) {
58
+ for (const [alias, canonical] of Object.entries(toolSpecific)) {
59
+ if (alias in args && !(canonical in args)) {
60
+ args[canonical] = args[alias];
61
+ delete args[alias];
62
+ }
63
+ }
44
64
  }
45
65
  // 3. Default lobe to the only available one when omitted
46
66
  if (!('lobe' in args) || args['lobe'] === undefined || args['lobe'] === '') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/memory-mcp",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "Codebase memory MCP server - persistent, evolving knowledge for AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",