@jungjaehoon/mama-server 1.9.3 → 1.10.1

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,16 +75,23 @@ Any MCP-compatible client can use MAMA with:
75
75
  npx -y @jungjaehoon/mama-server
76
76
  ```
77
77
 
78
- ## Available Tools (v1.3)
79
-
80
- The MCP server exposes 4 core tools:
81
-
82
- | Tool | Description |
83
- | ----------------- | --------------------------------------------------------------------- |
84
- | `save` | Save decision (`type='decision'`) or checkpoint (`type='checkpoint'`) |
85
- | `search` | Semantic search (with `query`) or list recent items (without `query`) |
86
- | `update` | Update decision outcome (case-insensitive: success/failed/partial) |
87
- | `load_checkpoint` | Resume previous session |
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 |
88
95
 
89
96
  ### Edge Types
90
97
 
@@ -224,7 +231,7 @@ Interactive visualization of your reasoning graph.
224
231
  ## Technical Details
225
232
 
226
233
  - **Database:** SQLite + pure-TS cosine similarity
227
- - **Embeddings:** Transformers.js (Xenova/multilingual-e5-small, 384-dim)
234
+ - **Embeddings:** Transformers.js (Xenova/multilingual-e5-large, 1024-dim)
228
235
  - **Transport:** stdio-based MCP protocol (default) + optional HTTP embedding server (port 3849)
229
236
  - **Storage:** ~/.claude/mama-memory.db (configurable via MAMA_DB_PATH)
230
237
  - **Port File:** ~/.mama-embedding-port (for client discovery)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-server",
3
- "version": "1.9.3",
3
+ "version": "1.10.1",
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.3.3",
42
+ "@jungjaehoon/mama-core": "^1.4.0",
43
43
  "@modelcontextprotocol/sdk": "^1.0.1"
44
44
  },
45
45
  "devDependencies": {
@@ -119,7 +119,7 @@ CREATE INDEX IF NOT EXISTS idx_sessions_last_active ON sessions(last_active_at);
119
119
  -- because sqlite-vss requires extension loading first
120
120
  --
121
121
  -- CREATE VIRTUAL TABLE vss_memories USING vss0(
122
- -- embedding(384) -- multilingual-e5-small embeddings
122
+ -- embedding(1024) -- multilingual-e5-large embeddings
123
123
  -- );
124
124
 
125
125
  -- ══════════════════════════════════════════════════════════════
@@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS error_patterns (
33
33
  first_seen INTEGER NOT NULL, -- First occurrence timestamp
34
34
 
35
35
  -- Vector Embedding (for semantic matching)
36
- embedding BLOB, -- 384-dim embedding (multilingual-e5-small)
36
+ embedding BLOB, -- 1024-dim embedding (multilingual-e5-large)
37
37
 
38
38
  -- Metadata
39
39
  created_at INTEGER DEFAULT (unixepoch()),
@@ -56,7 +56,7 @@ const FIX_INSTRUCTIONS = {
56
56
  steps: [
57
57
  '1. Install Transformers.js: npm install @xenova/transformers',
58
58
  '2. Configure model in ~/.mama/config.json:',
59
- ' { "embeddingModel": "Xenova/multilingual-e5-small" }',
59
+ ' { "embeddingModel": "Xenova/multilingual-e5-large" }',
60
60
  '3. Restart Claude Code to reload configuration',
61
61
  ],
62
62
  impact: 'Without embeddings, MAMA falls back to keyword search (30% less accurate)',
package/src/server.js CHANGED
@@ -248,6 +248,28 @@ type='checkpoint': session state for resumption (ALSO requires search first!)`,
248
248
  minimum: 0,
249
249
  maximum: 1,
250
250
  },
