@jungjaehoon/mama-server 1.13.0 → 1.14.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/README.md +21 -2
- package/package.json +2 -2
- package/src/server.js +129 -6
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ Any MCP-compatible client can use MAMA with:
|
|
|
75
75
|
npx -y @jungjaehoon/mama-server
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
## Available Tools
|
|
78
|
+
## Available Tools
|
|
79
79
|
|
|
80
80
|
The MCP server exposes 13 tools:
|
|
81
81
|
|
|
@@ -83,7 +83,7 @@ The MCP server exposes 13 tools:
|
|
|
83
83
|
| -------------------------------- | ----------------------------------------------------------------------- |
|
|
84
84
|
| `save_decision` | Save decision with optional scopes and event_date for temporal tracking |
|
|
85
85
|
| `recall_decision` | Recall decision history by topic, scope-filtered via recallMemory v2 |
|
|
86
|
-
| `suggest_decision` | Semantic search
|
|
86
|
+
| `suggest_decision` | Semantic search with scopes, strictness controls, and diagnostics |
|
|
87
87
|
| `list_decisions` | List recent decisions, scope-filterable |
|
|
88
88
|
| `update_outcome` | Update decision outcome (case-insensitive: success/failed/partial) |
|
|
89
89
|
| `search_narrative` | Narrative search with link expansion (depth 0-2) |
|
|
@@ -106,6 +106,25 @@ Decisions connect through relationships. Include patterns in your reasoning:
|
|
|
106
106
|
| `debates` | `debates: decision_xxx` | Alternative view |
|
|
107
107
|
| `synthesizes` | `synthesizes: [id1, id2]` | Merges multiple approaches |
|
|
108
108
|
|
|
109
|
+
### Search Quality Controls
|
|
110
|
+
|
|
111
|
+
`suggest_decision` accepts optional search-quality parameters for agents and operators:
|
|
112
|
+
|
|
113
|
+
| Parameter | Use |
|
|
114
|
+
| ------------------- | ------------------------------------------------------------- |
|
|
115
|
+
| `strictness` | `'recall'`, `'balanced'`, or `'strict'` retrieval mode |
|
|
116
|
+
| `strict` | Shortcut for strict mode |
|
|
117
|
+
| `threshold` | Override the mode's minimum candidate threshold |
|
|
118
|
+
| `disableRecency` | Remove recency boosting when relevance matters more than time |
|
|
119
|
+
| `includeRelated` | Include or suppress graph-expanded related hits |
|
|
120
|
+
| `topicPrefix` | Limit search to a topic namespace |
|
|
121
|
+
| `minLexicalSupport` | Require independent relevance confirmation |
|
|
122
|
+
| `diagnostics` | Return why each result was included or rejected |
|
|
123
|
+
| `scopes` | Limit search to project/channel/user/global memory scopes |
|
|
124
|
+
|
|
125
|
+
Use `strictness: "balanced"` for normal agent work and `strictness: "strict"` when a result will
|
|
126
|
+
drive a code change, user-facing answer, or provenance claim.
|
|
127
|
+
|
|
109
128
|
## Usage Example
|
|
110
129
|
|
|
111
130
|
Once configured, use MAMA through your MCP client:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungjaehoon/mama-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "MAMA MCP Server - Memory-Augmented MCP Assistant for Claude Code & Desktop",
|
|
5
5
|
"main": "src/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=22.13.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@jungjaehoon/mama-core": "^1.
|
|
42
|
+
"@jungjaehoon/mama-core": "^1.6.0",
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.0.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
package/src/server.js
CHANGED
|
@@ -327,6 +327,41 @@ class MAMAServer {
|
|
|
327
327
|
description: "Filter by type. Default: 'all'",
|
|
328
328
|
},
|
|
329
329
|
limit: { type: 'number', description: 'Max results. Default: 10' },
|
|
330
|
+
threshold: {
|
|
331
|
+
type: 'number',
|
|
332
|
+
minimum: 0,
|
|
333
|
+
maximum: 1,
|
|
334
|
+
description: 'Minimum retrieval threshold. Omit for mode default.',
|
|
335
|
+
},
|
|
336
|
+
strict: {
|
|
337
|
+
type: 'boolean',
|
|
338
|
+
description: 'Shortcut for strict search mode.',
|
|
339
|
+
},
|
|
340
|
+
strictness: {
|
|
341
|
+
type: 'string',
|
|
342
|
+
enum: ['recall', 'balanced', 'strict'],
|
|
343
|
+
description: "Search quality mode. Default: 'recall'.",
|
|
344
|
+
},
|
|
345
|
+
disableRecency: {
|
|
346
|
+
type: 'boolean',
|
|
347
|
+
description: 'Disable recency weighting in search.',
|
|
348
|
+
},
|
|
349
|
+
includeRelated: {
|
|
350
|
+
type: 'boolean',
|
|
351
|
+
description: 'Include related graph-expanded results.',
|
|
352
|
+
},
|
|
353
|
+
topicPrefix: {
|
|
354
|
+
type: 'string',
|
|
355
|
+
description: 'Restrict search to topics with this prefix.',
|
|
356
|
+
},
|
|
357
|
+
minLexicalSupport: {
|
|
358
|
+
type: 'boolean',
|
|
359
|
+
description: 'Require lexical/entity/exact-topic confirmation.',
|
|
360
|
+
},
|
|
361
|
+
diagnostics: {
|
|
362
|
+
type: 'boolean',
|
|
363
|
+
description: 'Return retrieval diagnostics for search quality inspection.',
|
|
364
|
+
},
|
|
330
365
|
scopes: {
|
|
331
366
|
type: 'array',
|
|
332
367
|
items: {
|
|
@@ -506,14 +541,39 @@ After failure → save a NEW decision with same topic to create evolution histor
|
|
|
506
541
|
* Handle unified search (decisions + checkpoints)
|
|
507
542
|
*/
|
|
508
543
|
async handleSearch(args) {
|
|
509
|
-
const {
|
|
510
|
-
|
|
511
|
-
|
|
544
|
+
const {
|
|
545
|
+
query,
|
|
546
|
+
type = 'all',
|
|
547
|
+
limit = 10,
|
|
548
|
+
scopes,
|
|
549
|
+
threshold,
|
|
550
|
+
strict,
|
|
551
|
+
strictness,
|
|
552
|
+
disableRecency,
|
|
553
|
+
includeRelated,
|
|
554
|
+
topicPrefix,
|
|
555
|
+
minLexicalSupport,
|
|
556
|
+
diagnostics,
|
|
557
|
+
} = args;
|
|
558
|
+
|
|
559
|
+
// type='checkpoint' without query → load latest checkpoint (resume session).
|
|
560
|
+
// load_checkpoint does not yet honor scopes, so reject scoped checkpoint reads
|
|
561
|
+
// explicitly rather than silently bypass scope isolation.
|
|
512
562
|
if (type === 'checkpoint' && !query) {
|
|
563
|
+
if (Array.isArray(scopes) && scopes.length > 0) {
|
|
564
|
+
return {
|
|
565
|
+
success: false,
|
|
566
|
+
code: 'scoped_checkpoint_unsupported',
|
|
567
|
+
count: 0,
|
|
568
|
+
results: [],
|
|
569
|
+
message: 'Scoped checkpoint reads are not supported yet',
|
|
570
|
+
};
|
|
571
|
+
}
|
|
513
572
|
return await memoryTools.load_checkpoint.handler(args);
|
|
514
573
|
}
|
|
515
574
|
|
|
516
575
|
const results = [];
|
|
576
|
+
let searchDiagnostics;
|
|
517
577
|
|
|
518
578
|
// Search decisions
|
|
519
579
|
if (type === 'all' || type === 'decision') {
|
|
@@ -522,8 +582,52 @@ After failure → save a NEW decision with same topic to create evolution histor
|
|
|
522
582
|
const suggestResult = await mama.suggest(query, {
|
|
523
583
|
limit,
|
|
524
584
|
...(scopes && { scopes }),
|
|
585
|
+
...(threshold !== undefined && { threshold }),
|
|
586
|
+
...(strict !== undefined && { strict }),
|
|
587
|
+
...(strictness !== undefined && { strictness }),
|
|
588
|
+
...(disableRecency !== undefined && { disableRecency }),
|
|
589
|
+
...(includeRelated !== undefined && { includeRelated }),
|
|
590
|
+
...(topicPrefix !== undefined && { topicPrefix }),
|
|
591
|
+
...(minLexicalSupport !== undefined && { minLexicalSupport }),
|
|
592
|
+
...(diagnostics !== undefined && { diagnostics }),
|
|
525
593
|
});
|
|
526
|
-
|
|
594
|
+
// Preserve the failure signal — collapsing a null/invalid suggest
|
|
595
|
+
// response to [] would make callers unable to distinguish "no matches"
|
|
596
|
+
// from "search pipeline failed". Mirror the standalone handler's
|
|
597
|
+
// suggest_returned_null code so behavior stays consistent across
|
|
598
|
+
// transports.
|
|
599
|
+
if (!suggestResult || typeof suggestResult !== 'object') {
|
|
600
|
+
return {
|
|
601
|
+
success: false,
|
|
602
|
+
code: 'suggest_returned_null',
|
|
603
|
+
count: 0,
|
|
604
|
+
results: [],
|
|
605
|
+
message: 'Search failed: suggest() returned no result for query',
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
// Forward explicit { success: false, code, error } failures from
|
|
609
|
+
// mama.suggest() unchanged so callers see the real cause instead of
|
|
610
|
+
// a synthetic empty success.
|
|
611
|
+
if (suggestResult.success === false) {
|
|
612
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
613
|
+
const forwarded = {
|
|
614
|
+
...suggestResult,
|
|
615
|
+
success: false,
|
|
616
|
+
code: suggestResult.code || 'suggest_failed',
|
|
617
|
+
};
|
|
618
|
+
if (!hasOwn.call(forwarded, 'count')) {
|
|
619
|
+
forwarded.count = 0;
|
|
620
|
+
}
|
|
621
|
+
if (!hasOwn.call(forwarded, 'results')) {
|
|
622
|
+
forwarded.results = [];
|
|
623
|
+
}
|
|
624
|
+
if (!hasOwn.call(forwarded, 'message')) {
|
|
625
|
+
forwarded.message = suggestResult.error || 'Search pipeline failed';
|
|
626
|
+
}
|
|
627
|
+
return forwarded;
|
|
628
|
+
}
|
|
629
|
+
searchDiagnostics = suggestResult.diagnostics;
|
|
630
|
+
decisions = Array.isArray(suggestResult.results) ? suggestResult.results : [];
|
|
527
631
|
} else {
|
|
528
632
|
decisions = await mama.list({ limit, ...(scopes && { scopes }) });
|
|
529
633
|
}
|
|
@@ -537,8 +641,25 @@ After failure → save a NEW decision with same topic to create evolution histor
|
|
|
537
641
|
}
|
|
538
642
|
}
|
|
539
643
|
|
|
644
|
+
// mama.listCheckpoints() does not yet honor the scopes filter, so any
|
|
645
|
+
// checkpoint read with scopes provided would silently bypass scope
|
|
646
|
+
// isolation. Reject explicitly when the caller requested scopes — for
|
|
647
|
+
// type='checkpoint' this fails the whole search; for type='all' we let
|
|
648
|
+
// decisions (which DO honor scopes via mama.suggest/list) return alone
|
|
649
|
+
// and skip the checkpoint blocks below.
|
|
650
|
+
const checkpointReadsBlockedByScope = Array.isArray(scopes) && scopes.length > 0;
|
|
651
|
+
if (checkpointReadsBlockedByScope && type === 'checkpoint') {
|
|
652
|
+
return {
|
|
653
|
+
success: false,
|
|
654
|
+
code: 'scoped_checkpoint_unsupported',
|
|
655
|
+
count: 0,
|
|
656
|
+
results: [],
|
|
657
|
+
message: 'Scoped checkpoint reads are not supported yet',
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
540
661
|
// Search checkpoints (with query = search, without = handled above as load)
|
|
541
|
-
if ((type === 'all' || type === 'checkpoint') && query) {
|
|
662
|
+
if ((type === 'all' || type === 'checkpoint') && query && !checkpointReadsBlockedByScope) {
|
|
542
663
|
const checkpoints = await mama.listCheckpoints(limit);
|
|
543
664
|
results.push(
|
|
544
665
|
...checkpoints
|
|
@@ -554,7 +675,7 @@ After failure → save a NEW decision with same topic to create evolution histor
|
|
|
554
675
|
}
|
|
555
676
|
|
|
556
677
|
// type='all' without query — include recent checkpoints
|
|
557
|
-
if (type === 'all' && !query) {
|
|
678
|
+
if (type === 'all' && !query && !checkpointReadsBlockedByScope) {
|
|
558
679
|
const checkpoints = await mama.listCheckpoints(limit);
|
|
559
680
|
results.push(
|
|
560
681
|
...checkpoints.map((c) => ({
|
|
@@ -577,8 +698,10 @@ After failure → save a NEW decision with same topic to create evolution histor
|
|
|
577
698
|
|
|
578
699
|
return {
|
|
579
700
|
success: true,
|
|
701
|
+
...(query ? { query } : {}),
|
|
580
702
|
count: limited.length,
|
|
581
703
|
results: limited,
|
|
704
|
+
...(searchDiagnostics !== undefined ? { diagnostics: searchDiagnostics } : {}),
|
|
582
705
|
};
|
|
583
706
|
}
|
|
584
707
|
|