@getmarrow/mcp 2.7.0 → 2.9.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 +121 -271
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +566 -117
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +23 -98
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -23
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +9 -3
package/dist/cli.js
CHANGED
|
@@ -20,13 +20,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
20
20
|
}) : function(o, v) {
|
|
21
21
|
o["default"] = v;
|
|
22
22
|
});
|
|
23
|
-
var __importStar = (this && this.__importStar) || function (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
30
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
41
|
const readline = __importStar(require("readline"));
|
|
32
42
|
const index_1 = require("./index");
|
|
@@ -40,6 +50,26 @@ if (!API_KEY) {
|
|
|
40
50
|
// Auto-orient on startup — cache warnings, inject into EVERY marrow_think response
|
|
41
51
|
let cachedOrientWarnings = [];
|
|
42
52
|
let thinkCallCount = 0;
|
|
53
|
+
const pendingDecisions = new Map();
|
|
54
|
+
const PENDING_TTL_MS = 30 * 60 * 1000; // 30 min TTL
|
|
55
|
+
function actionHash(action) {
|
|
56
|
+
const normalized = action.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
57
|
+
// djb2 hash to prevent decision_id mismatches from normalization-only collisions
|
|
58
|
+
let h = 5381;
|
|
59
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
60
|
+
h = ((h << 5) + h) ^ normalized.charCodeAt(i);
|
|
61
|
+
h = h >>> 0;
|
|
62
|
+
}
|
|
63
|
+
return h.toString(36) + '_' + normalized.slice(0, 32);
|
|
64
|
+
}
|
|
65
|
+
function cleanupPending() {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
for (const [key, val] of pendingDecisions) {
|
|
68
|
+
if (now - val.timestamp > PENDING_TTL_MS) {
|
|
69
|
+
pendingDecisions.delete(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
43
73
|
function formatWarningActionably(w) {
|
|
44
74
|
const pct = Math.round(w.failureRate * 100);
|
|
45
75
|
return `⚠️ ${w.type} has ${pct}% failure rate — check what went wrong last time before proceeding`;
|
|
@@ -49,11 +79,13 @@ async function refreshOrientWarnings() {
|
|
|
49
79
|
const r = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, undefined, SESSION_ID);
|
|
50
80
|
cachedOrientWarnings = r.warnings;
|
|
51
81
|
}
|
|
52
|
-
catch {
|
|
82
|
+
catch {
|
|
83
|
+
// ignore
|
|
84
|
+
}
|
|
53
85
|
}
|
|
54
86
|
// Initial orient
|
|
55
87
|
refreshOrientWarnings().then(() => {
|
|
56
|
-
if (cachedOrientWarnings.some(w => w.failureRate > 0.4)) {
|
|
88
|
+
if (cachedOrientWarnings.some((w) => w.failureRate > 0.4)) {
|
|
57
89
|
process.stderr.write(`[marrow] ⚠️ High failure rate detected on startup — call marrow_orient for details before acting\n`);
|
|
58
90
|
}
|
|
59
91
|
});
|
|
@@ -72,7 +104,9 @@ async function autoCommitOnClose() {
|
|
|
72
104
|
}, SESSION_ID);
|
|
73
105
|
clearTimeout(timeout);
|
|
74
106
|
}
|
|
75
|
-
catch {
|
|
107
|
+
catch {
|
|
108
|
+
// ignore
|
|
109
|
+
}
|
|
76
110
|
}
|
|
77
111
|
}
|
|
78
112
|
process.on('SIGTERM', async () => {
|
|
@@ -81,7 +115,9 @@ process.on('SIGTERM', async () => {
|
|
|
81
115
|
await autoCommitOnClose();
|
|
82
116
|
process.exit(0);
|
|
83
117
|
});
|
|
84
|
-
process.stdin.on('end', async () => {
|
|
118
|
+
process.stdin.on('end', async () => {
|
|
119
|
+
await autoCommitOnClose();
|
|
120
|
+
});
|
|
85
121
|
function send(response) {
|
|
86
122
|
process.stdout.write(JSON.stringify(response) + '\n');
|
|
87
123
|
}
|
|
@@ -91,6 +127,157 @@ function success(id, result) {
|
|
|
91
127
|
function error(id, code, message) {
|
|
92
128
|
send({ jsonrpc: '2.0', id, error: { code, message } });
|
|
93
129
|
}
|
|
130
|
+
// Memory API functions
|
|
131
|
+
async function marrowListMemories(apiKey, baseUrl, params, sessionId) {
|
|
132
|
+
const qs = new URLSearchParams();
|
|
133
|
+
if (params?.status)
|
|
134
|
+
qs.set('status', params.status);
|
|
135
|
+
if (params?.query)
|
|
136
|
+
qs.set('query', params.query);
|
|
137
|
+
if (params?.limit)
|
|
138
|
+
qs.set('limit', String(params.limit));
|
|
139
|
+
if (params?.agentId)
|
|
140
|
+
qs.set('agent_id', params.agentId);
|
|
141
|
+
const res = await fetch(`${baseUrl}/v1/memories?${qs.toString()}`, {
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${apiKey}`,
|
|
144
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
const json = await res.json();
|
|
148
|
+
return json.data?.memories || [];
|
|
149
|
+
}
|
|
150
|
+
async function marrowGetMemory(apiKey, baseUrl, id, sessionId) {
|
|
151
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
|
|
152
|
+
headers: {
|
|
153
|
+
Authorization: `Bearer ${apiKey}`,
|
|
154
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const json = await res.json();
|
|
158
|
+
return json.data?.memory || null;
|
|
159
|
+
}
|
|
160
|
+
async function marrowUpdateMemory(apiKey, baseUrl, id, patch, sessionId) {
|
|
161
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
|
|
162
|
+
method: 'PATCH',
|
|
163
|
+
headers: {
|
|
164
|
+
Authorization: `Bearer ${apiKey}`,
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
167
|
+
},
|
|
168
|
+
body: JSON.stringify(patch),
|
|
169
|
+
});
|
|
170
|
+
const json = await res.json();
|
|
171
|
+
return json.data.memory;
|
|
172
|
+
}
|
|
173
|
+
async function marrowDeleteMemory(apiKey, baseUrl, id, meta, sessionId) {
|
|
174
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
|
|
175
|
+
method: 'DELETE',
|
|
176
|
+
headers: {
|
|
177
|
+
Authorization: `Bearer ${apiKey}`,
|
|
178
|
+
'Content-Type': 'application/json',
|
|
179
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify(meta || {}),
|
|
182
|
+
});
|
|
183
|
+
const json = await res.json();
|
|
184
|
+
return json.data.memory;
|
|
185
|
+
}
|
|
186
|
+
async function marrowMarkOutdated(apiKey, baseUrl, id, meta, sessionId) {
|
|
187
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}/outdated`, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: {
|
|
190
|
+
Authorization: `Bearer ${apiKey}`,
|
|
191
|
+
'Content-Type': 'application/json',
|
|
192
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
193
|
+
},
|
|
194
|
+
body: JSON.stringify(meta || {}),
|
|
195
|
+
});
|
|
196
|
+
const json = await res.json();
|
|
197
|
+
return json.data.memory;
|
|
198
|
+
}
|
|
199
|
+
async function marrowSupersedeMemory(apiKey, baseUrl, id, replacement, sessionId) {
|
|
200
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}/supersede`, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: {
|
|
203
|
+
Authorization: `Bearer ${apiKey}`,
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
206
|
+
},
|
|
207
|
+
body: JSON.stringify(replacement),
|
|
208
|
+
});
|
|
209
|
+
const json = await res.json();
|
|
210
|
+
return json.data;
|
|
211
|
+
}
|
|
212
|
+
async function marrowShareMemory(apiKey, baseUrl, id, agentIds, actor, sessionId) {
|
|
213
|
+
const res = await fetch(`${baseUrl}/v1/memories/${id}/share`, {
|
|
214
|
+
method: 'POST',
|
|
215
|
+
headers: {
|
|
216
|
+
Authorization: `Bearer ${apiKey}`,
|
|
217
|
+
'Content-Type': 'application/json',
|
|
218
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify({ agent_ids: agentIds, actor }),
|
|
221
|
+
});
|
|
222
|
+
const json = await res.json();
|
|
223
|
+
return json.data.memory;
|
|
224
|
+
}
|
|
225
|
+
async function marrowExportMemories(apiKey, baseUrl, params, sessionId) {
|
|
226
|
+
const qs = new URLSearchParams();
|
|
227
|
+
if (params?.format)
|
|
228
|
+
qs.set('format', params.format);
|
|
229
|
+
if (params?.status)
|
|
230
|
+
qs.set('status', params.status);
|
|
231
|
+
if (params?.tags)
|
|
232
|
+
qs.set('tags', params.tags);
|
|
233
|
+
const res = await fetch(`${baseUrl}/v1/memories/export?${qs.toString()}`, {
|
|
234
|
+
headers: {
|
|
235
|
+
Authorization: `Bearer ${apiKey}`,
|
|
236
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
const json = await res.json();
|
|
240
|
+
return json.data;
|
|
241
|
+
}
|
|
242
|
+
async function marrowImportMemories(apiKey, baseUrl, memories, mode, sessionId) {
|
|
243
|
+
const res = await fetch(`${baseUrl}/v1/memories/import`, {
|
|
244
|
+
method: 'POST',
|
|
245
|
+
headers: {
|
|
246
|
+
Authorization: `Bearer ${apiKey}`,
|
|
247
|
+
'Content-Type': 'application/json',
|
|
248
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
249
|
+
},
|
|
250
|
+
body: JSON.stringify({ memories, mode }),
|
|
251
|
+
});
|
|
252
|
+
const json = await res.json();
|
|
253
|
+
return json.data;
|
|
254
|
+
}
|
|
255
|
+
async function marrowRetrieveMemories(apiKey, baseUrl, query, params, sessionId) {
|
|
256
|
+
const qs = new URLSearchParams();
|
|
257
|
+
qs.set('q', query);
|
|
258
|
+
if (params?.limit)
|
|
259
|
+
qs.set('limit', String(params.limit));
|
|
260
|
+
if (params?.from)
|
|
261
|
+
qs.set('from', params.from);
|
|
262
|
+
if (params?.to)
|
|
263
|
+
qs.set('to', params.to);
|
|
264
|
+
if (params?.tags)
|
|
265
|
+
qs.set('tags', params.tags);
|
|
266
|
+
if (params?.source)
|
|
267
|
+
qs.set('source', params.source);
|
|
268
|
+
if (params?.status)
|
|
269
|
+
qs.set('status', params.status);
|
|
270
|
+
if (params?.shared !== undefined)
|
|
271
|
+
qs.set('shared', String(params.shared));
|
|
272
|
+
const res = await fetch(`${baseUrl}/v1/memories/retrieve?${qs.toString()}`, {
|
|
273
|
+
headers: {
|
|
274
|
+
Authorization: `Bearer ${apiKey}`,
|
|
275
|
+
...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const json = await res.json();
|
|
279
|
+
return json.data;
|
|
280
|
+
}
|
|
94
281
|
// Tool definitions
|
|
95
282
|
const TOOLS = [
|
|
96
283
|
{
|
|
@@ -205,6 +392,36 @@ const TOOLS = [
|
|
|
205
392
|
required: ['description', 'success', 'outcome'],
|
|
206
393
|
},
|
|
207
394
|
},
|
|
395
|
+
{
|
|
396
|
+
name: 'marrow_auto',
|
|
397
|
+
description: 'Zero-friction Marrow logging. One call for any action — Marrow handles everything in the background without blocking. ' +
|
|
398
|
+
'Pass what you are about to do. Optionally pass outcome if already done. ' +
|
|
399
|
+
'Use for ANY action: deploys, file writes, API calls, external sends. ' +
|
|
400
|
+
'If you only have time for one call: pass action + outcome + success together — done in one shot.',
|
|
401
|
+
inputSchema: {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {
|
|
404
|
+
action: {
|
|
405
|
+
type: 'string',
|
|
406
|
+
description: 'What you are about to do or just did',
|
|
407
|
+
},
|
|
408
|
+
outcome: {
|
|
409
|
+
type: 'string',
|
|
410
|
+
description: 'What happened (if already done). Omit to log intent only.',
|
|
411
|
+
},
|
|
412
|
+
success: {
|
|
413
|
+
type: 'boolean',
|
|
414
|
+
description: 'Did it succeed (default: true)',
|
|
415
|
+
},
|
|
416
|
+
type: {
|
|
417
|
+
type: 'string',
|
|
418
|
+
enum: ['implementation', 'security', 'architecture', 'process', 'general'],
|
|
419
|
+
description: 'Type of action (default: general)',
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
required: ['action'],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
208
425
|
{
|
|
209
426
|
name: 'marrow_ask',
|
|
210
427
|
description: 'Query the collective hive in plain English. ' +
|
|
@@ -230,6 +447,145 @@ const TOOLS = [
|
|
|
230
447
|
required: [],
|
|
231
448
|
},
|
|
232
449
|
},
|
|
450
|
+
{
|
|
451
|
+
name: 'marrow_list_memories',
|
|
452
|
+
description: 'List memories with optional filters (status, query, limit, agent_id for shared memories).',
|
|
453
|
+
inputSchema: {
|
|
454
|
+
type: 'object',
|
|
455
|
+
properties: {
|
|
456
|
+
status: { type: 'string', enum: ['active', 'outdated', 'deleted'], description: 'Filter by status' },
|
|
457
|
+
query: { type: 'string', description: 'Search query' },
|
|
458
|
+
limit: { type: 'number', description: 'Max results (default: 20)' },
|
|
459
|
+
agentId: { type: 'string', description: 'Agent ID for shared memories' },
|
|
460
|
+
},
|
|
461
|
+
required: [],
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: 'marrow_get_memory',
|
|
466
|
+
description: 'Get a single memory by ID.',
|
|
467
|
+
inputSchema: {
|
|
468
|
+
type: 'object',
|
|
469
|
+
properties: {
|
|
470
|
+
id: { type: 'string', description: 'Memory ID' },
|
|
471
|
+
},
|
|
472
|
+
required: ['id'],
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: 'marrow_update_memory',
|
|
477
|
+
description: 'Update memory text, tags, or metadata.',
|
|
478
|
+
inputSchema: {
|
|
479
|
+
type: 'object',
|
|
480
|
+
properties: {
|
|
481
|
+
id: { type: 'string', description: 'Memory ID' },
|
|
482
|
+
text: { type: 'string', description: 'New text' },
|
|
483
|
+
source: { type: 'string', description: 'Source' },
|
|
484
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
|
|
485
|
+
actor: { type: 'string', description: 'Actor name' },
|
|
486
|
+
note: { type: 'string', description: 'Audit note' },
|
|
487
|
+
},
|
|
488
|
+
required: ['id'],
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: 'marrow_delete_memory',
|
|
493
|
+
description: 'Soft delete a memory.',
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: 'object',
|
|
496
|
+
properties: {
|
|
497
|
+
id: { type: 'string', description: 'Memory ID' },
|
|
498
|
+
actor: { type: 'string', description: 'Actor name' },
|
|
499
|
+
note: { type: 'string', description: 'Audit note' },
|
|
500
|
+
},
|
|
501
|
+
required: ['id'],
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: 'marrow_mark_outdated',
|
|
506
|
+
description: 'Mark a memory as outdated.',
|
|
507
|
+
inputSchema: {
|
|
508
|
+
type: 'object',
|
|
509
|
+
properties: {
|
|
510
|
+
id: { type: 'string', description: 'Memory ID' },
|
|
511
|
+
actor: { type: 'string', description: 'Actor name' },
|
|
512
|
+
note: { type: 'string', description: 'Audit note' },
|
|
513
|
+
},
|
|
514
|
+
required: ['id'],
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: 'marrow_supersede_memory',
|
|
519
|
+
description: 'Atomically replace a memory with a new version.',
|
|
520
|
+
inputSchema: {
|
|
521
|
+
type: 'object',
|
|
522
|
+
properties: {
|
|
523
|
+
id: { type: 'string', description: 'Memory ID to supersede' },
|
|
524
|
+
text: { type: 'string', description: 'New memory text' },
|
|
525
|
+
source: { type: 'string', description: 'Source' },
|
|
526
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
|
|
527
|
+
actor: { type: 'string', description: 'Actor name' },
|
|
528
|
+
note: { type: 'string', description: 'Audit note' },
|
|
529
|
+
},
|
|
530
|
+
required: ['id', 'text'],
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: 'marrow_share_memory',
|
|
535
|
+
description: 'Share a memory with specific agents.',
|
|
536
|
+
inputSchema: {
|
|
537
|
+
type: 'object',
|
|
538
|
+
properties: {
|
|
539
|
+
id: { type: 'string', description: 'Memory ID' },
|
|
540
|
+
agentIds: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to share with' },
|
|
541
|
+
actor: { type: 'string', description: 'Actor name' },
|
|
542
|
+
},
|
|
543
|
+
required: ['id', 'agentIds'],
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: 'marrow_export_memories',
|
|
548
|
+
description: 'Export memories to JSON or CSV.',
|
|
549
|
+
inputSchema: {
|
|
550
|
+
type: 'object',
|
|
551
|
+
properties: {
|
|
552
|
+
format: { type: 'string', enum: ['json', 'csv'], description: 'Export format' },
|
|
553
|
+
status: { type: 'string', enum: ['active', 'all'], description: 'Filter by status' },
|
|
554
|
+
tags: { type: 'string', description: 'Comma-separated tags' },
|
|
555
|
+
},
|
|
556
|
+
required: [],
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
name: 'marrow_import_memories',
|
|
561
|
+
description: 'Import memories with merge (dedup) or replace mode.',
|
|
562
|
+
inputSchema: {
|
|
563
|
+
type: 'object',
|
|
564
|
+
properties: {
|
|
565
|
+
memories: { type: 'array', items: { type: 'object', properties: { text: { type: 'string' }, source: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } } } }, description: 'Memories to import' },
|
|
566
|
+
mode: { type: 'string', enum: ['merge', 'replace'], description: 'Import mode' },
|
|
567
|
+
},
|
|
568
|
+
required: ['memories', 'mode'],
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: 'marrow_retrieve_memories',
|
|
573
|
+
description: 'Full-text search memories with filters (from, to, tags, source, status, shared).',
|
|
574
|
+
inputSchema: {
|
|
575
|
+
type: 'object',
|
|
576
|
+
properties: {
|
|
577
|
+
query: { type: 'string', description: 'Search query' },
|
|
578
|
+
limit: { type: 'number', description: 'Max results' },
|
|
579
|
+
from: { type: 'string', description: 'From date (ISO-8601)' },
|
|
580
|
+
to: { type: 'string', description: 'To date (ISO-8601)' },
|
|
581
|
+
tags: { type: 'string', description: 'Comma-separated tags' },
|
|
582
|
+
source: { type: 'string', description: 'Source filter' },
|
|
583
|
+
status: { type: 'string', enum: ['active', 'outdated', 'deleted'], description: 'Status filter' },
|
|
584
|
+
shared: { type: 'boolean', description: 'Include shared memories' },
|
|
585
|
+
},
|
|
586
|
+
required: ['query'],
|
|
587
|
+
},
|
|
588
|
+
},
|
|
233
589
|
];
|
|
234
590
|
// Request handler
|
|
235
591
|
async function handleRequest(req) {
|
|
@@ -239,7 +595,7 @@ async function handleRequest(req) {
|
|
|
239
595
|
success(id, {
|
|
240
596
|
protocolVersion: '2024-11-05',
|
|
241
597
|
capabilities: { tools: {}, prompts: {} },
|
|
242
|
-
serverInfo: { name: 'marrow', version: '2.
|
|
598
|
+
serverInfo: { name: 'marrow', version: '2.8.0' },
|
|
243
599
|
});
|
|
244
600
|
return;
|
|
245
601
|
}
|
|
@@ -249,9 +605,9 @@ async function handleRequest(req) {
|
|
|
249
605
|
{
|
|
250
606
|
name: 'marrow-always-on',
|
|
251
607
|
description: 'Always-on Marrow memory loop. Instructs the agent to orient at session start, log intent before meaningful actions, and commit outcomes after completion. Install once — works automatically.',
|
|
252
|
-
arguments: []
|
|
253
|
-
}
|
|
254
|
-
]
|
|
608
|
+
arguments: [],
|
|
609
|
+
},
|
|
610
|
+
],
|
|
255
611
|
});
|
|
256
612
|
return;
|
|
257
613
|
}
|
|
@@ -270,25 +626,19 @@ async function handleRequest(req) {
|
|
|
270
626
|
type: 'text',
|
|
271
627
|
text: `You have Marrow — a memory layer that tells you what went wrong last time before you do it again.
|
|
272
628
|
|
|
273
|
-
##
|
|
629
|
+
## PRIMARY: Use marrow_auto for everything
|
|
274
630
|
|
|
275
|
-
|
|
276
|
-
Marrow returns what broke last time, what worked, and what to watch for. Follow the recommendedNext field.
|
|
631
|
+
One tool. Two calls max. Zero loop management.
|
|
277
632
|
|
|
278
|
-
|
|
633
|
+
The minimum loop:
|
|
634
|
+
1. Before any external action: \`marrow_auto({ action: "...what you are about to do" })\`
|
|
635
|
+
2. After the action: \`marrow_auto({ action: "...", outcome: "...what happened", success: true/false })\`
|
|
279
636
|
|
|
280
|
-
|
|
281
|
-
- description: what you did
|
|
282
|
-
- success: true or false
|
|
283
|
-
- outcome: one line of what happened
|
|
637
|
+
If you only have time for one call: \`marrow_auto({ action: "did X", outcome: "result", success: true })\` — that's the whole loop.
|
|
284
638
|
|
|
285
|
-
|
|
639
|
+
You never need think + commit separately. \`marrow_auto\` handles everything.
|
|
286
640
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
1. **Session start:** Call marrow_orient FIRST. It surfaces failure warnings so you avoid known mistakes.
|
|
290
|
-
2. **Before acting:** Call marrow_think with what you're about to do.
|
|
291
|
-
3. **After acting:** Call marrow_commit with what happened.
|
|
641
|
+
All Marrow API calls run in the background — \`marrow_auto\` returns instantly with cached warnings so you never wait.
|
|
292
642
|
|
|
293
643
|
## Query the hive
|
|
294
644
|
|
|
@@ -297,16 +647,27 @@ Use marrow_ask to query your decision history in plain English:
|
|
|
297
647
|
- "what's our failure rate on security tasks?"
|
|
298
648
|
- "what worked last time we published?"
|
|
299
649
|
|
|
650
|
+
## Advanced mode: fine-grained control
|
|
651
|
+
|
|
652
|
+
For cases where you need explicit control over the orient → think → act → commit loop:
|
|
653
|
+
|
|
654
|
+
1. **Session start:** Call marrow_orient FIRST. It surfaces failure warnings so you avoid known mistakes.
|
|
655
|
+
2. **Before acting:** Call marrow_think with what you're about to do. Returns pattern insights and recommendedNext — follow it.
|
|
656
|
+
3. **After acting:** Call marrow_commit with what happened.
|
|
657
|
+
4. **Shortcut:** marrow_run handles orient → think → commit in a single awaited call.
|
|
658
|
+
|
|
659
|
+
Most agents never need advanced mode. \`marrow_auto\` covers 99% of cases.
|
|
660
|
+
|
|
300
661
|
## Why you must not skip this
|
|
301
662
|
|
|
302
663
|
If you skip logging, the hive can't protect you or future agents from this mistake.
|
|
303
664
|
Every decision you log compounds. Failure rates drop. Patterns emerge. The next session starts smarter.
|
|
304
665
|
Marrow tells you what went wrong last time before you do it again. But only if you logged it.
|
|
305
666
|
|
|
306
|
-
This is not optional overhead — it's how you stop repeating the same failures
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
]
|
|
667
|
+
This is not optional overhead — it's how you stop repeating the same failures.`,
|
|
668
|
+
},
|
|
669
|
+
},
|
|
670
|
+
],
|
|
310
671
|
});
|
|
311
672
|
return;
|
|
312
673
|
}
|
|
@@ -318,16 +679,9 @@ This is not optional overhead — it's how you stop repeating the same failures.
|
|
|
318
679
|
const toolName = params?.name;
|
|
319
680
|
const args = (params?.arguments || {});
|
|
320
681
|
if (toolName === 'marrow_orient') {
|
|
321
|
-
const result = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, {
|
|
322
|
-
taskType: args.taskType,
|
|
323
|
-
}, SESSION_ID);
|
|
682
|
+
const result = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, { taskType: args.taskType }, SESSION_ID);
|
|
324
683
|
success(id, {
|
|
325
|
-
content: [
|
|
326
|
-
{
|
|
327
|
-
type: 'text',
|
|
328
|
-
text: JSON.stringify(result, null, 2),
|
|
329
|
-
},
|
|
330
|
-
],
|
|
684
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
331
685
|
});
|
|
332
686
|
return;
|
|
333
687
|
}
|
|
@@ -343,33 +697,27 @@ This is not optional overhead — it's how you stop repeating the same failures.
|
|
|
343
697
|
// Refresh orient warnings every 5th think call
|
|
344
698
|
thinkCallCount++;
|
|
345
699
|
if (thinkCallCount % 5 === 0) {
|
|
346
|
-
refreshOrientWarnings()
|
|
700
|
+
refreshOrientWarnings();
|
|
347
701
|
}
|
|
348
|
-
// Inject orient warnings into
|
|
702
|
+
// Inject cached orient warnings into intelligence.insights
|
|
349
703
|
if (cachedOrientWarnings.length > 0) {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
...cachedOrientWarnings.map(w => ({
|
|
704
|
+
const existingInsights = result.intelligence?.insights || [];
|
|
705
|
+
result.intelligence.insights = [
|
|
706
|
+
...cachedOrientWarnings.map((w) => ({
|
|
354
707
|
type: 'failure_pattern',
|
|
355
|
-
summary:
|
|
708
|
+
summary: w.message,
|
|
356
709
|
action: `Review past ${w.type} failures before proceeding`,
|
|
357
|
-
severity: w.failureRate > 0.4 ? 'critical' : 'warning',
|
|
710
|
+
severity: (w.failureRate > 0.4 ? 'critical' : 'warning'),
|
|
358
711
|
count: 0,
|
|
359
712
|
})),
|
|
360
|
-
...
|
|
713
|
+
...existingInsights,
|
|
361
714
|
];
|
|
362
715
|
}
|
|
363
|
-
// Track for auto-commit
|
|
716
|
+
// Track for auto-commit
|
|
364
717
|
lastDecisionId = result.decision_id;
|
|
365
718
|
lastCommitted = false;
|
|
366
719
|
success(id, {
|
|
367
|
-
content: [
|
|
368
|
-
{
|
|
369
|
-
type: 'text',
|
|
370
|
-
text: JSON.stringify(result, null, 2),
|
|
371
|
-
},
|
|
372
|
-
],
|
|
720
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
373
721
|
});
|
|
374
722
|
return;
|
|
375
723
|
}
|
|
@@ -381,108 +729,209 @@ This is not optional overhead — it's how you stop repeating the same failures.
|
|
|
381
729
|
caused_by: args.caused_by,
|
|
382
730
|
}, SESSION_ID);
|
|
383
731
|
lastCommitted = true;
|
|
732
|
+
lastDecisionId = null;
|
|
384
733
|
success(id, {
|
|
385
|
-
content: [
|
|
386
|
-
{
|
|
387
|
-
type: 'text',
|
|
388
|
-
text: JSON.stringify(result, null, 2),
|
|
389
|
-
},
|
|
390
|
-
],
|
|
734
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
391
735
|
});
|
|
392
736
|
return;
|
|
393
737
|
}
|
|
394
738
|
if (toolName === 'marrow_run') {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const runSuccess = args.success;
|
|
398
|
-
const outcome = args.outcome;
|
|
739
|
+
// marrow_run = orient + think + commit in one call
|
|
740
|
+
await (0, index_1.marrowOrient)(API_KEY, BASE_URL, undefined, SESSION_ID);
|
|
399
741
|
const thinkResult = await (0, index_1.marrowThink)(API_KEY, BASE_URL, {
|
|
400
|
-
action: description,
|
|
401
|
-
type,
|
|
742
|
+
action: args.description,
|
|
743
|
+
type: args.type || 'general',
|
|
402
744
|
}, SESSION_ID);
|
|
403
745
|
const commitResult = await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
404
746
|
decision_id: thinkResult.decision_id,
|
|
405
|
-
success:
|
|
406
|
-
outcome,
|
|
747
|
+
success: args.success ?? true,
|
|
748
|
+
outcome: args.outcome,
|
|
407
749
|
}, SESSION_ID);
|
|
408
|
-
lastCommitted = true;
|
|
409
|
-
lastDecisionId = thinkResult.decision_id;
|
|
410
750
|
success(id, {
|
|
411
751
|
content: [
|
|
412
752
|
{
|
|
413
753
|
type: 'text',
|
|
414
|
-
text: JSON.stringify({
|
|
415
|
-
decision_id: thinkResult.decision_id,
|
|
416
|
-
success_rate: commitResult.success_rate,
|
|
417
|
-
insight: commitResult.insight,
|
|
418
|
-
intelligence: thinkResult.intelligence,
|
|
419
|
-
}, null, 2),
|
|
754
|
+
text: JSON.stringify({ think: thinkResult, commit: commitResult }, null, 2),
|
|
420
755
|
},
|
|
421
756
|
],
|
|
422
757
|
});
|
|
423
758
|
return;
|
|
424
759
|
}
|
|
760
|
+
if (toolName === 'marrow_auto') {
|
|
761
|
+
// marrow_auto = fire-and-forget background logging
|
|
762
|
+
// Return immediately with cached orient warnings, API calls happen in background
|
|
763
|
+
const action = args.action;
|
|
764
|
+
const outcome = args.outcome;
|
|
765
|
+
const outcomeSuccess = args.success ?? true;
|
|
766
|
+
const type = args.type || 'general';
|
|
767
|
+
// Return cached warnings immediately
|
|
768
|
+
const response = {
|
|
769
|
+
action,
|
|
770
|
+
outcome: outcome || 'pending',
|
|
771
|
+
warnings: cachedOrientWarnings.map(formatWarningActionably),
|
|
772
|
+
};
|
|
773
|
+
// Fire-and-forget the actual API calls
|
|
774
|
+
(async () => {
|
|
775
|
+
try {
|
|
776
|
+
if (!outcome) {
|
|
777
|
+
// Intent only
|
|
778
|
+
await (0, index_1.marrowThink)(API_KEY, BASE_URL, { action, type }, SESSION_ID);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
// Full loop
|
|
782
|
+
const thinkResult = await (0, index_1.marrowThink)(API_KEY, BASE_URL, { action, type }, SESSION_ID);
|
|
783
|
+
await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
784
|
+
decision_id: thinkResult.decision_id,
|
|
785
|
+
success: outcomeSuccess,
|
|
786
|
+
outcome,
|
|
787
|
+
}, SESSION_ID);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
catch {
|
|
791
|
+
// Silently fail - auto is best-effort
|
|
792
|
+
}
|
|
793
|
+
})();
|
|
794
|
+
success(id, {
|
|
795
|
+
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
796
|
+
});
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
425
799
|
if (toolName === 'marrow_ask') {
|
|
426
|
-
const
|
|
427
|
-
if (!query) {
|
|
428
|
-
error(id, -32602, 'query parameter is required');
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
const result = await (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query }, SESSION_ID);
|
|
800
|
+
const result = await (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query: args.query }, SESSION_ID);
|
|
432
801
|
success(id, {
|
|
433
|
-
content: [
|
|
434
|
-
{
|
|
435
|
-
type: 'text',
|
|
436
|
-
text: JSON.stringify(result, null, 2),
|
|
437
|
-
},
|
|
438
|
-
],
|
|
802
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
439
803
|
});
|
|
440
804
|
return;
|
|
441
805
|
}
|
|
442
806
|
if (toolName === 'marrow_status') {
|
|
443
807
|
const result = await (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID);
|
|
444
808
|
success(id, {
|
|
445
|
-
content: [
|
|
446
|
-
{
|
|
447
|
-
type: 'text',
|
|
448
|
-
text: JSON.stringify(result, null, 2),
|
|
449
|
-
},
|
|
450
|
-
],
|
|
809
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
451
810
|
});
|
|
452
811
|
return;
|
|
453
812
|
}
|
|
454
|
-
|
|
813
|
+
// Memory control tools
|
|
814
|
+
if (toolName === 'marrow_list_memories') {
|
|
815
|
+
const result = await marrowListMemories(API_KEY, BASE_URL, {
|
|
816
|
+
status: args.status,
|
|
817
|
+
query: args.query,
|
|
818
|
+
limit: args.limit,
|
|
819
|
+
agentId: args.agentId,
|
|
820
|
+
}, SESSION_ID);
|
|
821
|
+
success(id, {
|
|
822
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
823
|
+
});
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (toolName === 'marrow_get_memory') {
|
|
827
|
+
const result = await marrowGetMemory(API_KEY, BASE_URL, args.id, SESSION_ID);
|
|
828
|
+
success(id, {
|
|
829
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
830
|
+
});
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
if (toolName === 'marrow_update_memory') {
|
|
834
|
+
const result = await marrowUpdateMemory(API_KEY, BASE_URL, args.id, {
|
|
835
|
+
text: args.text,
|
|
836
|
+
source: args.source,
|
|
837
|
+
tags: args.tags,
|
|
838
|
+
actor: args.actor,
|
|
839
|
+
note: args.note,
|
|
840
|
+
}, SESSION_ID);
|
|
841
|
+
success(id, {
|
|
842
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
843
|
+
});
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (toolName === 'marrow_delete_memory') {
|
|
847
|
+
const result = await marrowDeleteMemory(API_KEY, BASE_URL, args.id, { actor: args.actor, note: args.note }, SESSION_ID);
|
|
848
|
+
success(id, {
|
|
849
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
850
|
+
});
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (toolName === 'marrow_mark_outdated') {
|
|
854
|
+
const result = await marrowMarkOutdated(API_KEY, BASE_URL, args.id, { actor: args.actor, note: args.note }, SESSION_ID);
|
|
855
|
+
success(id, {
|
|
856
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
857
|
+
});
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (toolName === 'marrow_supersede_memory') {
|
|
861
|
+
const result = await marrowSupersedeMemory(API_KEY, BASE_URL, args.id, {
|
|
862
|
+
text: args.text,
|
|
863
|
+
source: args.source,
|
|
864
|
+
tags: args.tags,
|
|
865
|
+
actor: args.actor,
|
|
866
|
+
note: args.note,
|
|
867
|
+
}, SESSION_ID);
|
|
868
|
+
success(id, {
|
|
869
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
870
|
+
});
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (toolName === 'marrow_share_memory') {
|
|
874
|
+
const result = await marrowShareMemory(API_KEY, BASE_URL, args.id, args.agentIds || [], args.actor, SESSION_ID);
|
|
875
|
+
success(id, {
|
|
876
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
877
|
+
});
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (toolName === 'marrow_export_memories') {
|
|
881
|
+
const result = await marrowExportMemories(API_KEY, BASE_URL, {
|
|
882
|
+
format: args.format,
|
|
883
|
+
status: args.status,
|
|
884
|
+
tags: args.tags,
|
|
885
|
+
}, SESSION_ID);
|
|
886
|
+
success(id, {
|
|
887
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
888
|
+
});
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (toolName === 'marrow_import_memories') {
|
|
892
|
+
const result = await marrowImportMemories(API_KEY, BASE_URL, args.memories || [], args.mode || 'merge', SESSION_ID);
|
|
893
|
+
success(id, {
|
|
894
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
895
|
+
});
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (toolName === 'marrow_retrieve_memories') {
|
|
899
|
+
const result = await marrowRetrieveMemories(API_KEY, BASE_URL, args.query, {
|
|
900
|
+
limit: args.limit,
|
|
901
|
+
from: args.from,
|
|
902
|
+
to: args.to,
|
|
903
|
+
tags: args.tags,
|
|
904
|
+
source: args.source,
|
|
905
|
+
status: args.status,
|
|
906
|
+
shared: args.shared,
|
|
907
|
+
}, SESSION_ID);
|
|
908
|
+
success(id, {
|
|
909
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
910
|
+
});
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
error(id, -32601, `Method not found: ${toolName}`);
|
|
455
914
|
return;
|
|
456
915
|
}
|
|
457
|
-
// Unknown method
|
|
458
916
|
error(id, -32601, `Method not found: ${method}`);
|
|
459
917
|
}
|
|
460
918
|
catch (err) {
|
|
461
|
-
const
|
|
462
|
-
error(id, -
|
|
919
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
920
|
+
error(id, -32000, message);
|
|
463
921
|
}
|
|
464
922
|
}
|
|
465
|
-
// stdio
|
|
923
|
+
// MCP stdio loop
|
|
466
924
|
const rl = readline.createInterface({
|
|
467
925
|
input: process.stdin,
|
|
468
926
|
output: process.stdout,
|
|
469
|
-
terminal: false,
|
|
470
927
|
});
|
|
471
|
-
rl.on('line', (line) => {
|
|
472
|
-
const trimmed = line.trim();
|
|
473
|
-
if (!trimmed)
|
|
474
|
-
return;
|
|
928
|
+
rl.on('line', async (line) => {
|
|
475
929
|
try {
|
|
476
|
-
const
|
|
477
|
-
handleRequest(
|
|
478
|
-
process.stderr.write(`Unhandled error: ${err}\n`);
|
|
479
|
-
});
|
|
930
|
+
const msg = JSON.parse(line);
|
|
931
|
+
await handleRequest(msg);
|
|
480
932
|
}
|
|
481
|
-
catch {
|
|
482
|
-
process.stderr.write(`
|
|
933
|
+
catch (err) {
|
|
934
|
+
process.stderr.write(`[marrow] Parse error: ${err}\n`);
|
|
483
935
|
}
|
|
484
936
|
});
|
|
485
|
-
rl.on('close', () => {
|
|
486
|
-
process.exit(0);
|
|
487
|
-
});
|
|
488
937
|
//# sourceMappingURL=cli.js.map
|