251
+ // Scope & temporal fields
252
+ scopes: {
253
+ type: 'array',
254
+ items: {
255
+ type: 'object',
256
+ properties: {
257
+ kind: {
258
+ type: 'string',
259
+ enum: ['global', 'user', 'channel', 'project'],
260
+ },
261
+ id: { type: 'string' },
262
+ },
263
+ required: ['kind', 'id'],
264
+ },
265
+ description:
266
+ '[Decision] Memory scopes for isolation. Example: [{"kind": "project", "id": "/path/to/project"}]',
267
+ },
268
+ event_date: {
269
+ type: 'string',
270
+ description:
271
+ '[Decision] ISO 8601 date when the event occurred (e.g., "2024-01-15"). Defaults to now.',
272
+ },
251
273
  // Checkpoint fields
252
274
  summary: {
253
275
  type: 'string',
@@ -311,6 +333,21 @@ When presenting search results to the user or agent, include a brief **Reasoning
311
333
  type: 'number',
312
334
  description: 'Maximum results. Default: 10',
313
335
  },
336
+ scopes: {
337
+ type: 'array',
338
+ items: {
339
+ type: 'object',
340
+ properties: {
341
+ kind: {
342
+ type: 'string',
343
+ enum: ['global', 'user', 'channel', 'project'],
344
+ },
345
+ id: { type: 'string' },
346
+ },
347
+ required: ['kind', 'id'],
348
+ },
349
+ description: 'Filter search results by scope.',
350
+ },
314
351
  },
315
352
  },
316
353
  },
@@ -415,6 +452,8 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
415
452
  // Handle tool execution - 4 core tools only
416
453
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
417
454
  const { name, arguments: args } = request.params;
455
+ const toolStart = Date.now();
456
+ console.error(`[MAMA MCP] Tool start: ${name}`);
418
457
 
419
458
  try {
420
459
  let result;
@@ -446,6 +485,8 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
446
485
  this.legacyNoticeEmittedInToolResponse = true;
447
486
  }
448
487
 
