@memoraone/mcp 0.1.5 → 0.1.6

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/dist/cli.cjs ADDED
@@ -0,0 +1,797 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/index.ts
26
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
27
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
28
+
29
+ // src/config.ts
30
+ var process2 = __toESM(require("process"), 1);
31
+ var fs = __toESM(require("fs"), 1);
32
+ var path = __toESM(require("path"), 1);
33
+ var import_dotenv = __toESM(require("dotenv"), 1);
34
+ var import_v4 = require("zod/v4");
35
+
36
+ // src/configUtils.ts
37
+ var DEFAULT_API_URL = "https://api.memoraone.com";
38
+ var DEV_API_URL = "http://localhost:3001";
39
+ function resolveApiUrl(env2) {
40
+ const explicitUrl = env2.MEMORAONE_API_URL?.trim();
41
+ if (explicitUrl) {
42
+ return explicitUrl;
43
+ }
44
+ if (env2.MEMORAONE_DEV_MODE === "1") {
45
+ return DEV_API_URL;
46
+ }
47
+ return DEFAULT_API_URL;
48
+ }
49
+
50
+ // src/config.ts
51
+ var dotenvPath = path.resolve(process2.cwd(), ".env");
52
+ if (fs.existsSync(dotenvPath)) {
53
+ try {
54
+ import_dotenv.default.config({ path: dotenvPath });
55
+ } catch (err) {
56
+ process2.stderr.write("[memoraone-mcp] Failed to load .env: " + String(err) + "\n");
57
+ }
58
+ }
59
+ var EnvSchema = import_v4.z.object({
60
+ MEMORAONE_API_URL: import_v4.z.string().url().optional(),
61
+ MEMORAONE_API_KEY: import_v4.z.string().min(1),
62
+ MEMORAONE_DEV_MODE: import_v4.z.string().min(1).optional(),
63
+ MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
64
+ MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
65
+ MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
66
+ MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
67
+ MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
68
+ MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
69
+ });
70
+ var requiredEnvVars = ["MEMORAONE_API_KEY"];
71
+ var missingEnvVars = requiredEnvVars.filter((key) => {
72
+ const value = process2.env[key];
73
+ return value === void 0 || value.trim() === "";
74
+ });
75
+ if (missingEnvVars.length > 0) {
76
+ for (const key of missingEnvVars) {
77
+ process2.stderr.write(`Missing ${key}
78
+ `);
79
+ }
80
+ process2.exit(1);
81
+ }
82
+ var parsed = EnvSchema.safeParse(process2.env);
83
+ var resolvedApiUrl = resolveApiUrl(process2.env);
84
+ if (!parsed.success) {
85
+ const formatted = parsed.error.format();
86
+ process2.stderr.write(
87
+ "[memoraone-mcp] Invalid environment variables " + JSON.stringify(formatted) + "\n"
88
+ );
89
+ throw new Error("Config validation failed");
90
+ }
91
+ var parseBooleanFlag = (value, defaultValue) => {
92
+ if (value === void 0) {
93
+ return defaultValue;
94
+ }
95
+ const normalized = value.trim().toLowerCase();
96
+ if (["1", "true", "yes", "on"].includes(normalized)) {
97
+ return true;
98
+ }
99
+ if (["0", "false", "no", "off"].includes(normalized)) {
100
+ return false;
101
+ }
102
+ return defaultValue;
103
+ };
104
+ var config = {
105
+ apiUrl: resolvedApiUrl.replace(/\/+$/, ""),
106
+ apiKey: parsed.data.MEMORAONE_API_KEY,
107
+ agentName: parsed.data.MEMORAONE_AGENT_NAME ?? "cursor",
108
+ agentType: parsed.data.MEMORAONE_AGENT_TYPE ?? "agent",
109
+ source: parsed.data.MEMORAONE_SOURCE ?? "cursor",
110
+ worklogEnabled: parseBooleanFlag(parsed.data.MEMORAONE_WORKLOG, true),
111
+ heartbeatEnabled: parseBooleanFlag(parsed.data.MEMORAONE_HEARTBEAT, true),
112
+ heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "30000", 10)
113
+ };
114
+
115
+ // src/client/memoraClient.ts
116
+ async function requestJson(url, method, headers, body) {
117
+ const res = await fetch(url, {
118
+ method,
119
+ headers,
120
+ body: method === "GET" ? void 0 : JSON.stringify(body ?? {})
121
+ });
122
+ const text = await res.text();
123
+ return {
124
+ status: res.status,
125
+ statusText: res.statusText,
126
+ ok: res.ok,
127
+ text
128
+ };
129
+ }
130
+ var MemoraOneHttpError = class extends Error {
131
+ constructor(status, statusText, body) {
132
+ super(`MemoraOne request failed: ${status} ${statusText}`);
133
+ this.name = "MemoraOneHttpError";
134
+ this.status = status;
135
+ this.body = body;
136
+ }
137
+ };
138
+ var MemoraClient = class {
139
+ constructor(cfg) {
140
+ this.baseUrl = cfg.apiUrl;
141
+ this.apiKey = cfg.apiKey;
142
+ }
143
+ buildHeaders(options) {
144
+ return {
145
+ "content-type": "application/json",
146
+ "x-api-key": this.apiKey,
147
+ ...options?.headers ?? {}
148
+ };
149
+ }
150
+ async post(path2, body, options) {
151
+ const url = `${this.baseUrl}${path2.startsWith("/") ? path2 : `/${path2}`}`;
152
+ process.stderr.write(
153
+ `[memoraone-mcp] http url=${url} apiKeyPrefix=${String(this.apiKey).slice(0, 8)} apiKeyLen=${String(this.apiKey).length}
154
+ `
155
+ );
156
+ const res = await requestJson(url, "POST", this.buildHeaders(options), body);
157
+ if (!res.ok) {
158
+ throw new MemoraOneHttpError(res.status, res.statusText, res.text);
159
+ }
160
+ return res.text ? JSON.parse(res.text) : null;
161
+ }
162
+ async get(path2, options) {
163
+ const url = `${this.baseUrl}${path2.startsWith("/") ? path2 : `/${path2}`}`;
164
+ const res = await requestJson(url, "GET", this.buildHeaders(options));
165
+ if (!res.ok) {
166
+ throw new MemoraOneHttpError(res.status, res.statusText, res.text);
167
+ }
168
+ return res.text ? JSON.parse(res.text) : null;
169
+ }
170
+ };
171
+ var memoraClient_default = MemoraClient;
172
+
173
+ // src/tools/postEvent.ts
174
+ var import_v42 = require("zod/v4");
175
+ var postEventShape = {
176
+ kind: import_v42.z.string().min(1),
177
+ actor: import_v42.z.object({ identifier: import_v42.z.string().min(1) }),
178
+ content: import_v42.z.record(import_v42.z.string(), import_v42.z.any()),
179
+ metadata: import_v42.z.record(import_v42.z.string(), import_v42.z.any()).optional()
180
+ };
181
+
182
+ // src/tools/askWithMemory.ts
183
+ var import_v43 = require("zod/v4");
184
+ var askWithMemoryShape = {
185
+ question: import_v43.z.string().min(1),
186
+ code_context: import_v43.z.object({
187
+ file_path: import_v43.z.string().optional(),
188
+ selected_text: import_v43.z.string().optional(),
189
+ language: import_v43.z.string().optional()
190
+ }).optional()
191
+ };
192
+
193
+ // src/tools/logIntent.ts
194
+ var import_v44 = require("zod/v4");
195
+ var logIntentShape = {
196
+ intent: import_v44.z.enum(["task", "decision"]),
197
+ message: import_v44.z.string().min(1),
198
+ context: import_v44.z.record(import_v44.z.string(), import_v44.z.any()).optional(),
199
+ intent_source: import_v44.z.string().optional().default("cursor_chat"),
200
+ run_id: import_v44.z.string().min(1).optional()
201
+ };
202
+
203
+ // src/tools/logChangeSummary.ts
204
+ var import_v45 = require("zod/v4");
205
+ var logChangeSummaryShape = {
206
+ summary: import_v45.z.string().min(1),
207
+ scope: import_v45.z.string().min(1).optional(),
208
+ files: import_v45.z.array(import_v45.z.string().min(1)).optional(),
209
+ stats: import_v45.z.object({
210
+ files: import_v45.z.number().int().nonnegative().optional(),
211
+ add: import_v45.z.number().int().nonnegative().optional(),
212
+ del: import_v45.z.number().int().nonnegative().optional()
213
+ }).optional(),
214
+ commit: import_v45.z.string().min(1).optional(),
215
+ run_id: import_v45.z.string().min(1).optional()
216
+ };
217
+
218
+ // src/tools/logToolResult.ts
219
+ var import_v46 = require("zod/v4");
220
+ var logToolResultShape = {
221
+ tool: import_v46.z.string().min(1),
222
+ status: import_v46.z.enum(["ok", "error", "partial"]),
223
+ summary: import_v46.z.string().min(1),
224
+ run_id: import_v46.z.string().min(1).optional(),
225
+ duration_ms: import_v46.z.number().int().nonnegative().optional(),
226
+ error_code: import_v46.z.string().min(1).optional(),
227
+ error_message: import_v46.z.string().min(1).optional(),
228
+ error_kind: import_v46.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
229
+ stats: import_v46.z.record(import_v46.z.string(), import_v46.z.any()).optional()
230
+ };
231
+
232
+ // src/tools/logCommand.ts
233
+ var import_v47 = require("zod/v4");
234
+ var logCommandShape = {
235
+ cmd: import_v47.z.string().min(1),
236
+ summary: import_v47.z.string().min(1),
237
+ cwd: import_v47.z.string().min(1).optional(),
238
+ exit_code: import_v47.z.number().int().optional(),
239
+ duration_ms: import_v47.z.number().int().nonnegative().optional(),
240
+ run_id: import_v47.z.string().min(1).optional(),
241
+ stats: import_v47.z.record(import_v47.z.string(), import_v47.z.any()).optional()
242
+ };
243
+
244
+ // src/tools/listProjects.ts
245
+ var listProjectsShape = {};
246
+
247
+ // src/tools/setProject.ts
248
+ var import_v48 = require("zod/v4");
249
+ var setProjectShape = {
250
+ projectId: import_v48.z.string().min(1)
251
+ };
252
+
253
+ // src/tools/handlers/postEvent.ts
254
+ var import_v49 = require("zod/v4");
255
+
256
+ // src/runContext.ts
257
+ var crypto = __toESM(require("crypto"), 1);
258
+ var currentRunId = null;
259
+ var currentProjectId = null;
260
+ function setCurrentRunId(id) {
261
+ currentRunId = id;
262
+ }
263
+ function getCurrentProjectId() {
264
+ return currentProjectId;
265
+ }
266
+ function setCurrentProjectId(id) {
267
+ currentProjectId = id;
268
+ }
269
+ function resolveRunId(passed) {
270
+ if (passed) {
271
+ return passed;
272
+ }
273
+ return currentRunId;
274
+ }
275
+ function generateRunId() {
276
+ return crypto.randomBytes(16).toString("hex");
277
+ }
278
+
279
+ // src/tools/handlers/postEvent.ts
280
+ var postEventInputSchema = import_v49.z.object({
281
+ kind: import_v49.z.string().min(1),
282
+ actor: import_v49.z.object({ identifier: import_v49.z.string().min(1) }),
283
+ content: import_v49.z.record(import_v49.z.string(), import_v49.z.any()),
284
+ metadata: import_v49.z.record(import_v49.z.string(), import_v49.z.any()).optional()
285
+ });
286
+ async function handlePostEvent(client, args) {
287
+ const parsed2 = postEventInputSchema.parse(args ?? {});
288
+ const projectId = getCurrentProjectId();
289
+ if (!projectId) {
290
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
291
+ }
292
+ const body = {
293
+ kind: parsed2.kind,
294
+ actor: { type: config.agentType, identifier: parsed2.actor.identifier },
295
+ content: parsed2.content,
296
+ metadata: {
297
+ source: config.source,
298
+ ...parsed2.metadata
299
+ }
300
+ };
301
+ await client.post("/timeline/events", body, { projectId });
302
+ return { ok: true, forwarded: true };
303
+ }
304
+
305
+ // src/tools/handlers/askWithMemory.ts
306
+ var import_v410 = require("zod/v4");
307
+ var askWithMemoryInputSchema = import_v410.z.object({
308
+ question: import_v410.z.string().min(1),
309
+ code_context: import_v410.z.object({
310
+ file_path: import_v410.z.string().optional(),
311
+ selected_text: import_v410.z.string().optional(),
312
+ language: import_v410.z.string().optional()
313
+ }).optional()
314
+ });
315
+ function isAskWithMemoryResponse(value) {
316
+ return typeof value === "object" && value !== null && typeof value.answer === "string" && value.answer !== "";
317
+ }
318
+ async function handleAskWithMemory(client, args) {
319
+ const parsed2 = askWithMemoryInputSchema.parse(args ?? {});
320
+ const projectId = getCurrentProjectId();
321
+ if (!projectId) {
322
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
323
+ }
324
+ const payload = {
325
+ question: parsed2.question
326
+ };
327
+ if (parsed2.code_context) {
328
+ payload.code_context = parsed2.code_context;
329
+ }
330
+ const res = await client.post("/agent/ask-with-memory", payload, { projectId });
331
+ if (!isAskWithMemoryResponse(res)) {
332
+ const err = new Error("Unexpected response from MemoraOne");
333
+ err.status = 502;
334
+ err.body = res;
335
+ throw err;
336
+ }
337
+ return {
338
+ answer: res.answer,
339
+ used: res.used ?? {}
340
+ };
341
+ }
342
+
343
+ // src/tools/handlers/logIntent.ts
344
+ var import_v411 = require("zod/v4");
345
+ var logIntentInputSchema = import_v411.z.object({
346
+ intent: import_v411.z.enum(["task", "decision"]),
347
+ message: import_v411.z.string().min(1),
348
+ context: import_v411.z.record(import_v411.z.string(), import_v411.z.any()).optional(),
349
+ intent_source: import_v411.z.string().optional().default("cursor_chat"),
350
+ run_id: import_v411.z.string().min(1).optional()
351
+ });
352
+ async function handleLogIntent(client, args) {
353
+ const parsed2 = logIntentInputSchema.parse(args ?? {});
354
+ const projectId = getCurrentProjectId();
355
+ if (!projectId) {
356
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
357
+ }
358
+ const intent = parsed2.intent;
359
+ const message = parsed2.message.trim();
360
+ if (!message) {
361
+ throw new Error("message cannot be empty after trimming");
362
+ }
363
+ const intent_source = parsed2.intent_source ?? "cursor_chat";
364
+ const context = parsed2.context;
365
+ const purpose = intent;
366
+ const concept = purpose === "task" ? "concept:task" : "concept:decision";
367
+ const run_id = parsed2.run_id ?? generateRunId();
368
+ setCurrentRunId(run_id);
369
+ const body = {
370
+ kind: "note",
371
+ actor: { type: config.agentType, name: config.agentName },
372
+ concept,
373
+ // MUST be TOP-LEVEL so it populates timeline_events.concept
374
+ message,
375
+ metadata: {
376
+ source: config.source,
377
+ purpose,
378
+ // 'task' | 'decision'
379
+ intent_source: intent_source ?? "cursor_chat",
380
+ run_id,
381
+ ...context ? { context } : {}
382
+ }
383
+ };
384
+ await client.post("/timeline/events", body, { projectId });
385
+ return { ok: true, run_id };
386
+ }
387
+
388
+ // src/tools/handlers/logChangeSummary.ts
389
+ var import_v412 = require("zod/v4");
390
+ var logChangeSummaryInputSchema = import_v412.z.object({
391
+ summary: import_v412.z.string().min(1),
392
+ scope: import_v412.z.string().min(1).optional(),
393
+ files: import_v412.z.array(import_v412.z.string().min(1)).optional(),
394
+ stats: import_v412.z.object({
395
+ files: import_v412.z.number().int().nonnegative().optional(),
396
+ add: import_v412.z.number().int().nonnegative().optional(),
397
+ del: import_v412.z.number().int().nonnegative().optional()
398
+ }).optional(),
399
+ commit: import_v412.z.string().min(1).optional(),
400
+ run_id: import_v412.z.string().min(1).optional()
401
+ });
402
+ async function handleLogChangeSummary(client, args) {
403
+ const parsed2 = logChangeSummaryInputSchema.parse(args ?? {});
404
+ const projectId = getCurrentProjectId();
405
+ if (!projectId) {
406
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
407
+ }
408
+ const { summary, scope, files, stats, commit } = parsed2;
409
+ const message = summary.startsWith("CHANGE:") ? summary : `CHANGE: ${scope ?? "code"} \u2014 ${summary}`;
410
+ const run_id = resolveRunId(parsed2.run_id);
411
+ const body = {
412
+ kind: "note",
413
+ concept: "concept:change_summary",
414
+ actor: { type: config.agentType, name: config.agentName },
415
+ message,
416
+ metadata: {
417
+ source: config.source,
418
+ purpose: "change_summary",
419
+ tool: "memora_log_change_summary",
420
+ ...scope ? { scope } : {},
421
+ ...files ? { files } : {},
422
+ ...stats ? { stats } : {},
423
+ ...commit ? { commit } : {},
424
+ ...run_id ? { run_id } : {}
425
+ }
426
+ };
427
+ await client.post("/timeline/events", body, { projectId });
428
+ return { ok: true };
429
+ }
430
+
431
+ // src/tools/handlers/logToolResult.ts
432
+ var import_v413 = require("zod/v4");
433
+ var logToolResultInputSchema = import_v413.z.object({
434
+ tool: import_v413.z.string().min(1),
435
+ status: import_v413.z.enum(["ok", "error", "partial"]),
436
+ summary: import_v413.z.string().min(1),
437
+ run_id: import_v413.z.string().min(1).optional(),
438
+ duration_ms: import_v413.z.number().int().nonnegative().optional(),
439
+ error_code: import_v413.z.string().min(1).optional(),
440
+ error_message: import_v413.z.string().min(1).optional(),
441
+ error_kind: import_v413.z.enum(["infra", "logic", "auth", "rate_limit", "validation", "unknown"]).optional(),
442
+ stats: import_v413.z.record(import_v413.z.string(), import_v413.z.any()).optional()
443
+ });
444
+ async function handleLogToolResult(client, args) {
445
+ const parsed2 = logToolResultInputSchema.parse(args ?? {});
446
+ const projectId = getCurrentProjectId();
447
+ if (!projectId) {
448
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
449
+ }
450
+ const { tool, status, summary, duration_ms, error_code, error_message, error_kind, stats } = parsed2;
451
+ const message = summary.startsWith("RESULT:") ? summary : `RESULT: ${tool} \u2014 ${status} \u2014 ${summary}`;
452
+ const run_id = resolveRunId(parsed2.run_id);
453
+ const body = {
454
+ kind: "note",
455
+ concept: "concept:tool_result",
456
+ actor: { type: config.agentType, name: config.agentName },
457
+ message,
458
+ metadata: {
459
+ source: config.source,
460
+ purpose: "tool_result",
461
+ tool: "memora_log_tool_result",
462
+ tool_name: tool,
463
+ status,
464
+ ...run_id ? { run_id } : {},
465
+ ...duration_ms ? { duration_ms } : {},
466
+ ...error_code ? { error_code } : {},
467
+ ...error_message ? { error_message } : {},
468
+ ...error_kind ? { error_kind } : {},
469
+ ...stats ? { stats } : {}
470
+ }
471
+ };
472
+ await client.post("/timeline/events", body, { projectId });
473
+ return { ok: true };
474
+ }
475
+
476
+ // src/tools/handlers/logCommand.ts
477
+ var import_v414 = require("zod/v4");
478
+ var logCommandInputSchema = import_v414.z.object({
479
+ cmd: import_v414.z.string().min(1),
480
+ summary: import_v414.z.string().min(1),
481
+ cwd: import_v414.z.string().min(1).optional(),
482
+ exit_code: import_v414.z.number().int().optional(),
483
+ duration_ms: import_v414.z.number().int().nonnegative().optional(),
484
+ run_id: import_v414.z.string().min(1).optional(),
485
+ stats: import_v414.z.record(import_v414.z.string(), import_v414.z.any()).optional()
486
+ });
487
+ async function handleLogCommand(client, args) {
488
+ const parsed2 = logCommandInputSchema.parse(args ?? {});
489
+ const projectId = getCurrentProjectId();
490
+ if (!projectId) {
491
+ throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
492
+ }
493
+ const { cmd, summary, cwd: cwd2, exit_code, duration_ms, stats } = parsed2;
494
+ const message = summary.startsWith("COMMAND:") ? summary : `COMMAND: ${cmd} \u2014 ${summary}`;
495
+ const run_id = resolveRunId(parsed2.run_id);
496
+ const body = {
497
+ kind: "note",
498
+ concept: "concept:command",
499
+ actor: { type: config.agentType, name: config.agentName },
500
+ message,
501
+ metadata: {
502
+ source: config.source,
503
+ purpose: "command",
504
+ tool: "memora_log_command",
505
+ cmd,
506
+ ...cwd2 ? { cwd: cwd2 } : {},
507
+ ...exit_code !== void 0 ? { exit_code } : {},
508
+ ...duration_ms !== void 0 ? { duration_ms } : {},
509
+ ...run_id ? { run_id } : {},
510
+ ...stats ? { stats } : {}
511
+ }
512
+ };
513
+ await client.post("/timeline/events", body, { projectId });
514
+ return { ok: true };
515
+ }
516
+
517
+ // src/tools/handlers/listProjects.ts
518
+ async function handleListProjects(client) {
519
+ const res = await client.get("/admin/projects");
520
+ return res ?? { items: [] };
521
+ }
522
+
523
+ // src/tools/handlers/setProject.ts
524
+ var import_v415 = require("zod/v4");
525
+ var setProjectInputSchema = import_v415.z.object({
526
+ projectId: import_v415.z.string().min(1)
527
+ });
528
+ async function handleSetProject(args) {
529
+ const parsed2 = setProjectInputSchema.parse(args ?? {});
530
+ setCurrentProjectId(parsed2.projectId);
531
+ return { ok: true, projectId: parsed2.projectId };
532
+ }
533
+
534
+ // src/index.ts
535
+ async function sendHeartbeat(client) {
536
+ try {
537
+ await client.post("/admin/api-keys/heartbeat", {});
538
+ } catch (err) {
539
+ console.error("[memoraone-mcp] heartbeat error (silent)", err);
540
+ }
541
+ }
542
+ function redactSensitiveFields(obj) {
543
+ if (obj === null || obj === void 0) return obj;
544
+ if (typeof obj !== "object") return obj;
545
+ if (Array.isArray(obj)) return obj.map(redactSensitiveFields);
546
+ const redacted = {};
547
+ const sensitiveKeys = /^(headers?|api[_-]?key|token|cookie|authorization|auth|password|secret|credential|bearer)$/i;
548
+ for (const [key, value] of Object.entries(obj)) {
549
+ if (sensitiveKeys.test(key)) {
550
+ redacted[key] = "[REDACTED]";
551
+ } else if (typeof value === "object") {
552
+ redacted[key] = redactSensitiveFields(value);
553
+ } else {
554
+ redacted[key] = value;
555
+ }
556
+ }
557
+ return redacted;
558
+ }
559
+ function sanitizeArgsSummary(args) {
560
+ try {
561
+ const redacted = redactSensitiveFields(args);
562
+ const summary = JSON.stringify(redacted);
563
+ return summary.length > 200 ? summary.slice(0, 200) + "..." : summary;
564
+ } catch {
565
+ return "[unable to serialize args]";
566
+ }
567
+ }
568
+ async function postWorklogEvent(client, message) {
569
+ try {
570
+ const body = {
571
+ kind: "note",
572
+ concept: "concept:worklog",
573
+ actor: { type: config.agentType, name: config.agentName },
574
+ message,
575
+ metadata: {
576
+ source: config.source,
577
+ purpose: "worklog"
578
+ }
579
+ };
580
+ await client.post("/timeline/events", body);
581
+ } catch (err) {
582
+ console.error("[memoraone-mcp] worklog error (silent)", err);
583
+ }
584
+ }
585
+ function registerToolWithWorklog(server, client, toolName, description, schema, handler) {
586
+ if (!config.worklogEnabled) {
587
+ server.tool(toolName, description, schema, handler);
588
+ return;
589
+ }
590
+ server.tool(toolName, description, schema, async (args) => {
591
+ const argsSummary = sanitizeArgsSummary(args);
592
+ const start = Date.now();
593
+ await postWorklogEvent(client, `tool_start: ${toolName} ${argsSummary}`);
594
+ try {
595
+ const result = await handler(args);
596
+ const durationMs = Date.now() - start;
597
+ await postWorklogEvent(client, `tool_end: ${toolName} ok (${durationMs}ms)`);
598
+ return result;
599
+ } catch (err) {
600
+ const durationMs = Date.now() - start;
601
+ const errorSummary = err?.message || String(err);
602
+ const shortError = errorSummary.length > 100 ? errorSummary.slice(0, 100) + "..." : errorSummary;
603
+ await postWorklogEvent(client, `tool_end: ${toolName} error (${durationMs}ms): ${shortError}`);
604
+ throw err;
605
+ }
606
+ });
607
+ }
608
+ async function main() {
609
+ const client = new memoraClient_default(config);
610
+ console.error(
611
+ `[memoraone-mcp] Agent identity: name="${config.agentName}", type="${config.agentType}", source="${config.source}"`
612
+ );
613
+ const server = new import_mcp.McpServer({
614
+ name: "memoraone-mcp",
615
+ version: "1.0.0"
616
+ });
617
+ const registeredToolNames = [];
618
+ registeredToolNames.push("memora_post_event");
619
+ registerToolWithWorklog(
620
+ server,
621
+ client,
622
+ "memora_post_event",
623
+ "Forward an event to MemoraOne timeline",
624
+ postEventShape,
625
+ async (args) => {
626
+ const result = await handlePostEvent(client, args);
627
+ return {
628
+ content: [
629
+ {
630
+ type: "text",
631
+ text: JSON.stringify(result)
632
+ }
633
+ ]
634
+ };
635
+ }
636
+ );
637
+ registeredToolNames.push("memora_list_projects");
638
+ server.tool(
639
+ "memora_list_projects",
640
+ "List projects available to the current API key",
641
+ listProjectsShape,
642
+ async () => {
643
+ const result = await handleListProjects(client);
644
+ return {
645
+ content: [
646
+ {
647
+ type: "text",
648
+ text: JSON.stringify(result)
649
+ }
650
+ ]
651
+ };
652
+ }
653
+ );
654
+ registeredToolNames.push("memora_set_project");
655
+ server.tool(
656
+ "memora_set_project",
657
+ "Set the current project id for subsequent tool calls",
658
+ setProjectShape,
659
+ async (args) => {
660
+ const result = await handleSetProject(args);
661
+ return {
662
+ content: [
663
+ {
664
+ type: "text",
665
+ text: JSON.stringify(result)
666
+ }
667
+ ]
668
+ };
669
+ }
670
+ );
671
+ registeredToolNames.push("memora_ask_with_memory");
672
+ registerToolWithWorklog(
673
+ server,
674
+ client,
675
+ "memora_ask_with_memory",
676
+ "Ask MemoraOne with project memory context",
677
+ askWithMemoryShape,
678
+ async (args) => {
679
+ const result = await handleAskWithMemory(client, args);
680
+ return {
681
+ content: [
682
+ {
683
+ type: "text",
684
+ text: JSON.stringify(result)
685
+ }
686
+ ]
687
+ };
688
+ }
689
+ );
690
+ registeredToolNames.push("memora_log_intent");
691
+ registerToolWithWorklog(
692
+ server,
693
+ client,
694
+ "memora_log_intent",
695
+ "Log a natural-language TASK or DECISION intent to the MemoraOne timeline",
696
+ logIntentShape,
697
+ async (args) => {
698
+ const result = await handleLogIntent(client, args);
699
+ return {
700
+ content: [
701
+ {
702
+ type: "text",
703
+ text: JSON.stringify(result)
704
+ }
705
+ ]
706
+ };
707
+ }
708
+ );
709
+ registeredToolNames.push("memora_log_change_summary");
710
+ registerToolWithWorklog(
711
+ server,
712
+ client,
713
+ "memora_log_change_summary",
714
+ "Log a concise code change summary to the MemoraOne timeline",
715
+ logChangeSummaryShape,
716
+ async (args) => {
717
+ const result = await handleLogChangeSummary(client, args);
718
+ return {
719
+ content: [
720
+ {
721
+ type: "text",
722
+ text: JSON.stringify(result)
723
+ }
724
+ ]
725
+ };
726
+ }
727
+ );
728
+ registeredToolNames.push("memora_log_tool_result");
729
+ registerToolWithWorklog(
730
+ server,
731
+ client,
732
+ "memora_log_tool_result",
733
+ "Log a tool execution result to the MemoraOne timeline",
734
+ logToolResultShape,
735
+ async (args) => {
736
+ const result = await handleLogToolResult(client, args);
737
+ return {
738
+ content: [
739
+ {
740
+ type: "text",
741
+ text: JSON.stringify(result)
742
+ }
743
+ ]
744
+ };
745
+ }
746
+ );
747
+ registeredToolNames.push("memora_log_command");
748
+ registerToolWithWorklog(
749
+ server,
750
+ client,
751
+ "memora_log_command",
752
+ "Log a command execution to the MemoraOne timeline",
753
+ logCommandShape,
754
+ async (args) => {
755
+ const result = await handleLogCommand(client, args);
756
+ return {
757
+ content: [
758
+ {
759
+ type: "text",
760
+ text: JSON.stringify(result)
761
+ }
762
+ ]
763
+ };
764
+ }
765
+ );
766
+ console.error("[memoraone-mcp] Starting MCP server with", registeredToolNames.length, "tools");
767
+ const transport = new import_stdio.StdioServerTransport();
768
+ await server.connect(transport);
769
+ let heartbeatInterval = null;
770
+ if (config.heartbeatEnabled) {
771
+ if (globalThis.__memoraHeartbeatInterval) {
772
+ clearInterval(globalThis.__memoraHeartbeatInterval);
773
+ }
774
+ await sendHeartbeat(client);
775
+ const intervalMs = Number.isFinite(config.heartbeatIntervalMs) ? Math.max(1e3, config.heartbeatIntervalMs) : 3e4;
776
+ heartbeatInterval = setInterval(() => {
777
+ sendHeartbeat(client).catch(() => {
778
+ });
779
+ }, intervalMs);
780
+ globalThis.__memoraHeartbeatInterval = heartbeatInterval;
781
+ }
782
+ const shutdown = (signal) => {
783
+ if (heartbeatInterval) clearInterval(heartbeatInterval);
784
+ if (globalThis.__memoraHeartbeatInterval) {
785
+ clearInterval(globalThis.__memoraHeartbeatInterval);
786
+ }
787
+ console.error(`[memoraone-mcp] Received ${signal}, shutting down`);
788
+ process.exit(0);
789
+ };
790
+ process.on("SIGINT", () => shutdown("SIGINT"));
791
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
792
+ console.error("[memoraone-mcp] MCP server ready");
793
+ }
794
+ main().catch((err) => {
795
+ console.error("[memoraone-mcp] fatal error", err);
796
+ process.exit(1);
797
+ });