@jungjaehoon/mama-server 1.10.1 β 1.11.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/package.json +1 -1
- package/src/server.js +80 -261
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -28,9 +28,9 @@ const {
|
|
|
28
28
|
} = require('@modelcontextprotocol/sdk/types.js');
|
|
29
29
|
const path = require('path');
|
|
30
30
|
|
|
31
|
-
// Import MAMA tools
|
|
32
|
-
|
|
33
|
-
const
|
|
31
|
+
// Import all MAMA tools from src/tools/ β single source of truth for tool definitions
|
|
32
|
+
const { createMemoryTools } = require('./tools/index.js');
|
|
33
|
+
const memoryTools = createMemoryTools();
|
|
34
34
|
const mama = require('@jungjaehoon/mama-core/mama-api');
|
|
35
35
|
|
|
36
36
|
// Import core modules from mama-core
|
|
@@ -192,264 +192,79 @@ class MAMAServer {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
setupHandlers() {
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
2. Check if same topic exists (yours will supersede it)
|
|
214
|
-
3. MUST include link in reasoning/summary field
|
|
215
|
-
|
|
216
|
-
π LINKING FORMAT:
|
|
217
|
-
β’ [Decision] reasoning: End with 'builds_on: <id>' or 'debates: <id>' or 'synthesizes: [id1, id2]'
|
|
218
|
-
β’ [Checkpoint] summary: Include 'Related decisions: decision_xxx, decision_yyy'
|
|
219
|
-
|
|
220
|
-
type='decision': choices & lessons (same topic = evolution chain)
|
|
221
|
-
type='checkpoint': session state for resumption (ALSO requires search first!)`,
|
|
222
|
-
inputSchema: {
|
|
223
|
-
type: 'object',
|
|
224
|
-
properties: {
|
|
225
|
-
type: {
|
|
226
|
-
type: 'string',
|
|
227
|
-
enum: ['decision', 'checkpoint'],
|
|
228
|
-
description: "What to save: 'decision' or 'checkpoint'",
|
|
229
|
-
},
|
|
230
|
-
// Decision fields
|
|
231
|
-
topic: {
|
|
232
|
-
type: 'string',
|
|
233
|
-
description:
|
|
234
|
-
"[Decision] Topic identifier (e.g., 'auth_strategy'). β‘ REUSE same topic = supersedes previous, creating evolution chain.",
|
|
235
|
-
},
|
|
236
|
-
decision: {
|
|
237
|
-
type: 'string',
|
|
238
|
-
description: "[Decision] The decision made (e.g., 'Use JWT with refresh tokens').",
|
|
239
|
-
},
|
|
240
|
-
reasoning: {
|
|
241
|
-
type: 'string',
|
|
242
|
-
description:
|
|
243
|
-
"[Decision] Why this decision was made. Include 5-layer narrative: (1) Context - what problem/situation; (2) Evidence - what proves this works (tests, benchmarks, prior experience); (3) Alternatives - what other options were considered and why rejected; (4) Risks - known limitations or failure modes; (5) Rationale - final reasoning for this choice. β οΈ REQUIRED: End with 'builds_on: <id>' or 'debates: <id>' or 'synthesizes: [id1, id2]' to link related decisions.",
|
|
244
|
-
},
|
|
245
|
-
confidence: {
|
|
246
|
-
type: 'number',
|
|
247
|
-
description: '[Decision] Confidence 0.0-1.0. Default: 0.5',
|
|
248
|
-
minimum: 0,
|
|
249
|
-
maximum: 1,
|
|
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
|
-
},
|
|
273
|
-
// Checkpoint fields
|
|
274
|
-
summary: {
|
|
275
|
-
type: 'string',
|
|
276
|
-
description:
|
|
277
|
-
"[Checkpoint] Session state summary. Use 4-section format: (1) π― Goal & Progress - what was the goal, where did you stop; (2) β
Evidence - mark each item as Verified/Not run/Assumed with proof; (3) β³ Unfinished & Risks - incomplete work, blockers, unknowns; (4) π¦ Next Agent Briefing - Definition of Done, quick health checks to run first. β οΈ Include 'Related decisions: decision_xxx, decision_yyy' to link context.",
|
|
278
|
-
},
|
|
279
|
-
next_steps: {
|
|
280
|
-
type: 'string',
|
|
281
|
-
description:
|
|
282
|
-
'[Checkpoint] Instructions for next session: DoD (Definition of Done), quick verification commands (npm test, curl health), constraints/cautions.',
|
|
283
|
-
},
|
|
284
|
-
open_files: {
|
|
285
|
-
type: 'array',
|
|
286
|
-
items: { type: 'string' },
|
|
287
|
-
description: '[Checkpoint] Currently relevant files.',
|
|
288
|
-
},
|
|
195
|
+
// Tool definitions come from src/tools/ (single source of truth).
|
|
196
|
+
// Legacy unified tools (save, search, update) kept as wrappers for backward compat.
|
|
197
|
+
const legacyNotice = this.legacyHttpEmbeddingMode
|
|
198
|
+
? `${this.getLegacyMigrationNotice()}\n\n`
|
|
199
|
+
: '';
|
|
200
|
+
|
|
201
|
+
const legacyTools = [
|
|
202
|
+
{
|
|
203
|
+
name: 'save',
|
|
204
|
+
description: `${legacyNotice}${memoryTools.save_decision.description}\n\nAlso supports type='checkpoint' for session state.`,
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: 'object',
|
|
207
|
+
properties: {
|
|
208
|
+
...memoryTools.save_decision.inputSchema.properties,
|
|
209
|
+
type: {
|
|
210
|
+
type: 'string',
|
|
211
|
+
enum: ['decision', 'checkpoint'],
|
|
212
|
+
description: "What to save: 'decision' or 'checkpoint'",
|
|
289
213
|
},
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// 2. SEARCH - Unified search across decisions and checkpoints
|
|
294
|
-
{
|
|
295
|
-
name: 'search',
|
|
296
|
-
description: `π Search the reasoning graph before acting.
|
|
297
|
-
|
|
298
|
-
β‘ TRIGGERS - Call this BEFORE:
|
|
299
|
-
β’ β οΈ REQUIRED before 'save' (find links first!)
|
|
300
|
-
β’ Making architectural choices (check prior art)
|
|
301
|
-
β’ Debugging (find past failures on similar issues)
|
|
302
|
-
β’ Starting work on a topic (load context)
|
|
303
|
-
β’ User asks: "λμλλΌ", "what did we decide", "μ΄μ μ"
|
|
304
|
-
|
|
305
|
-
π USE FOR REASONING GRAPH:
|
|
306
|
-
β’ Find decisions to supersede (same topic)
|
|
307
|
-
β’ Find decisions to link (builds_on, debates, synthesizes)
|
|
308
|
-
β’ Understand decision evolution (time-ordered results)
|
|
309
|
-
|
|
310
|
-
Cross-lingual: Works in Korean and English.
|
|
311
|
-
β οΈ High similarity (>0.8) = MUST link with builds_on/debates/synthesizes.
|
|
312
|
-
|
|
313
|
-
π§ OUTPUT EXPECTATION:
|
|
314
|
-
When presenting search results to the user or agent, include a brief **Reasoning Summary** grounded in the actual results:
|
|
315
|
-
- Why these results match (tokens/endpoint/field overlap)
|
|
316
|
-
- What is known vs unknown (explicitly mark unknowns)
|
|
317
|
-
- What to do next (use contract fields, avoid guessing)`,
|
|
318
|
-
inputSchema: {
|
|
319
|
-
type: 'object',
|
|
320
|
-
properties: {
|
|
321
|
-
query: {
|
|
322
|
-
type: 'string',
|
|
323
|
-
description:
|
|
324
|
-
'Search query (optional). Semantic search finds related decisions even with different wording. If empty, returns recent items sorted by time.',
|
|
325
|
-
},
|
|
326
|
-
type: {
|
|
327
|
-
type: 'string',
|
|
328
|
-
enum: ['all', 'decision', 'checkpoint'],
|
|
329
|
-
description:
|
|
330
|
-
"Filter by type: 'decision' for architectural choices, 'checkpoint' for session states, 'all' for both. Default: 'all'",
|
|
331
|
-
},
|
|
332
|
-
limit: {
|
|
333
|
-
type: 'number',
|
|
334
|
-
description: 'Maximum results. Default: 10',
|
|
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
|
-
},
|
|
214
|
+
summary: {
|
|
215
|
+
type: 'string',
|
|
216
|
+
description: '[Checkpoint] Session state summary.',
|
|
351
217
|
},
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
{
|
|
356
|
-
name: 'update',
|
|
357
|
-
description: `π Update decision outcome after real-world validation.
|
|
358
|
-
|
|
359
|
-
β‘ TRIGGERS - Call this when:
|
|
360
|
-
β’ Days/weeks later: issues discovered β mark 'failed' + reason
|
|
361
|
-
β’ Production success confirmed β mark 'success'
|
|
362
|
-
β’ Partial results with caveats β mark 'partial'
|
|
363
|
-
β’ User says: "μ΄κ±° μλμ΄", "this didn't work", "μ±κ³΅νμ΄"
|
|
364
|
-
|
|
365
|
-
π REASONING GRAPH IMPACT:
|
|
366
|
-
β’ 'failed' outcomes teach future LLMs what to avoid
|
|
367
|
-
β’ After failure β save NEW decision with same topic to supersede
|
|
368
|
-
|
|
369
|
-
π‘ TIP: Don't just update - if approach changed, save a NEW decision with same topic. This creates evolution history.`,
|
|
370
|
-
inputSchema: {
|
|
371
|
-
type: 'object',
|
|
372
|
-
properties: {
|
|
373
|
-
id: {
|
|
374
|
-
type: 'string',
|
|
375
|
-
description: 'Decision ID to update.',
|
|
376
|
-
},
|
|
377
|
-
outcome: {
|
|
378
|
-
type: 'string',
|
|
379
|
-
description:
|
|
380
|
-
"New outcome status (case-insensitive): 'success' or 'SUCCESS', 'failed' or 'FAILED', 'partial' or 'PARTIAL'.",
|
|
381
|
-
},
|
|
382
|
-
reason: {
|
|
383
|
-
type: 'string',
|
|
384
|
-
description:
|
|
385
|
-
'Why it succeeded/failed/was partial. Include specific evidence: error logs, metrics, user feedback, or what broke.',
|
|
386
|
-
},
|
|
218
|
+
next_steps: {
|
|
219
|
+
type: 'string',
|
|
220
|
+
description: '[Checkpoint] Instructions for next session.',
|
|
387
221
|
},
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
{
|
|
393
|
-
name: 'search_decisions_and_contracts',
|
|
394
|
-
description:
|
|
395
|
-
'Search decisions and related contracts for PreToolUse injection (MAMA v2 hooks).',
|
|
396
|
-
inputSchema: {
|
|
397
|
-
type: 'object',
|
|
398
|
-
properties: {
|
|
399
|
-
query: {
|
|
400
|
-
type: 'string',
|
|
401
|
-
description: 'Search query for decisions.',
|
|
402
|
-
},
|
|
403
|
-
filePath: {
|
|
404
|
-
type: 'string',
|
|
405
|
-
description: 'File path context for contract search.',
|
|
406
|
-
},
|
|
407
|
-
toolName: {
|
|
408
|
-
type: 'string',
|
|
409
|
-
description: 'Tool name context (Edit/Write/apply_patch).',
|
|
410
|
-
},
|
|
411
|
-
decisionLimit: {
|
|
412
|
-
type: 'number',
|
|
413
|
-
description: 'Max decision results (default: 5).',
|
|
414
|
-
},
|
|
415
|
-
contractLimit: {
|
|
416
|
-
type: 'number',
|
|
417
|
-
description: 'Max contract results (default: 3).',
|
|
418
|
-
},
|
|
419
|
-
similarityThreshold: {
|
|
420
|
-
type: 'number',
|
|
421
|
-
description: 'Similarity threshold for vector search (default: 0.7).',
|
|
422
|
-
},
|
|
222
|
+
open_files: {
|
|
223
|
+
type: 'array',
|
|
224
|
+
items: { type: 'string' },
|
|
225
|
+
description: '[Checkpoint] Currently relevant files.',
|
|
423
226
|
},
|
|
424
227
|
},
|
|
228
|
+
required: ['type'],
|
|
425
229
|
},
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
inputSchema: {
|
|
445
|
-
type: 'object',
|
|
446
|
-
properties: {},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: 'search',
|
|
233
|
+
description: memoryTools.suggest_decision.description,
|
|
234
|
+
inputSchema: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
query: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'Search query. Semantic search finds related decisions.',
|
|
240
|
+
},
|
|
241
|
+
type: {
|
|
242
|
+
type: 'string',
|
|
243
|
+
enum: ['all', 'decision', 'checkpoint'],
|
|
244
|
+
description: "Filter by type. Default: 'all'",
|
|
245
|
+
},
|
|
246
|
+
limit: { type: 'number', description: 'Maximum results. Default: 10' },
|
|
247
|
+
scopes: memoryTools.save_decision.inputSchema.properties.scopes,
|
|
447
248
|
},
|
|
448
249
|
},
|
|
449
|
-
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'update',
|
|
253
|
+
description: memoryTools.update_outcome.description,
|
|
254
|
+
inputSchema: memoryTools.update_outcome.inputSchema,
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
// All tools: legacy wrappers + all v2 tools from src/tools/
|
|
259
|
+
const v2Tools = Object.values(memoryTools)
|
|
260
|
+
.filter((t) => t.name && t.inputSchema)
|
|
261
|
+
.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }));
|
|
262
|
+
|
|
263
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
264
|
+
tools: [...legacyTools, ...v2Tools],
|
|
450
265
|
}));
|
|
451
266
|
|
|
452
|
-
// Handle tool execution
|
|
267
|
+
// Handle tool execution β legacy wrappers + v2 tools from src/tools/
|
|
453
268
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
454
269
|
const { name, arguments: args } = request.params;
|
|
455
270
|
const toolStart = Date.now();
|
|
@@ -459,6 +274,7 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
|
|
|
459
274
|
let result;
|
|
460
275
|
|
|
461
276
|
switch (name) {
|
|
277
|
+
// Legacy unified wrappers (backward compat)
|
|
462
278
|
case 'save':
|
|
463
279
|
result = await this.handleSave(args);
|
|
464
280
|
break;
|
|
@@ -468,14 +284,13 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
|
|
|
468
284
|
case 'update':
|
|
469
285
|
result = await this.handleUpdate(args);
|
|
470
286
|
break;
|
|
471
|
-
case 'search_decisions_and_contracts':
|
|
472
|
-
result = await this.handleSearchDecisionsAndContracts(args);
|
|
473
|
-
break;
|
|
474
|
-
case 'load_checkpoint':
|
|
475
|
-
result = await loadCheckpointTool.handler(args);
|
|
476
|
-
break;
|
|
477
287
|
default:
|
|
478
|
-
|
|
288
|
+
// All other tools β src/tools/ handlers (single source of truth)
|
|
289
|
+
if (memoryTools[name] && typeof memoryTools[name].handler === 'function') {
|
|
290
|
+
result = await memoryTools[name].handler(args);
|
|
291
|
+
} else {
|
|
292
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
293
|
+
}
|
|
479
294
|
}
|
|
480
295
|
|
|
481
296
|
const shouldInjectLegacyNotice =
|
|
@@ -610,9 +425,13 @@ Returns: summary (4-section), next_steps (DoD + commands), open_files
|
|
|
610
425
|
);
|
|
611
426
|
}
|
|
612
427
|
|
|
613
|
-
//
|
|
614
|
-
|
|
615
|
-
const
|
|
428
|
+
// Decisions are already sorted by similarity from suggest().
|
|
429
|
+
// Only sort checkpoints by time. Keep decisions first (relevance), checkpoints after (recency).
|
|
430
|
+
const decisions = results.filter((r) => r._type === 'decision');
|
|
431
|
+
const checkpoints = results
|
|
432
|
+
.filter((r) => r._type === 'checkpoint')
|
|
433
|
+
.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
|
434
|
+
const limited = [...decisions, ...checkpoints].slice(0, limit);
|
|
616
435
|
|
|
617
436
|
return {
|
|
618
437
|
success: true,
|