488
+ console.error(`[MAMA MCP] Tool done: ${name} (${Date.now() - toolStart}ms)`);
489
+
449
490
  return {
450
491
  content: [
451
492
  {
@@ -464,6 +505,7 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
464
505
  ],
465
506
  };
466
507
  } catch (error) {
508
+ console.error(`[MAMA MCP] Tool failed: ${name} (${Date.now() - toolStart}ms)`);
467
509
  console.error('[MAMA MCP] Tool execution error:', error);
468
510
  return {
469
511
  content: [
@@ -485,11 +527,19 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
485
527
  const { type } = args;
486
528
 
487
529
  if (type === 'decision') {
488
- const { topic, decision, reasoning, confidence = 0.5 } = args;
530
+ const { topic, decision, reasoning, confidence = 0.5, scopes, event_date } = args;
489
531
  if (!topic || !decision || !reasoning) {
490
532
  return { success: false, message: '❌ Decision requires: topic, decision, reasoning' };
491
533
  }
492
- const id = await mama.save({ topic, decision, reasoning, confidence });
534
+ const id = await mama.save({
535
+ type: 'user_decision',
536
+ topic,
537
+ decision,
538
+ reasoning,
539
+ confidence,
540
+ ...(scopes && { scopes }),
541
+ ...(event_date && { event_date }),
542
+ });
493
543
  return {
494
544
  success: true,
495
545
  id,
@@ -519,7 +569,7 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
519
569
  * Handle unified search (decisions + checkpoints)
520
570
  */
521
571
  async handleSearch(args) {
522
- const { query, type = 'all', limit = 10 } = args;
572
+ const { query, type = 'all', limit = 10, scopes } = args;
523
573
 
524
574
  const results = [];
525
575
 
@@ -527,12 +577,13 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
527
577
  if (type === 'all' || type === 'decision') {
528
578
  let decisions;
529
579
  if (query) {
530
- // suggest() returns { results: [...] } object or null
531
- // Note: suggest() takes options object as second parameter
532
- const suggestResult = await mama.suggest(query, { limit });
580
+ const suggestResult = await mama.suggest(query, {
581
+ limit,
582
+ ...(scopes && { scopes }),
583
+ });
533
584
  decisions = suggestResult?.results || [];
534
585
  } else {
535
- decisions = await mama.list(limit);
586
+ decisions = await mama.list({ limit, ...(scopes && { scopes }) });
536
587
  }
537
588
  // Ensure decisions is an array
538
589
  if (Array.isArray(decisions)) {
@@ -235,10 +235,6 @@ const loadCheckpointTool = {
235
235
  inputSchema: {
236
236
  type: 'object',
237
237
  properties: {
238
- session_id: {
239
- type: 'string',
240
- description: 'Optional session ID. If not provided, loads the most recent checkpoint.',
241
- },
242
238
  include_narrative: {
243
239
  type: 'boolean',
244
240
  description: 'Include related narrative/decisions (default: true)',
@@ -258,8 +254,7 @@ const loadCheckpointTool = {
258
254
  },
259
255
  handler: async (args = {}) => {
260
256
  const start = Date.now();
261
- // eslint-disable-next-line no-unused-vars
262
- const { session_id, include_narrative = true, include_links = true, link_depth = 1 } = args;
257
+ const { include_narrative = true, include_links = true, link_depth = 1 } = args;
263
258
 
264
259
  try {
265
260
  const adapter = getAdapter();
@@ -338,7 +333,7 @@ const loadCheckpointTool = {
338
333
  const formattedResponse = formatRestart(checkpoint, narrative, links);
339
334
 
340
335
  // BMad Workflow Integration: Add Story context
341
- const currentStory = inferCurrentStory(checkpoint.summary);
336
+ const currentStory = include_narrative ? inferCurrentStory(checkpoint.summary) : null;
342
337
  let bmadWorkflowContext = '';
343
338
 
344
339
  if (currentStory && currentStory.details) {
@@ -15,6 +15,7 @@
15
15
  * - search_narrative: Semantic search with link expansion ✅
16
16
  * - generate_quality_report: Generate coverage and quality metrics report ✅
17
17
  * - get_restart_metrics: Get restart success rate and latency metrics ✅
18
+ * - ingest_conversation: Ingest conversation messages into memory ✅
18
19
  *
19
20
  * @module tools
20
21
  */
@@ -27,6 +28,7 @@ const { updateOutcomeTool } = require('./update-outcome.js');
27
28
  const { saveCheckpointTool, loadCheckpointTool } = require('./checkpoint-tools.js');
28
29
  const { searchNarrativeTool } = require('./search-narrative.js');
29
30
  const { generateQualityReportTool, getRestartMetricsTool } = require('./quality-metrics-tools.js');
31
+ const { ingestConversationTool } = require('./ingest-conversation.js');
30
32
 
31
33
  /**
32
34
  * Create all MAMA memory tools
@@ -47,6 +49,7 @@ function createMemoryTools() {
47
49
  search_narrative: searchNarrativeTool,
48
50
  generate_quality_report: generateQualityReportTool,
49
51
  get_restart_metrics: getRestartMetricsTool,
52
+ ingest_conversation: ingestConversationTool,
50
53
  };
51
54
  }
52
55
 
@@ -63,4 +66,5 @@ module.exports = {
63
66
  searchNarrativeTool,
64
67
  generateQualityReportTool,
65
68
  getRestartMetricsTool,
69
+ ingestConversationTool,
66
70
  };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * MCP Tool: ingest_conversation
3
+ *
4
+ * Ingests conversation messages into MAMA memory with optional extraction.
5
+ *
6
+ * @module ingest-conversation
7
+ */
8
+
9
+ const { ingestConversation } = require('@jungjaehoon/mama-core');
10
+
11
+ const createIngestConversationTool = (mamaApi) => ({
12
+ name: 'ingest_conversation',
13
+ description:
14
+ "Ingest a conversation into MAMA's memory. Stores the raw conversation and optionally extracts structured memories (decisions, facts, preferences) using LLM. Use this to import past conversations or chat logs into memory.",
15
+ inputSchema: {
16
+ type: 'object',
17
+ properties: {
18
+ messages: {
19
+ type: 'array',
20
+ items: {
21
+ type: 'object',
22
+ properties: {
23
+ role: { type: 'string', enum: ['user', 'assistant', 'system'] },
24
+ content: { type: 'string' },
25
+ },
26
+ required: ['role', 'content'],
27
+ },
28
+ description: 'Conversation messages to ingest.',
29
+ },
30
+ scopes: {
31
+ type: 'array',
32
+ items: {
33
+ type: 'object',
34
+ properties: {
35
+ kind: { type: 'string', enum: ['global', 'user', 'channel', 'project'] },
36
+ id: { type: 'string' },
37
+ },
38
+ required: ['kind', 'id'],
39
+ },
40
+ description: 'Memory scopes for isolation.',
41
+ },
42
+ session_date: {
43
+ type: 'string',
44
+ description: 'ISO 8601 date when the conversation occurred (e.g., "2024-01-15").',
45
+ },
46
+ extract: {
47
+ type: 'boolean',
48
+ description: 'Whether to extract structured memories from the conversation. Default: false',
49
+ },
50
+ },
51
+ required: ['messages'],
52
+ },
53
+
54
+ async handler(params, _context) {
55
+ const { messages, scopes, session_date, extract = false } = params || {};
56
+
57
+ try {
58
+ if (!messages || !Array.isArray(messages) || messages.length === 0) {
59
+ return {
60
+ success: false,
61
+ message: '❌ Validation error: messages must be a non-empty array',
62
+ };
63
+ }
64
+
65
+ const ingestFn = mamaApi.ingestConversation || ingestConversation;
66
+ const result = await ingestFn({
67
+ messages,
68
+ scopes: scopes || [],
69
+ source: {
70
+ package: 'mcp-server',
71
+ source_type: 'mcp_ingest_conversation',
72
+ },
73
+ ...(session_date && { sessionDate: session_date }),
74
+ ...(extract && { extract: { enabled: true } }),
75
+ });
76
+
77
+ return {
78
+ success: true,
79
+ raw_id: result.rawId,
80
+ extracted_memories: result.extractedMemories || [],
81
+ message: `✅ Conversation ingested (ID: ${result.rawId})${
82
+ result.extractedMemories?.length
83
+ ? `, extracted ${result.extractedMemories.length} memories`
84
+ : ''
85
+ }`,
86
+ };
87
+ } catch (error) {
88
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
89
+ return {
90
+ success: false,
91
+ message: `❌ Failed to ingest conversation: ${errorMessage}`,
92
+ };
93
+ }
94
+ },
95
+ });
96
+
97
+ const ingestConversationTool = createIngestConversationTool({});
98
+
99
+ module.exports = { ingestConversationTool, createIngestConversationTool };
@@ -26,7 +26,7 @@ const mama = require('@jungjaehoon/mama-core/mama-api');
26
26
  const listDecisionsTool = {
27
27
  name: 'list_decisions',
28
28
  description:
29
- 'List recent decisions in chronological order. Returns formatted list showing time, type (user/assistant), topic, preview, confidence, and status. Use this to see recent activity or find decisions by browsing.',
29
+ 'List recent decisions in chronological order. Returns formatted list showing time, type (user/assistant), topic, preview, confidence, and status. Use this to see recent activity or find decisions by browsing. Use scopes to filter by project/channel.',
30
30
  inputSchema: {
31
31
  type: 'object',
32
32
  properties: {
@@ -36,12 +36,24 @@ const listDecisionsTool = {
36
36
  minimum: 1,
37
37
  maximum: 100,
38
38
  },
39
+ scopes: {
40
+ type: 'array',
41
+ items: {
42
+ type: 'object',
43
+ properties: {
44
+ kind: { type: 'string', enum: ['global', 'user', 'channel', 'project'] },
45
+ id: { type: 'string' },
46
+ },
47
+ required: ['kind', 'id'],
48
+ },
49
+ description: 'Filter list by scope. If omitted, lists all scopes.',
50
+ },
39
51
  },
40
52
  required: [],
41
53
  },
42
54
 
43
55
  async handler(params, _context) {
44
- const { limit = 20 } = params || {};
56
+ const { limit = 20, scopes } = params || {};
45
57
 
46
58
  try {
47
59
  // Validation: Limit range check
@@ -52,9 +64,7 @@ const listDecisionsTool = {
52
64
  };
53
65
  }
54
66
 
55
- // Call MAMA API with markdown format for human display
56
- // mama.list() defaults to JSON (LLM-first), but we need markdown for user display
57
- const list = await mama.list({ limit, format: 'markdown' });
67
+ const list = await mama.list({ limit, format: 'markdown', ...(scopes && { scopes }) });
58
68
 
59
69
  // Return success response with formatted list
60
70
  return {
@@ -4,15 +4,8 @@
4
4
  * Story M1.3: MCP Tool - recall_decision (ported from mcp-server)
5
5
  * Priority: P1 (Core Feature)
6
6
  *
7
- * Recalls full decision history for a specific topic.
8
- * This is a wrapper around the existing mama.recall() API.
9
- *
10
- * Flow:
11
- * 1. User (via Claude Desktop): "Recall my decision about auth strategy"
12
- * 2. Claude: Calls recall_decision MCP tool
13
- * 3. Tool: Validates input, calls mama.recall()
14
- * 4. mama.recall(): Queries decision history + formats as markdown
15
- * 5. Tool: Returns formatted markdown response
7
+ * Recalls decision history for a specific topic using v2 recallMemory API.
8
+ * Supports scope-based filtering.
16
9
  *
17
10
  * @module recall-decision
18
11
  */
@@ -20,26 +13,51 @@
20
13
  const mama = require('@jungjaehoon/mama-core/mama-api');
21
14
 
22
15
  /**
23
- * Recall decision tool definition
16
+ * Create recall decision tool with dependencies
17
+ * @param {Object} mamaApi - MAMA API instance
24
18
  */
25
- const recallDecisionTool = {
19
+ const createRecallDecisionTool = (mamaApi) => ({
26
20
  name: 'recall_decision',
27
21
  description:
28
- 'Recall full decision history for a specific topic. Returns all past decisions on this topic in chronological order with reasoning, confidence, and outcomes. Use this when you need to review previous decisions, understand decision evolution, or check current position on a topic.\n\n⚡ GRAPH TRAVERSAL: When the same topic is reused across multiple decisions, this tool automatically shows the decision evolution chain (supersedes graph), enabling Learn/Unlearn/Relearn workflows.',
22
+ 'Recall decision history for a topic using semantic search. Returns past decisions filtered by scope if provided. Use this when you need to review previous decisions, understand decision evolution, or check current position on a topic.\n\n⚡ GRAPH TRAVERSAL: When the same topic is reused across multiple decisions, this tool automatically shows the decision evolution chain (supersedes graph), enabling Learn/Unlearn/Relearn workflows.',
29
23
  inputSchema: {
30
24
  type: 'object',
31
25
  properties: {
32
26
  topic: {
33
27
  type: 'string',
34
28
  description:
35
- "Decision topic to recall (e.g., 'auth_strategy', 'mesh_detail_choice'). Use the EXACT SAME topic name used in save_decision to see full decision evolution graph. Different topic names will show separate, disconnected decisions.",
29
+ "Decision topic to recall (e.g., 'auth_strategy', 'mesh_detail_choice'). Use the EXACT SAME topic name used in save_decision to see full decision evolution graph.",
30
+ },
31
+ format: {
32
+ type: 'string',
33
+ enum: ['markdown', 'json'],
34
+ description: "Output format. Default: 'markdown'",
35
+ },
36
+ scopes: {
37
+ type: 'array',
38
+ items: {
39
+ type: 'object',
40
+ properties: {
41
+ kind: {
42
+ type: 'string',
43
+ enum: ['global', 'user', 'channel', 'project'],
44
+ description: 'Scope type',
45
+ },
46
+ id: {
47
+ type: 'string',
48
+ description: 'Scope identifier (e.g., project path, channel ID)',
49
+ },
50
+ },
51
+ required: ['kind', 'id'],
52
+ },
53
+ description: 'Filter recall results by scope. If omitted, returns all scopes.',
36
54
  },
37
55
  },
38
56
  required: ['topic'],
39
57
  },
40
58
 
41
59
  async handler(params, _context) {
42
- const { topic } = params || {};
60
+ const { topic, format = 'markdown', scopes } = params || {};
43
61
 
44
62
  try {
45
63
  // Validation: Non-empty string check
@@ -50,26 +68,53 @@ const recallDecisionTool = {
50
68
  };
51
69
  }
52
70
 
53
- // Call MAMA API with markdown format for human display
54
- // mama.recall() defaults to JSON (LLM-first), but we need markdown for user display
55
- const history = await mama.recall(topic, { format: 'markdown' });
71
+ if (scopes && scopes.length > 0) {
72
+ // Use v2 recallMemory for scope-aware semantic recall
73
+ const bundle = await mamaApi.recallMemory(topic, {
74
+ scopes,
75
+ includeHistory: true,
76
+ });
77
+
78
+ if (format === 'json') {
79
+ return { success: true, history: bundle, message: bundle };
80
+ }
81
+
82
+ const memories = bundle.memories || [];
83
+ let md = `🧠 **Recall: ${topic}** (${memories.length} results)\n\n`;
84
+ for (const m of memories) {
85
+ md += `### ${m.topic}\n`;
86
+ md += `${m.summary}\n`;
87
+ if (m.details && m.details !== m.summary) {
88
+ md += `> ${m.details}\n`;
89
+ }
90
+ md += `- Confidence: ${m.confidence} | Status: ${m.status}`;
91
+ if (m.event_date) {
92
+ md += ` | Event: ${m.event_date}`;
93
+ }
94
+ md += '\n\n';
95
+ }
96
+ return { success: true, history: md, message: md };
97
+ }
98
+
99
+ // Legacy path: topic-exact-match recall (no scopes)
100
+ const history = await mamaApi.recall(topic, { format });
56
101
 
57
- // Return success response with formatted history
58
102
  return {
59
103
  success: true,
60
104
  history,
61
- message: history, // For backward compatibility with MCP response format
105
+ message: history,
62
106
  };
63
107
  } catch (error) {
64
- // Error handling: Return user-friendly message
65
108
  const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
66
-
67
109
  return {
68
110
  success: false,
69
111
  message: `❌ Failed to recall decisions: ${errorMessage}`,
70
112
  };
71
113
  }
72
114
  },
73
- };
115
+ });
116
+
117
+ // Default instance with real dependency
118
+ const recallDecisionTool = createRecallDecisionTool(mama);
74
119
 
75
- module.exports = { recallDecisionTool };
120
+ module.exports = { recallDecisionTool, createRecallDecisionTool };
@@ -149,6 +149,10 @@ When you find similar past decisions (returned in similar_decisions), choose you
149
149
  - **debate**: Present a counter-argument with evidence. Explain why the prior decision may be wrong.
150
150
  - **synthesize**: Merge multiple decisions into a new unified approach.
151
151
 
152
+ **SCOPES & TEMPORAL:**
153
+ - Use 'scopes' to isolate decisions per project/channel/user (e.g., [{"kind": "project", "id": "/my/app"}])
154
+ - Use 'event_date' (ISO 8601) to record when events actually happened vs. when they were saved
155
+
152
156
  **5-LAYER REASONING (CoT Guide):**
153
157
  Structure your reasoning with these layers for maximum value:
154
158
  1. **Context**: What problem/situation prompted this decision?
@@ -211,6 +215,31 @@ Structure your reasoning with these layers for maximum value:
211
215
  type: 'string',
212
216
  description: 'Potential risks or downsides (Tension layer).',
213
217
  },
218
+ scopes: {
219
+ type: 'array',
220
+ items: {
221
+ type: 'object',
222
+ properties: {
223
+ kind: {
224
+ type: 'string',
225
+ enum: ['global', 'user', 'channel', 'project'],
226
+ description: 'Scope type',
227
+ },
228
+ id: {
229
+ type: 'string',
230
+ description: 'Scope identifier (e.g., project path, channel ID)',
231
+ },
232
+ },
233
+ required: ['kind', 'id'],
234
+ },
235
+ description:
236
+ 'Memory scopes for isolation. Decisions are stored per-scope. If omitted, decision is unscoped (global). Example: [{"kind": "project", "id": "/path/to/project"}]',
237
+ },
238
+ event_date: {
239
+ type: 'string',
240
+ description:
241
+ 'ISO 8601 date when the event actually occurred (e.g., "2024-01-15"). If omitted, defaults to current time. Use this when recording decisions about past events.',
242
+ },
214
243
  },
215
244
  required: ['topic', 'decision', 'reasoning'],
216
245
  },
@@ -226,6 +255,8 @@ Structure your reasoning with these layers for maximum value:
226
255
  evidence,
227
256
  alternatives,
228
257
  risks,
258
+ scopes,
259
+ event_date,
229
260
  } = params || {};
230
261
 
231
262
  try {
@@ -305,6 +336,8 @@ Structure your reasoning with these layers for maximum value:
305
336
  alternatives,
306
337
  risks,
307
338
  trust_context: trustContext,
339
+ ...(scopes && { scopes }),
340
+ ...(event_date && { event_date }),
308
341
  });
309
342
 
310
343
  // Story 1.2: Return enhanced response with collaborative fields
@@ -17,7 +17,7 @@ const mama = require('@jungjaehoon/mama-core/mama-api');
17
17
  const suggestDecisionTool = {
18
18
  name: 'suggest_decision',
19
19
  description:
20
- "Auto-suggest relevant past decisions based on user's question. Uses semantic search to find decisions related to the current context. Returns null if no relevant decisions found. Supports multilingual queries (English, Korean, etc.).",
20
+ "Auto-suggest relevant past decisions based on user's question. Uses semantic search to find decisions related to the current context. Returns null if no relevant decisions found. Supports multilingual queries (English, Korean, etc.). Use 'scopes' to filter by project/channel.",
21
21
  inputSchema: {
22
22
  type: 'object',
23
23
  properties: {
@@ -48,12 +48,24 @@ const suggestDecisionTool = {
48
48
  description: 'Optional: Disable recency weighting entirely. Default: false',
49
49
  default: false,
50
50
  },
51
+ scopes: {
52
+ type: 'array',
53
+ items: {
54
+ type: 'object',
55
+ properties: {
56
+ kind: { type: 'string', enum: ['global', 'user', 'channel', 'project'] },
57
+ id: { type: 'string' },
58
+ },
59
+ required: ['kind', 'id'],
60
+ },
61
+ description: 'Filter suggestions by scope. If omitted, searches all scopes.',
62
+ },
51
63
  },
52
64
  required: ['userQuestion'],
53
65
  },
54
66
 
55
67
  async handler(params, _context) {
56
- const { userQuestion, recencyWeight, recencyScale, recencyDecay, disableRecency } =
68
+ const { userQuestion, recencyWeight, recencyScale, recencyDecay, disableRecency, scopes } =
57
69
  params || {};
58
70
 
59
71
  try {
@@ -65,13 +77,13 @@ const suggestDecisionTool = {
65
77
  };
66
78
  }
67
79
 
68
- // Call MAMA API with markdown format
69
80
  const suggestions = await mama.suggest(userQuestion, {
70
81
  format: 'markdown',
71
82
  recencyWeight,
72
83
  recencyScale,
73
84
  recencyDecay,
74
85
  disableRecency,
86
+ ...(scopes && { scopes }),
75
87
  });
76
88
 
77
89
  if (!suggestions) {