@jungjaehoon/mama-server 1.12.0 → 1.13.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 CHANGED
@@ -75,23 +75,25 @@ Any MCP-compatible client can use MAMA with:
75
75
  npx -y @jungjaehoon/mama-server
76
76
  ```
77
77
 
78
- ## Available Tools (v1.9)
79
-
80
- The MCP server exposes 11 tools:
81
-
82
- | Tool | Description |
83
- | ------------------------- | ----------------------------------------------------------------------- |
84
- | `save_decision` | Save decision with optional scopes and event_date for temporal tracking |
85
- | `recall_decision` | Recall decision history by topic, scope-filtered via recallMemory v2 |
86
- | `suggest_decision` | Semantic search for relevant past decisions, scope-aware |
87
- | `list_decisions` | List recent decisions, scope-filterable |
88
- | `update_outcome` | Update decision outcome (case-insensitive: success/failed/partial) |
89
- | `search_narrative` | Narrative search with link expansion (depth 0-2) |
90
- | `ingest_conversation` | Ingest conversation messages into memory with optional LLM extraction |
91
- | `save_checkpoint` | Save session checkpoint for later resumption |
92
- | `load_checkpoint` | Resume previous session |
93
- | `generate_quality_report` | Quality metrics and observability report |
94
- | `get_restart_metrics` | Restart success rate and latency monitoring |
78
+ ## Available Tools (v1.13)
79
+
80
+ The MCP server exposes 13 tools:
81
+
82
+ | Tool | Description |
83
+ | -------------------------------- | ----------------------------------------------------------------------- |
84
+ | `save_decision` | Save decision with optional scopes and event_date for temporal tracking |
85
+ | `recall_decision` | Recall decision history by topic, scope-filtered via recallMemory v2 |
86
+ | `suggest_decision` | Semantic search for relevant past decisions, scope-aware |
87
+ | `list_decisions` | List recent decisions, scope-filterable |
88
+ | `update_outcome` | Update decision outcome (case-insensitive: success/failed/partial) |
89
+ | `search_narrative` | Narrative search with link expansion (depth 0-2) |
90
+ | `ingest_conversation` | Ingest conversation messages into memory with optional LLM extraction |
91
+ | `save_checkpoint` | Save session checkpoint for later resumption |
92
+ | `load_checkpoint` | Resume previous session |
93
+ | `generate_quality_report` | Quality metrics and observability report |
94
+ | `get_restart_metrics` | Restart success rate and latency monitoring |
95
+ | `search_decisions_and_contracts` | Decision + contract lookup for tooling and hook pipelines |
96
+ | `case_timeline_range` | Read bounded case timeline windows for case-first workflows |
95
97
 
96
98
  ### Edge Types
97
99
 
@@ -261,4 +263,4 @@ MAMA was inspired by [mem0](https://github.com/mem0ai/mem0) (Apache 2.0). While
261
263
  ---
262
264
 
263
265
  **Author:** SpineLift Team
264
- **Version:** 1.9.0
266
+ **Version:** 1.13.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-server",
3
- "version": "1.12.0",
3
+ "version": "1.13.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,10 +39,11 @@
39
39
  "node": ">=22.13.0"
40
40
  },
41
41
  "dependencies": {
42
- "@jungjaehoon/mama-core": "^1.4.0",
42
+ "@jungjaehoon/mama-core": "^1.5.0",
43
43
  "@modelcontextprotocol/sdk": "^1.0.1"
44
44
  },
45
45
  "devDependencies": {
46
+ "better-sqlite3": "^12.8.0",
46
47
  "vitest": "^1.0.0"
47
48
  },
48
49
  "files": [
package/src/server.js CHANGED
@@ -26,7 +26,6 @@ const {
26
26
  CallToolRequestSchema,
27
27
  ListToolsRequestSchema,
28
28
  } = require('@modelcontextprotocol/sdk/types.js');
29
- const path = require('path');
30
29
 
31
30
  // Import all MAMA tools from src/tools/ — single source of truth for tool definitions
32
31
  const { createMemoryTools } = require('./tools/index.js');
@@ -35,8 +34,6 @@ const mama = require('@jungjaehoon/mama-core/mama-api');
35
34
 
36
35
  // Import core modules from mama-core
37
36
  const { initDB } = require('@jungjaehoon/mama-core/db-manager');
38
- const { generateEmbedding } = require('@jungjaehoon/mama-core/embeddings');
39
- const { vectorSearch } = require('@jungjaehoon/mama-core/memory-store');
40
37
  const embeddingServer = require('@jungjaehoon/mama-core/embedding-server');
41
38
  const http = require('http');
42
39
 
@@ -369,12 +366,18 @@ After failure → save a NEW decision with same topic to create evolution histor
369
366
  required: ['id', 'outcome'],
370
367
  },
371
368
  },
372
- // 4. SEARCH_DECISIONS_AND_CONTRACTS — PreToolUse hook RPC
369
+ // 4. SEARCH_DECISIONS_AND_CONTRACTS — PreToolUse hook RPC (defined in src/tools/)
373
370
  {
374
- name: 'search_decisions_and_contracts',
375
- description: 'Search decisions and contracts for PreToolUse hook injection.',
371
+ name: memoryTools.search_decisions_and_contracts.name,
372
+ description: memoryTools.search_decisions_and_contracts.description,
376
373
  inputSchema: memoryTools.search_decisions_and_contracts.inputSchema,
377
374
  },
375
+ // 5. CASE_TIMELINE_RANGE — Phase 3 case timeline RPC (defined in src/tools/)
376
+ {
377
+ name: memoryTools.case_timeline_range.name,
378
+ description: memoryTools.case_timeline_range.description,
379
+ inputSchema: memoryTools.case_timeline_range.inputSchema,
380
+ },
378
381
  ];
379
382
 
380
383
  this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
@@ -579,74 +582,6 @@ After failure → save a NEW decision with same topic to create evolution histor
579
582
  };
580
583
  }
581
584
 
582
- /**
583
- * Handle PreToolUse search for decisions + contracts
584
- */
585
- async handleSearchDecisionsAndContracts(args = {}) {
586
- const {
587
- query = '',
588
- filePath = '',
589
- toolName = '',
590
- decisionLimit = 5,
591
- contractLimit = 3,
592
- similarityThreshold = 0.7,
593
- } = args;
594
-
595
- await initDB();
596
-
597
- let decisionResults = [];
598
- let contractResults = [];
599
-
600
- // Decision search
601
- if (decisionLimit > 0 && query) {
602
- try {
603
- const queryEmbedding = await generateEmbedding(query);
604
- const results = await vectorSearch(queryEmbedding, decisionLimit, similarityThreshold);
605
- if (Array.isArray(results)) {
606
- decisionResults = results.slice(0, decisionLimit);
607
- }
608
- } catch (err) {
609
- console.error('[MAMA MCP] Decision search failed:', err.message);
610
- }
611
- }
612
-
613
- // Contract search (file-specific)
614
- const contractTools = ['Edit', 'Write', 'apply_patch'];
615
- const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.rs', '.java'];
616
- const ext = filePath ? path.extname(filePath) : '';
617
-
618
- if (
619
- contractLimit > 0 &&
620
- filePath &&
621
- contractTools.includes(toolName) &&
622
- codeExtensions.includes(ext)
623
- ) {
624
- const basename = path.basename(filePath, ext);
625
- const keywords = basename.split(/[-_]/).filter(Boolean);
626
- const contractQuery = `contract api ${keywords.join(' ')}`.trim();
627
-
628
- if (contractQuery) {
629
- try {
630
- const contractEmbedding = await generateEmbedding(contractQuery);
631
- const contractMatches = await vectorSearch(contractEmbedding, 10, similarityThreshold);
632
- if (Array.isArray(contractMatches)) {
633
- contractResults = contractMatches
634
- .filter((r) => r.topic && r.topic.startsWith('contract_'))
635
- .slice(0, contractLimit);
636
- }
637
- } catch (err) {
638
- console.error('[MAMA MCP] Contract search failed:', err.message);
639
- }
640
- }
641
- }
642
-
643
- return {
644
- success: true,
645
- decisionResults,
646
- contractResults,
647
- };
648
- }
649
-
650
585
  /**
651
586
  * Handle update (decision outcome)
652
587
  * Story 3.1: Case-insensitive outcome support
@@ -0,0 +1,77 @@
1
+ /**
2
+ * MCP Tool: case_timeline_range
3
+ *
4
+ * Thin MCP wrapper around mama-core caseTimelineRange().
5
+ */
6
+
7
+ const { caseTimelineRange } = require('@jungjaehoon/mama-core');
8
+ const { initDB, getAdapter } = require('@jungjaehoon/mama-core/db-manager');
9
+
10
+ let adapterOverrideForTest = null;
11
+
12
+ const caseTimelineRangeTool = {
13
+ name: 'case_timeline_range',
14
+ description:
15
+ 'Return a bounded, chronological timeline for a case. Includes decision, event, observation, and artifact memberships resolved through canonical case chains.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ case_id: {
20
+ type: 'string',
21
+ description: 'Case UUID to read. Merged cases resolve through their canonical case chain.',
22
+ },
23
+ from: {
24
+ oneOf: [{ type: 'string' }, { type: 'number' }],
25
+ description: 'Optional inclusive lower date bound. ISO 8601 string or epoch milliseconds.',
26
+ },
27
+ to: {
28
+ oneOf: [{ type: 'string' }, { type: 'number' }],
29
+ description: 'Optional inclusive upper date bound. ISO 8601 string or epoch milliseconds.',
30
+ },
31
+ order: {
32
+ type: 'string',
33
+ enum: ['asc', 'desc'],
34
+ description: "Timeline order. Default: 'asc'.",
35
+ },
36
+ limit: {
37
+ type: 'number',
38
+ minimum: 0,
39
+ maximum: 500,
40
+ description: 'Maximum items to return. Default: 100. Maximum: 500.',
41
+ },
42
+ include_connector_enrichments: {
43
+ type: 'boolean',
44
+ description: 'Include connector event snapshots for observations/artifacts when available.',
45
+ },
46
+ },
47
+ required: ['case_id'],
48
+ },
49
+
50
+ async handler(args) {
51
+ const adapter = await getCaseTimelineRangeAdapter();
52
+ return caseTimelineRange(adapter, args || {});
53
+ },
54
+ };
55
+
56
+ async function getCaseTimelineRangeAdapter() {
57
+ if (adapterOverrideForTest) {
58
+ return adapterOverrideForTest;
59
+ }
60
+
61
+ await initDB();
62
+ return getAdapter();
63
+ }
64
+
65
+ function setCaseTimelineRangeAdapterForTest(adapter) {
66
+ adapterOverrideForTest = adapter;
67
+ }
68
+
69
+ function resetCaseTimelineRangeAdapterForTest() {
70
+ adapterOverrideForTest = null;
71
+ }
72
+
73
+ module.exports = {
74
+ caseTimelineRangeTool,
75
+ setCaseTimelineRangeAdapterForTest,
76
+ resetCaseTimelineRangeAdapterForTest,
77
+ };
@@ -16,6 +16,7 @@
16
16
  * - generate_quality_report: Generate coverage and quality metrics report ✅
17
17
  * - get_restart_metrics: Get restart success rate and latency metrics ✅
18
18
  * - ingest_conversation: Ingest conversation messages into memory ✅
19
+ * - case_timeline_range: Read bounded case timeline ranges ✅
19
20
  *
20
21
  * @module tools
21
22
  */
@@ -29,6 +30,8 @@ const { saveCheckpointTool, loadCheckpointTool } = require('./checkpoint-tools.j
29
30
  const { searchNarrativeTool } = require('./search-narrative.js');
30
31
  const { generateQualityReportTool, getRestartMetricsTool } = require('./quality-metrics-tools.js');
31
32
  const { ingestConversationTool } = require('./ingest-conversation.js');
33
+ const { searchDecisionsAndContractsTool } = require('./search-decisions-and-contracts.js');
34
+ const { caseTimelineRangeTool } = require('./case-timeline-range.js');
32
35
 
33
36
  /**
34
37
  * Create all MAMA memory tools
@@ -50,6 +53,8 @@ function createMemoryTools() {
50
53
  generate_quality_report: generateQualityReportTool,
51
54
  get_restart_metrics: getRestartMetricsTool,
52
55
  ingest_conversation: ingestConversationTool,
56
+ search_decisions_and_contracts: searchDecisionsAndContractsTool,
57
+ case_timeline_range: caseTimelineRangeTool,
53
58
  };
54
59
  }
55
60
 
@@ -67,4 +72,6 @@ module.exports = {
67
72
  generateQualityReportTool,
68
73
  getRestartMetricsTool,
69
74
  ingestConversationTool,
75
+ searchDecisionsAndContractsTool,
76
+ caseTimelineRangeTool,
70
77
  };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * MCP Tool: search_decisions_and_contracts
3
+ *
4
+ * PreToolUse hook RPC — searches decisions and contract-specific memories
5
+ * for file-aware context injection before Edit/Write/apply_patch tool calls.
6
+ *
7
+ * @module search-decisions-and-contracts
8
+ */
9
+
10
+ const path = require('path');
11
+ const { initDB } = require('@jungjaehoon/mama-core/db-manager');
12
+ const { generateEmbedding } = require('@jungjaehoon/mama-core/embeddings');
13
+ const { vectorSearch } = require('@jungjaehoon/mama-core/memory-store');
14
+
15
+ /**
16
+ * search_decisions_and_contracts tool definition
17
+ */
18
+ const searchDecisionsAndContractsTool = {
19
+ name: 'search_decisions_and_contracts',
20
+ description: 'Search decisions and contracts for PreToolUse hook injection.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: {
24
+ query: { type: 'string', description: 'Search query for decisions.' },
25
+ filePath: { type: 'string', description: 'File path context.' },
26
+ toolName: { type: 'string', description: 'Tool name (Edit/Write/apply_patch).' },
27
+ decisionLimit: { type: 'number', description: 'Max decisions (default: 5).' },
28
+ contractLimit: { type: 'number', description: 'Max contracts (default: 3).' },
29
+ similarityThreshold: {
30
+ type: 'number',
31
+ description: 'Similarity threshold (default: 0.7).',
32
+ },
33
+ },
34
+ },
35
+
36
+ async handler(args = {}) {
37
+ try {
38
+ const {
39
+ query = '',
40
+ filePath = '',
41
+ toolName = '',
42
+ decisionLimit = 5,
43
+ contractLimit = 3,
44
+ similarityThreshold = 0.7,
45
+ } = args;
46
+
47
+ await initDB();
48
+
49
+ let decisionResults = [];
50
+ let contractResults = [];
51
+
52
+ // Decision search
53
+ if (decisionLimit > 0 && query) {
54
+ try {
55
+ const queryEmbedding = await generateEmbedding(query);
56
+ const results = await vectorSearch(queryEmbedding, decisionLimit, similarityThreshold);
57
+ if (Array.isArray(results)) {
58
+ decisionResults = results.slice(0, decisionLimit);
59
+ }
60
+ } catch (err) {
61
+ console.error('[MAMA MCP] Decision search failed:', err.message);
62
+ }
63
+ }
64
+
65
+ // Contract search (file-specific)
66
+ const contractTools = ['Edit', 'Write', 'apply_patch'];
67
+ const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.rs', '.java'];
68
+ const ext = filePath ? path.extname(filePath) : '';
69
+
70
+ if (
71
+ contractLimit > 0 &&
72
+ filePath &&
73
+ contractTools.includes(toolName) &&
74
+ codeExtensions.includes(ext)
75
+ ) {
76
+ const basename = path.basename(filePath, ext);
77
+ const keywords = basename.split(/[-_]/).filter(Boolean);
78
+ const contractQuery = `contract api ${keywords.join(' ')}`.trim();
79
+
80
+ if (contractQuery) {
81
+ try {
82
+ const contractEmbedding = await generateEmbedding(contractQuery);
83
+ const contractMatches = await vectorSearch(contractEmbedding, 10, similarityThreshold);
84
+ if (Array.isArray(contractMatches)) {
85
+ contractResults = contractMatches
86
+ .filter((r) => r.topic && r.topic.startsWith('contract_'))
87
+ .slice(0, contractLimit);
88
+ }
89
+ } catch (err) {
90
+ console.error('[MAMA MCP] Contract search failed:', err.message);
91
+ }
92
+ }
93
+ }
94
+
95
+ return {
96
+ success: true,
97
+ decisionResults,
98
+ contractResults,
99
+ };
100
+ } catch (err) {
101
+ return {
102
+ success: false,
103
+ error: err instanceof Error ? err.message : String(err),
104
+ };
105
+ }
106
+ },
107
+ };
108
+
109
+ module.exports = { searchDecisionsAndContractsTool };