@mndrk/memx 0.3.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/mcp.js ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for mem - exposes memory operations as tools
5
+ *
6
+ * Usage: mem mcp [--dir <path>]
7
+ *
8
+ * Tools exposed:
9
+ * - mem_context: Get full context (goal, state, learnings)
10
+ * - mem_checkpoint: Save a progress checkpoint
11
+ * - mem_learn: Add a learning
12
+ * - mem_next: Set next step
13
+ * - mem_status: Get current status
14
+ * - mem_stuck: Mark/clear blocker
15
+ */
16
+
17
+ const { spawnSync } = require('child_process');
18
+ const readline = require('readline');
19
+
20
+ // Run mem command and return output
21
+ function runMem(args, cwd) {
22
+ const result = spawnSync('mem', args, {
23
+ cwd: cwd || process.cwd(),
24
+ encoding: 'utf8',
25
+ stdio: ['pipe', 'pipe', 'pipe']
26
+ });
27
+ return {
28
+ success: result.status === 0,
29
+ output: result.stdout?.trim() || '',
30
+ error: result.stderr?.trim() || ''
31
+ };
32
+ }
33
+
34
+ // MCP Protocol handler
35
+ class MCPServer {
36
+ constructor(workDir) {
37
+ this.workDir = workDir || process.cwd();
38
+ }
39
+
40
+ // List available tools
41
+ listTools() {
42
+ return {
43
+ tools: [
44
+ {
45
+ name: 'mem_context',
46
+ description: 'Get full memory context - goal, state, learnings, playbook. Use on wake to hydrate.',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {},
50
+ required: []
51
+ }
52
+ },
53
+ {
54
+ name: 'mem_status',
55
+ description: 'Get current task status summary',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {},
59
+ required: []
60
+ }
61
+ },
62
+ {
63
+ name: 'mem_checkpoint',
64
+ description: 'Save a progress checkpoint',
65
+ inputSchema: {
66
+ type: 'object',
67
+ properties: {
68
+ message: {
69
+ type: 'string',
70
+ description: 'Checkpoint message describing what was accomplished'
71
+ }
72
+ },
73
+ required: ['message']
74
+ }
75
+ },
76
+ {
77
+ name: 'mem_learn',
78
+ description: 'Add a learning or insight',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ insight: {
83
+ type: 'string',
84
+ description: 'The learning or insight to record'
85
+ },
86
+ global: {
87
+ type: 'boolean',
88
+ description: 'If true, add to global playbook instead of task memory'
89
+ }
90
+ },
91
+ required: ['insight']
92
+ }
93
+ },
94
+ {
95
+ name: 'mem_next',
96
+ description: 'Set the next step to work on',
97
+ inputSchema: {
98
+ type: 'object',
99
+ properties: {
100
+ step: {
101
+ type: 'string',
102
+ description: 'Description of the next step'
103
+ }
104
+ },
105
+ required: ['step']
106
+ }
107
+ },
108
+ {
109
+ name: 'mem_stuck',
110
+ description: 'Mark task as stuck with a blocker, or clear blockers',
111
+ inputSchema: {
112
+ type: 'object',
113
+ properties: {
114
+ reason: {
115
+ type: 'string',
116
+ description: 'Reason for being stuck, or "clear" to remove blocker'
117
+ }
118
+ },
119
+ required: ['reason']
120
+ }
121
+ },
122
+ {
123
+ name: 'mem_goal',
124
+ description: 'Get or set the current goal',
125
+ inputSchema: {
126
+ type: 'object',
127
+ properties: {
128
+ goal: {
129
+ type: 'string',
130
+ description: 'New goal to set (omit to just get current goal)'
131
+ }
132
+ },
133
+ required: []
134
+ }
135
+ },
136
+ {
137
+ name: 'mem_tasks',
138
+ description: 'List all tasks (branches)',
139
+ inputSchema: {
140
+ type: 'object',
141
+ properties: {},
142
+ required: []
143
+ }
144
+ },
145
+ {
146
+ name: 'mem_switch',
147
+ description: 'Switch to a different task',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ task: {
152
+ type: 'string',
153
+ description: 'Task name to switch to'
154
+ }
155
+ },
156
+ required: ['task']
157
+ }
158
+ }
159
+ ]
160
+ };
161
+ }
162
+
163
+ // Execute a tool
164
+ callTool(name, args) {
165
+ switch (name) {
166
+ case 'mem_context':
167
+ return runMem(['context'], this.workDir);
168
+
169
+ case 'mem_status':
170
+ return runMem(['status'], this.workDir);
171
+
172
+ case 'mem_checkpoint':
173
+ return runMem(['checkpoint', args.message], this.workDir);
174
+
175
+ case 'mem_learn':
176
+ const learnArgs = args.global ? ['learn', '-g', args.insight] : ['learn', args.insight];
177
+ return runMem(learnArgs, this.workDir);
178
+
179
+ case 'mem_next':
180
+ return runMem(['next', args.step], this.workDir);
181
+
182
+ case 'mem_stuck':
183
+ return runMem(['stuck', args.reason], this.workDir);
184
+
185
+ case 'mem_goal':
186
+ return args.goal
187
+ ? runMem(['goal', args.goal], this.workDir)
188
+ : runMem(['goal'], this.workDir);
189
+
190
+ case 'mem_tasks':
191
+ return runMem(['tasks'], this.workDir);
192
+
193
+ case 'mem_switch':
194
+ return runMem(['switch', args.task], this.workDir);
195
+
196
+ default:
197
+ return { success: false, error: `Unknown tool: ${name}` };
198
+ }
199
+ }
200
+
201
+ // Handle incoming JSON-RPC message
202
+ handleMessage(message) {
203
+ const { id, method, params } = message;
204
+
205
+ switch (method) {
206
+ case 'initialize':
207
+ return {
208
+ jsonrpc: '2.0',
209
+ id,
210
+ result: {
211
+ protocolVersion: '2024-11-05',
212
+ capabilities: { tools: {} },
213
+ serverInfo: {
214
+ name: 'mem',
215
+ version: '0.1.0'
216
+ }
217
+ }
218
+ };
219
+
220
+ case 'tools/list':
221
+ return {
222
+ jsonrpc: '2.0',
223
+ id,
224
+ result: this.listTools()
225
+ };
226
+
227
+ case 'tools/call':
228
+ const { name, arguments: toolArgs } = params;
229
+ const result = this.callTool(name, toolArgs || {});
230
+ return {
231
+ jsonrpc: '2.0',
232
+ id,
233
+ result: {
234
+ content: [
235
+ {
236
+ type: 'text',
237
+ text: result.success ? result.output : `Error: ${result.error}`
238
+ }
239
+ ],
240
+ isError: !result.success
241
+ }
242
+ };
243
+
244
+ case 'notifications/initialized':
245
+ // Client ready, no response needed
246
+ return null;
247
+
248
+ default:
249
+ return {
250
+ jsonrpc: '2.0',
251
+ id,
252
+ error: {
253
+ code: -32601,
254
+ message: `Method not found: ${method}`
255
+ }
256
+ };
257
+ }
258
+ }
259
+
260
+ // Start the server (stdio transport)
261
+ start() {
262
+ const rl = readline.createInterface({
263
+ input: process.stdin,
264
+ output: process.stdout,
265
+ terminal: false
266
+ });
267
+
268
+ rl.on('line', (line) => {
269
+ try {
270
+ const message = JSON.parse(line);
271
+ const response = this.handleMessage(message);
272
+ if (response) {
273
+ console.log(JSON.stringify(response));
274
+ }
275
+ } catch (err) {
276
+ console.log(JSON.stringify({
277
+ jsonrpc: '2.0',
278
+ id: null,
279
+ error: {
280
+ code: -32700,
281
+ message: 'Parse error'
282
+ }
283
+ }));
284
+ }
285
+ });
286
+
287
+ rl.on('close', () => {
288
+ process.exit(0);
289
+ });
290
+ }
291
+ }
292
+
293
+ // Run if called directly
294
+ if (require.main === module) {
295
+ const args = process.argv.slice(2);
296
+ let workDir = process.cwd();
297
+
298
+ // Parse --dir argument
299
+ const dirIndex = args.indexOf('--dir');
300
+ if (dirIndex !== -1 && args[dirIndex + 1]) {
301
+ workDir = args[dirIndex + 1];
302
+ }
303
+
304
+ const server = new MCPServer(workDir);
305
+ server.start();
306
+ }
307
+
308
+ module.exports = { MCPServer };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@mndrk/memx",
3
+ "version": "0.3.0",
4
+ "description": "Persistent memory for AI agents - git-backed, branch-per-task, CLI interface",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "mem": "./index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "ai",
14
+ "agent",
15
+ "memory",
16
+ "cli",
17
+ "persistent",
18
+ "llm",
19
+ "context"
20
+ ],
21
+ "author": "mnrdk",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/ramarlina/memx"
26
+ }
27
+ }