@hasna/mementos 0.14.40 → 0.14.42
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/LICENSE +2 -1
- package/README.md +45 -0
- package/dist/cli/index.js +9 -3
- package/dist/index.js +8 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/mcp/http.d.ts +17 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +204 -80
- package/dist/server/index.js +10 -4
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -175,7 +176,7 @@
|
|
|
175
176
|
|
|
176
177
|
END OF TERMS AND CONDITIONS
|
|
177
178
|
|
|
178
|
-
Copyright
|
|
179
|
+
Copyright 2026 Hasna, Inc.
|
|
179
180
|
|
|
180
181
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
182
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -17,6 +17,39 @@ npm install -g @hasna/mementos
|
|
|
17
17
|
mementos --help
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## Shared Event Webhooks
|
|
21
|
+
|
|
22
|
+
`mementos` exposes the shared `@hasna/events` commands so memory events can
|
|
23
|
+
trigger deterministic or agentic automation without custom glue scripts. To
|
|
24
|
+
route mementos events into an OpenLoops worker/verifier template, register a
|
|
25
|
+
command webhook:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
mementos webhooks add loops \
|
|
29
|
+
--id openloops-mementos-events \
|
|
30
|
+
--transport command \
|
|
31
|
+
--source mementos \
|
|
32
|
+
--type "*" \
|
|
33
|
+
--arg=events \
|
|
34
|
+
--arg=handle \
|
|
35
|
+
--arg=generic \
|
|
36
|
+
--arg=--provider \
|
|
37
|
+
--arg=codewith \
|
|
38
|
+
--arg=--auth-profile \
|
|
39
|
+
--arg=account005 \
|
|
40
|
+
--arg=--permission-mode \
|
|
41
|
+
--arg=bypass \
|
|
42
|
+
--arg=--sandbox \
|
|
43
|
+
--arg=danger-full-access \
|
|
44
|
+
--timeout-ms 900000 \
|
|
45
|
+
--json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`@hasna/events` sends the event envelope on stdin and in `HASNA_EVENT_JSON`.
|
|
49
|
+
OpenLoops can then create a deduped one-shot workflow for the event. Keep the
|
|
50
|
+
event payload scoped and include `working_dir`, `project_path`, or `repo_path`
|
|
51
|
+
when a downstream agent needs to run inside a specific repository.
|
|
52
|
+
|
|
20
53
|
## MCP Server
|
|
21
54
|
|
|
22
55
|
```bash
|
|
@@ -25,6 +58,18 @@ mementos-mcp
|
|
|
25
58
|
|
|
26
59
|
116 tools available.
|
|
27
60
|
|
|
61
|
+
## HTTP mode
|
|
62
|
+
|
|
63
|
+
Run a shared Streamable HTTP MCP server (stateless, `127.0.0.1` only):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mementos-mcp --http
|
|
67
|
+
# or: MCP_HTTP=1 mementos-mcp
|
|
68
|
+
# default port: 8824 (override with --port or MCP_HTTP_PORT)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Endpoints: `GET /health`, `POST /mcp` (Streamable HTTP).
|
|
72
|
+
|
|
28
73
|
## REST API
|
|
29
74
|
|
|
30
75
|
```bash
|
package/dist/cli/index.js
CHANGED
|
@@ -1011,7 +1011,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1011
1011
|
this._exitCallback = (err) => {
|
|
1012
1012
|
if (err.code !== "commander.executeSubCommandAsync") {
|
|
1013
1013
|
throw err;
|
|
1014
|
-
}
|
|
1014
|
+
} else {}
|
|
1015
1015
|
};
|
|
1016
1016
|
}
|
|
1017
1017
|
return this;
|
|
@@ -29179,7 +29179,7 @@ class JSONSchemaGenerator {
|
|
|
29179
29179
|
if (val === undefined) {
|
|
29180
29180
|
if (this.unrepresentable === "throw") {
|
|
29181
29181
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
29182
|
-
}
|
|
29182
|
+
} else {}
|
|
29183
29183
|
} else if (typeof val === "bigint") {
|
|
29184
29184
|
if (this.unrepresentable === "throw") {
|
|
29185
29185
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -49430,7 +49430,7 @@ var require_tracestate_impl = __commonJS((exports) => {
|
|
|
49430
49430
|
const value = listMember.slice(i + 1, part.length);
|
|
49431
49431
|
if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
|
|
49432
49432
|
agg.set(key, value);
|
|
49433
|
-
}
|
|
49433
|
+
} else {}
|
|
49434
49434
|
}
|
|
49435
49435
|
return agg;
|
|
49436
49436
|
}, new Map);
|
|
@@ -64425,6 +64425,9 @@ import chalk19 from "chalk";
|
|
|
64425
64425
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
|
|
64426
64426
|
import { homedir as homedir3 } from "os";
|
|
64427
64427
|
import { basename, dirname as dirname3, join as join5, resolve as resolve13 } from "path";
|
|
64428
|
+
function isInMemoryDb2(path) {
|
|
64429
|
+
return path === ":memory:" || path.startsWith("file::memory:");
|
|
64430
|
+
}
|
|
64428
64431
|
var DEFAULT_CONFIG = {
|
|
64429
64432
|
default_scope: "private",
|
|
64430
64433
|
default_category: "knowledge",
|
|
@@ -64596,6 +64599,9 @@ function getDbPath2() {
|
|
|
64596
64599
|
}
|
|
64597
64600
|
const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
|
|
64598
64601
|
if (envDbPath) {
|
|
64602
|
+
if (isInMemoryDb2(envDbPath)) {
|
|
64603
|
+
return envDbPath;
|
|
64604
|
+
}
|
|
64599
64605
|
const resolved = resolve13(envDbPath);
|
|
64600
64606
|
ensureDir2(dirname3(resolved));
|
|
64601
64607
|
return resolved;
|
package/dist/index.js
CHANGED
|
@@ -20191,7 +20191,7 @@ class JSONSchemaGenerator {
|
|
|
20191
20191
|
if (val === undefined) {
|
|
20192
20192
|
if (this.unrepresentable === "throw") {
|
|
20193
20193
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
20194
|
-
}
|
|
20194
|
+
} else {}
|
|
20195
20195
|
} else if (typeof val === "bigint") {
|
|
20196
20196
|
if (this.unrepresentable === "throw") {
|
|
20197
20197
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -40442,7 +40442,7 @@ var require_tracestate_impl = __commonJS((exports) => {
|
|
|
40442
40442
|
const value = listMember.slice(i + 1, part.length);
|
|
40443
40443
|
if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
|
|
40444
40444
|
agg.set(key, value);
|
|
40445
|
-
}
|
|
40445
|
+
} else {}
|
|
40446
40446
|
}
|
|
40447
40447
|
return agg;
|
|
40448
40448
|
}, new Map);
|
|
@@ -55359,6 +55359,9 @@ function logSearchQuery(query, resultCount, agentId, projectId, db) {
|
|
|
55359
55359
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
|
|
55360
55360
|
import { homedir as homedir2 } from "os";
|
|
55361
55361
|
import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
55362
|
+
function isInMemoryDb2(path) {
|
|
55363
|
+
return path === ":memory:" || path.startsWith("file::memory:");
|
|
55364
|
+
}
|
|
55362
55365
|
var DEFAULT_CONFIG = {
|
|
55363
55366
|
default_scope: "private",
|
|
55364
55367
|
default_category: "knowledge",
|
|
@@ -55530,6 +55533,9 @@ function getDbPath2() {
|
|
|
55530
55533
|
}
|
|
55531
55534
|
const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
|
|
55532
55535
|
if (envDbPath) {
|
|
55536
|
+
if (isInMemoryDb2(envDbPath)) {
|
|
55537
|
+
return envDbPath;
|
|
55538
|
+
}
|
|
55533
55539
|
const resolved = resolve2(envDbPath);
|
|
55534
55540
|
ensureDir2(dirname2(resolved));
|
|
55535
55541
|
return resolved;
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,UAAU,CAAC;AAU5E,eAAO,MAAM,cAAc,EAAE,cA4B5B,CAAC;AA4DF,wBAAgB,UAAU,IAAI,cAAc,CAwC3C;AAwED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAMhD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQ1D;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAOvC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAmDlC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare const MCP_HTTP_SERVICE_NAME = "mementos";
|
|
3
|
+
export declare const DEFAULT_MCP_HTTP_PORT = 8867;
|
|
4
|
+
export declare function isHttpMode(argv?: string[], env?: NodeJS.ProcessEnv): boolean;
|
|
5
|
+
export declare function isStdioMode(argv?: string[], env?: NodeJS.ProcessEnv): boolean;
|
|
6
|
+
export declare function resolveMcpHttpPort(argv?: string[], env?: NodeJS.ProcessEnv): number;
|
|
7
|
+
export type McpHttpServerHandle = {
|
|
8
|
+
port: number;
|
|
9
|
+
host: string;
|
|
10
|
+
close: () => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
export declare function startMcpHttpServer(buildServer: () => McpServer, options?: {
|
|
13
|
+
port?: number;
|
|
14
|
+
host?: string;
|
|
15
|
+
serviceName?: string;
|
|
16
|
+
}): Promise<McpHttpServerHandle>;
|
|
17
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/mcp/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,wBAAgB,UAAU,CACxB,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAET;AAED,wBAAgB,WAAW,CACzB,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAET;AAED,wBAAgB,kBAAkB,CAChC,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CASR;AAoBD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,SAAS,EAC5B,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,OAAO,CAAC,mBAAmB,CAAC,CAwE9B"}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
/** Exported so other modules can push channel notifications via the underlying Server. */
|
|
4
4
|
export declare let mcpServer: McpServer | null;
|
|
5
|
+
export declare function buildServer(): McpServer;
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6CpE,0FAA0F;AAC1F,eAAO,IAAI,SAAS,EAAE,SAAS,GAAG,IAAW,CAAC;AAqB9C,wBAAgB,WAAW,IAAI,SAAS,CA4EvC"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -14668,6 +14668,9 @@ var init_export_v1 = __esm(() => {
|
|
|
14668
14668
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
|
|
14669
14669
|
import { homedir as homedir2 } from "os";
|
|
14670
14670
|
import { basename as basename2, dirname as dirname4, join as join6, resolve as resolve3 } from "path";
|
|
14671
|
+
function isInMemoryDb2(path) {
|
|
14672
|
+
return path === ":memory:" || path.startsWith("file::memory:");
|
|
14673
|
+
}
|
|
14671
14674
|
function deepMerge(target, source) {
|
|
14672
14675
|
const result = { ...target };
|
|
14673
14676
|
for (const key of Object.keys(source)) {
|
|
@@ -14774,6 +14777,9 @@ function getDbPath2() {
|
|
|
14774
14777
|
}
|
|
14775
14778
|
const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
|
|
14776
14779
|
if (envDbPath) {
|
|
14780
|
+
if (isInMemoryDb2(envDbPath)) {
|
|
14781
|
+
return envDbPath;
|
|
14782
|
+
}
|
|
14777
14783
|
const resolved = resolve3(envDbPath);
|
|
14778
14784
|
ensureDir2(dirname4(resolved));
|
|
14779
14785
|
return resolved;
|
|
@@ -26264,7 +26270,7 @@ class JSONSchemaGenerator {
|
|
|
26264
26270
|
if (val === undefined) {
|
|
26265
26271
|
if (this.unrepresentable === "throw") {
|
|
26266
26272
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
26267
|
-
}
|
|
26273
|
+
} else {}
|
|
26268
26274
|
} else if (typeof val === "bigint") {
|
|
26269
26275
|
if (this.unrepresentable === "throw") {
|
|
26270
26276
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -46515,7 +46521,7 @@ var require_tracestate_impl = __commonJS((exports) => {
|
|
|
46515
46521
|
const value = listMember.slice(i + 1, part.length);
|
|
46516
46522
|
if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
|
|
46517
46523
|
agg.set(key, value);
|
|
46518
|
-
}
|
|
46524
|
+
} else {}
|
|
46519
46525
|
}
|
|
46520
46526
|
return agg;
|
|
46521
46527
|
}, new Map);
|
|
@@ -68285,74 +68291,174 @@ function registerConsolidationTools(server) {
|
|
|
68285
68291
|
});
|
|
68286
68292
|
}
|
|
68287
68293
|
|
|
68294
|
+
// src/mcp/http.ts
|
|
68295
|
+
import { createServer } from "http";
|
|
68296
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
68297
|
+
var MCP_HTTP_SERVICE_NAME = "mementos";
|
|
68298
|
+
var DEFAULT_MCP_HTTP_PORT = 8867;
|
|
68299
|
+
function isStdioMode(argv = process.argv, env = process.env) {
|
|
68300
|
+
return argv.includes("--stdio") || env.MCP_STDIO === "1";
|
|
68301
|
+
}
|
|
68302
|
+
function resolveMcpHttpPort(argv = process.argv, env = process.env) {
|
|
68303
|
+
const portIdx = argv.indexOf("--port");
|
|
68304
|
+
if (portIdx !== -1 && argv[portIdx + 1]) {
|
|
68305
|
+
return parsePort(argv[portIdx + 1], "--port");
|
|
68306
|
+
}
|
|
68307
|
+
if (env.MCP_HTTP_PORT) {
|
|
68308
|
+
return parsePort(env.MCP_HTTP_PORT, "MCP_HTTP_PORT");
|
|
68309
|
+
}
|
|
68310
|
+
return DEFAULT_MCP_HTTP_PORT;
|
|
68311
|
+
}
|
|
68312
|
+
function parsePort(raw, source) {
|
|
68313
|
+
const parsed = Number(raw);
|
|
68314
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
68315
|
+
throw new Error(`Invalid ${source} value "${raw}". Expected 0-65535.`);
|
|
68316
|
+
}
|
|
68317
|
+
return parsed;
|
|
68318
|
+
}
|
|
68319
|
+
async function readJsonBody(req) {
|
|
68320
|
+
const chunks = [];
|
|
68321
|
+
for await (const chunk of req) {
|
|
68322
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
68323
|
+
}
|
|
68324
|
+
const text3 = Buffer.concat(chunks).toString("utf8");
|
|
68325
|
+
if (!text3)
|
|
68326
|
+
return;
|
|
68327
|
+
return JSON.parse(text3);
|
|
68328
|
+
}
|
|
68329
|
+
async function startMcpHttpServer(buildServer, options) {
|
|
68330
|
+
const host = options?.host ?? "127.0.0.1";
|
|
68331
|
+
const requestedPort = options?.port ?? resolveMcpHttpPort();
|
|
68332
|
+
const serviceName = options?.serviceName ?? MCP_HTTP_SERVICE_NAME;
|
|
68333
|
+
const httpServer = createServer(async (req, res) => {
|
|
68334
|
+
try {
|
|
68335
|
+
const url2 = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
68336
|
+
if (req.method === "GET" && url2.pathname === "/health") {
|
|
68337
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
68338
|
+
res.end(JSON.stringify({ status: "ok", name: serviceName }));
|
|
68339
|
+
return;
|
|
68340
|
+
}
|
|
68341
|
+
if (url2.pathname !== "/mcp") {
|
|
68342
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
68343
|
+
res.end("Not Found");
|
|
68344
|
+
return;
|
|
68345
|
+
}
|
|
68346
|
+
const server = buildServer();
|
|
68347
|
+
const transport = new StreamableHTTPServerTransport({
|
|
68348
|
+
sessionIdGenerator: undefined
|
|
68349
|
+
});
|
|
68350
|
+
await server.connect(transport);
|
|
68351
|
+
let parsedBody;
|
|
68352
|
+
if (req.method === "POST") {
|
|
68353
|
+
parsedBody = await readJsonBody(req);
|
|
68354
|
+
}
|
|
68355
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
68356
|
+
res.on("close", () => {
|
|
68357
|
+
transport.close();
|
|
68358
|
+
server.close();
|
|
68359
|
+
});
|
|
68360
|
+
} catch (error40) {
|
|
68361
|
+
console.error(`[${serviceName}-mcp] HTTP error:`, error40);
|
|
68362
|
+
if (!res.headersSent) {
|
|
68363
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
68364
|
+
res.end(JSON.stringify({
|
|
68365
|
+
jsonrpc: "2.0",
|
|
68366
|
+
error: { code: -32603, message: "Internal server error" },
|
|
68367
|
+
id: null
|
|
68368
|
+
}));
|
|
68369
|
+
}
|
|
68370
|
+
}
|
|
68371
|
+
});
|
|
68372
|
+
await new Promise((resolve6, reject) => {
|
|
68373
|
+
httpServer.once("error", reject);
|
|
68374
|
+
httpServer.listen(requestedPort, host, () => resolve6());
|
|
68375
|
+
});
|
|
68376
|
+
const addr = httpServer.address();
|
|
68377
|
+
const port = typeof addr === "object" && addr ? addr.port : requestedPort;
|
|
68378
|
+
console.error(`[${serviceName}-mcp] Streamable HTTP listening on http://${host}:${port}/mcp`);
|
|
68379
|
+
return {
|
|
68380
|
+
port,
|
|
68381
|
+
host,
|
|
68382
|
+
close: () => new Promise((resolve6, reject) => {
|
|
68383
|
+
httpServer.close((err) => err ? reject(err) : resolve6());
|
|
68384
|
+
})
|
|
68385
|
+
};
|
|
68386
|
+
}
|
|
68387
|
+
|
|
68288
68388
|
// src/mcp/index.ts
|
|
68289
68389
|
import { createRequire } from "module";
|
|
68290
68390
|
var _require = createRequire(import.meta.url);
|
|
68291
68391
|
var _pkg = _require("../../package.json");
|
|
68292
68392
|
var mcpServer = null;
|
|
68293
|
-
var server = new McpServer({
|
|
68294
|
-
name: "mementos",
|
|
68295
|
-
version: _pkg.version
|
|
68296
|
-
}, {
|
|
68297
|
-
capabilities: {
|
|
68298
|
-
experimental: { "claude/channel": {} }
|
|
68299
|
-
},
|
|
68300
|
-
instructions: `Mementos is the persistent memory layer for AI agents. It stores, searches, and manages memories across sessions and projects.
|
|
68301
|
-
|
|
68302
|
-
When running with --dangerously-load-development-channels, mementos will proactively push relevant memories into your conversation via channel notifications. These appear as <channel source="mementos"> tags. They contain memories activated by your current task context \u2014 use them to inform your work. You don't need to call memory_inject when auto-inject is active.`
|
|
68303
|
-
});
|
|
68304
|
-
mcpServer = server;
|
|
68305
68393
|
function hasFlag(...flags) {
|
|
68306
68394
|
return process.argv.some((arg) => flags.includes(arg));
|
|
68307
68395
|
}
|
|
68308
68396
|
function printHelp() {
|
|
68309
68397
|
process.stdout.write(`Usage: mementos-mcp [options]
|
|
68310
68398
|
|
|
68311
|
-
Mementos MCP server (stdio transport)
|
|
68399
|
+
Mementos MCP server (stdio transport by default)
|
|
68312
68400
|
|
|
68313
68401
|
Options:
|
|
68402
|
+
--http Serve MCP over Streamable HTTP (127.0.0.1)
|
|
68403
|
+
--port <number> HTTP port (default: 8824, env: MCP_HTTP_PORT)
|
|
68314
68404
|
-h, --help Show help
|
|
68315
68405
|
-V, --version Show version
|
|
68316
68406
|
`);
|
|
68317
68407
|
}
|
|
68318
|
-
|
|
68319
|
-
|
|
68320
|
-
|
|
68321
|
-
|
|
68322
|
-
|
|
68323
|
-
|
|
68324
|
-
|
|
68325
|
-
|
|
68326
|
-
|
|
68327
|
-
|
|
68328
|
-
|
|
68329
|
-
|
|
68330
|
-
|
|
68331
|
-
|
|
68332
|
-
|
|
68333
|
-
|
|
68334
|
-
|
|
68335
|
-
|
|
68336
|
-
|
|
68337
|
-
|
|
68338
|
-
|
|
68339
|
-
|
|
68340
|
-
|
|
68341
|
-
|
|
68342
|
-
|
|
68343
|
-
|
|
68344
|
-
server
|
|
68345
|
-
|
|
68346
|
-
|
|
68347
|
-
|
|
68348
|
-
server
|
|
68349
|
-
|
|
68350
|
-
|
|
68351
|
-
|
|
68352
|
-
server
|
|
68353
|
-
|
|
68354
|
-
|
|
68355
|
-
|
|
68408
|
+
function buildServer() {
|
|
68409
|
+
const server = new McpServer({
|
|
68410
|
+
name: "mementos",
|
|
68411
|
+
version: _pkg.version
|
|
68412
|
+
}, {
|
|
68413
|
+
capabilities: {
|
|
68414
|
+
experimental: { "claude/channel": {} }
|
|
68415
|
+
},
|
|
68416
|
+
instructions: `Mementos is the persistent memory layer for AI agents. It stores, searches, and manages memories across sessions and projects.
|
|
68417
|
+
|
|
68418
|
+
When running with --dangerously-load-development-channels, mementos will proactively push relevant memories into your conversation via channel notifications. These appear as <channel source="mementos"> tags. They contain memories activated by your current task context \u2014 use them to inform your work. You don't need to call memory_inject when auto-inject is active.`
|
|
68419
|
+
});
|
|
68420
|
+
registerMemoryCrudTools(server);
|
|
68421
|
+
registerMemoryHistoryTools(server);
|
|
68422
|
+
registerMemoryHealthTools(server);
|
|
68423
|
+
registerMemoryValidationTools(server);
|
|
68424
|
+
registerMemorySearchTools(server);
|
|
68425
|
+
registerMemoryLifecycleTools(server);
|
|
68426
|
+
registerMemorySyncTools(server);
|
|
68427
|
+
registerMemoryStatsTools(server);
|
|
68428
|
+
registerMemoryAuditTools(server);
|
|
68429
|
+
registerMemoryIoTools(server);
|
|
68430
|
+
registerMemoryInjectTools(server);
|
|
68431
|
+
registerEntityTools(server);
|
|
68432
|
+
registerRelationTools(server);
|
|
68433
|
+
registerGraphQueryTools(server);
|
|
68434
|
+
registerAgentTools(server);
|
|
68435
|
+
registerProjectTools(server);
|
|
68436
|
+
registerBulkTools(server);
|
|
68437
|
+
registerLockTools(server);
|
|
68438
|
+
registerFocusTools(server);
|
|
68439
|
+
registerHookTools(server);
|
|
68440
|
+
registerSynthesisTools(server);
|
|
68441
|
+
registerAutoMemoryTools(server);
|
|
68442
|
+
registerSessionTools(server);
|
|
68443
|
+
registerUtilityTools(server);
|
|
68444
|
+
registerSystemTools(server);
|
|
68445
|
+
registerMementosStorageTools(server);
|
|
68446
|
+
registerConsolidationTools(server);
|
|
68447
|
+
server.resource("memories", "mementos://memories", { description: "All active memories", mimeType: "application/json" }, async () => {
|
|
68448
|
+
const memories = listMemories({ status: "active", limit: 1000 });
|
|
68449
|
+
return { contents: [{ uri: "mementos://memories", text: JSON.stringify(memories, null, 2), mimeType: "application/json" }] };
|
|
68450
|
+
});
|
|
68451
|
+
server.resource("agents", "mementos://agents", { description: "All registered agents", mimeType: "application/json" }, async () => {
|
|
68452
|
+
const agents = listAgents();
|
|
68453
|
+
return { contents: [{ uri: "mementos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
|
|
68454
|
+
});
|
|
68455
|
+
server.resource("projects", "mementos://projects", { description: "All registered projects", mimeType: "application/json" }, async () => {
|
|
68456
|
+
const projects = listProjects();
|
|
68457
|
+
return { contents: [{ uri: "mementos://projects", text: JSON.stringify(projects, null, 2), mimeType: "application/json" }] };
|
|
68458
|
+
});
|
|
68459
|
+
mcpServer = server;
|
|
68460
|
+
return server;
|
|
68461
|
+
}
|
|
68356
68462
|
async function ensureRestServerRunning() {
|
|
68357
68463
|
try {
|
|
68358
68464
|
const res = await fetch("http://127.0.0.1:19428/api/memories?limit=0", {
|
|
@@ -68378,6 +68484,16 @@ async function ensureRestServerRunning() {
|
|
|
68378
68484
|
} catch {}
|
|
68379
68485
|
}
|
|
68380
68486
|
}
|
|
68487
|
+
async function prepareMcpRuntime() {
|
|
68488
|
+
try {
|
|
68489
|
+
const warning = getPrimaryMachineStartupWarning(getDatabase());
|
|
68490
|
+
if (warning) {
|
|
68491
|
+
console.warn(`[mementos-mcp] ${warning}`);
|
|
68492
|
+
}
|
|
68493
|
+
} catch {}
|
|
68494
|
+
await ensureRestServerRunning();
|
|
68495
|
+
loadWebhooksFromDb();
|
|
68496
|
+
}
|
|
68381
68497
|
async function main() {
|
|
68382
68498
|
if (hasFlag("--help", "-h")) {
|
|
68383
68499
|
printHelp();
|
|
@@ -68388,37 +68504,45 @@ async function main() {
|
|
|
68388
68504
|
`);
|
|
68389
68505
|
return;
|
|
68390
68506
|
}
|
|
68391
|
-
|
|
68392
|
-
|
|
68393
|
-
|
|
68394
|
-
|
|
68395
|
-
|
|
68396
|
-
|
|
68397
|
-
|
|
68398
|
-
|
|
68399
|
-
|
|
68400
|
-
|
|
68401
|
-
|
|
68402
|
-
|
|
68403
|
-
|
|
68404
|
-
|
|
68405
|
-
|
|
68406
|
-
|
|
68407
|
-
|
|
68408
|
-
|
|
68507
|
+
await prepareMcpRuntime();
|
|
68508
|
+
if (isStdioMode()) {
|
|
68509
|
+
const server = buildServer();
|
|
68510
|
+
const transport = new StdioServerTransport;
|
|
68511
|
+
await server.connect(transport);
|
|
68512
|
+
const autoProject = detectProject();
|
|
68513
|
+
startAutoInject({
|
|
68514
|
+
server,
|
|
68515
|
+
project_id: autoProject?.id,
|
|
68516
|
+
project_name: autoProject?.name,
|
|
68517
|
+
cwd: process.cwd()
|
|
68518
|
+
}).catch(() => {});
|
|
68519
|
+
process.on("SIGINT", () => {
|
|
68520
|
+
stopAutoInject();
|
|
68521
|
+
process.exit(0);
|
|
68522
|
+
});
|
|
68523
|
+
process.on("SIGTERM", () => {
|
|
68524
|
+
stopAutoInject();
|
|
68525
|
+
process.exit(0);
|
|
68526
|
+
});
|
|
68527
|
+
return;
|
|
68528
|
+
}
|
|
68529
|
+
const handle = await startMcpHttpServer(buildServer, {
|
|
68530
|
+
port: resolveMcpHttpPort()
|
|
68531
|
+
});
|
|
68409
68532
|
process.on("SIGINT", () => {
|
|
68410
|
-
|
|
68411
|
-
process.exit(0);
|
|
68533
|
+
handle.close().finally(() => process.exit(0));
|
|
68412
68534
|
});
|
|
68413
68535
|
process.on("SIGTERM", () => {
|
|
68414
|
-
|
|
68415
|
-
|
|
68536
|
+
handle.close().finally(() => process.exit(0));
|
|
68537
|
+
});
|
|
68538
|
+
}
|
|
68539
|
+
if (import.meta.main) {
|
|
68540
|
+
main().catch((error40) => {
|
|
68541
|
+
console.error("MCP server error:", error40);
|
|
68542
|
+
process.exit(1);
|
|
68416
68543
|
});
|
|
68417
68544
|
}
|
|
68418
|
-
main().catch((error40) => {
|
|
68419
|
-
console.error("MCP server error:", error40);
|
|
68420
|
-
process.exit(1);
|
|
68421
|
-
});
|
|
68422
68545
|
export {
|
|
68423
|
-
mcpServer
|
|
68546
|
+
mcpServer,
|
|
68547
|
+
buildServer
|
|
68424
68548
|
};
|
package/dist/server/index.js
CHANGED
|
@@ -5954,7 +5954,7 @@ __export(exports_database, {
|
|
|
5954
5954
|
});
|
|
5955
5955
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync as cpSync2 } from "fs";
|
|
5956
5956
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
5957
|
-
function
|
|
5957
|
+
function isInMemoryDb2(path) {
|
|
5958
5958
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
5959
5959
|
}
|
|
5960
5960
|
function findNearestMementosDb(startDir) {
|
|
@@ -6011,7 +6011,7 @@ function getDbPath2() {
|
|
|
6011
6011
|
return join3(home, ".hasna", "mementos", "mementos.db");
|
|
6012
6012
|
}
|
|
6013
6013
|
function ensureDir2(filePath) {
|
|
6014
|
-
if (
|
|
6014
|
+
if (isInMemoryDb2(filePath))
|
|
6015
6015
|
return;
|
|
6016
6016
|
const dir = dirname2(resolve2(filePath));
|
|
6017
6017
|
if (!existsSync2(dir)) {
|
|
@@ -22974,7 +22974,7 @@ class JSONSchemaGenerator {
|
|
|
22974
22974
|
if (val === undefined) {
|
|
22975
22975
|
if (this.unrepresentable === "throw") {
|
|
22976
22976
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
22977
|
-
}
|
|
22977
|
+
} else {}
|
|
22978
22978
|
} else if (typeof val === "bigint") {
|
|
22979
22979
|
if (this.unrepresentable === "throw") {
|
|
22980
22980
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -43225,7 +43225,7 @@ var require_tracestate_impl = __commonJS((exports) => {
|
|
|
43225
43225
|
const value = listMember.slice(i + 1, part.length);
|
|
43226
43226
|
if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
|
|
43227
43227
|
agg.set(key, value);
|
|
43228
|
-
}
|
|
43228
|
+
} else {}
|
|
43229
43229
|
}
|
|
43230
43230
|
return agg;
|
|
43231
43231
|
}, new Map);
|
|
@@ -55997,6 +55997,9 @@ import { join as join5, resolve as resolve4, sep } from "path";
|
|
|
55997
55997
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync, cpSync } from "fs";
|
|
55998
55998
|
import { homedir } from "os";
|
|
55999
55999
|
import { basename, dirname, join, resolve } from "path";
|
|
56000
|
+
function isInMemoryDb(path) {
|
|
56001
|
+
return path === ":memory:" || path.startsWith("file::memory:");
|
|
56002
|
+
}
|
|
56000
56003
|
function findFileWalkingUp(filename) {
|
|
56001
56004
|
let dir = process.cwd();
|
|
56002
56005
|
while (true) {
|
|
@@ -56063,6 +56066,9 @@ function getDbPath() {
|
|
|
56063
56066
|
}
|
|
56064
56067
|
const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
|
|
56065
56068
|
if (envDbPath) {
|
|
56069
|
+
if (isInMemoryDb(envDbPath)) {
|
|
56070
|
+
return envDbPath;
|
|
56071
|
+
}
|
|
56066
56072
|
const resolved = resolve(envDbPath);
|
|
56067
56073
|
ensureDir(dirname(resolved));
|
|
56068
56074
|
return resolved;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/mementos",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.42",
|
|
4
4
|
"description": "Universal memory system for AI agents - CLI + MCP server + library API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"build": "bun run clean && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/index.ts src/storage.ts --outdir dist --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun && tsc --emitDeclarationOnly --outDir dist",
|
|
36
36
|
"prepare": "bun run build",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
|
-
"test": "bun test",
|
|
38
|
+
"test": "bun test --isolate --timeout=10000",
|
|
39
39
|
"dev:cli": "bun run src/cli/index.tsx",
|
|
40
40
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
41
41
|
"dev:serve": "bun run src/server/index.ts",
|