@codemem/mcp 0.40.2 → 0.42.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.
@@ -0,0 +1,28 @@
1
+ export interface ViewerChildProcess {
2
+ on(event: "error", listener: () => void): unknown;
3
+ unref(): void;
4
+ }
5
+ export type SpawnViewerProcess = (command: string, args: string[], options: {
6
+ detached: true;
7
+ stdio: "ignore";
8
+ env: NodeJS.ProcessEnv;
9
+ }) => ViewerChildProcess;
10
+ export interface ViewerProbeOptions {
11
+ host?: string;
12
+ port?: string;
13
+ fetchImpl?: typeof fetch;
14
+ }
15
+ export interface EnsureViewerOptions extends ViewerProbeOptions {
16
+ env?: NodeJS.ProcessEnv;
17
+ execPath?: string;
18
+ resolveCliPath?: () => string | null;
19
+ sleep?: (milliseconds: number) => Promise<void>;
20
+ spawnImpl?: SpawnViewerProcess;
21
+ }
22
+ /** Resolve the `codemem` CLI binary path. Checks package-local paths first, then PATH. */
23
+ export declare function resolveCliPath(): string | null;
24
+ /** Check for a live CodeMem viewer via the shared core probe contract. */
25
+ export declare function isViewerHealthy(options?: ViewerProbeOptions): Promise<boolean>;
26
+ /** Attempt to start the viewer as a detached, best-effort background process. */
27
+ export declare function ensureViewer(options?: EnsureViewerOptions): Promise<void>;
28
+ //# sourceMappingURL=stdio-viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio-viewer.d.ts","sourceRoot":"","sources":["../src/stdio-viewer.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IAClC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;IAClD,KAAK,IAAI,IAAI,CAAC;CACd;AAED,MAAM,MAAM,kBAAkB,GAAG,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACR,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB,KACG,kBAAkB,CAAC;AAExB,MAAM,WAAW,kBAAkB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACzB;AAED,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,0FAA0F;AAC1F,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAW9C;AAED,0EAA0E;AAC1E,wBAAsB,eAAe,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAexF;AAED,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCnF"}
package/dist/stdio.js CHANGED
@@ -1,67 +1,84 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as e } from "./server-BshtPXit.js";
3
- import { MemoryStore as t, resolveDbPath as n } from "@codemem/core";
4
- import { existsSync as r } from "node:fs";
5
- import { dirname as i, join as a } from "node:path";
6
- import { spawn as o } from "node:child_process";
3
+ import { MemoryStore as t, probeCodememViewerLiveness as n, resolveDbPath as r } from "@codemem/core";
4
+ import { existsSync as i } from "node:fs";
5
+ import { dirname as a, join as o } from "node:path";
7
6
  import { StdioServerTransport as s } from "@modelcontextprotocol/sdk/server/stdio.js";
8
- //#region src/stdio.ts
9
- var c = process.env.CODEMEM_VIEWER_HOST ?? "127.0.0.1", l = process.env.CODEMEM_VIEWER_PORT ?? "38888";
10
- function u() {
11
- let e = [], t = i(import.meta.dirname ?? ".");
12
- e.push(a(t, "..", "cli", "dist", "index.js")), e.push(a(t, "..", ".bin", "codemem")), e.push(a(t, "..", "..", ".bin", "codemem"));
13
- for (let t of e) if (r(t)) return t;
7
+ import { spawn as c } from "node:child_process";
8
+ //#region src/stdio-viewer.ts
9
+ var l = "127.0.0.1", u = "38888", d = 2e3, f = 1e3, p = 5;
10
+ function m() {
11
+ let e = a(import.meta.dirname ?? "."), t = [
12
+ o(e, "..", "cli", "dist", "index.js"),
13
+ o(e, "..", ".bin", "codemem"),
14
+ o(e, "..", "..", ".bin", "codemem")
15
+ ];
16
+ for (let e of t) if (i(e)) return e;
14
17
  return "codemem";
15
18
  }
16
- async function d() {
17
- try {
18
- let e = `http://${c}:${l}/api/health`;
19
- return (await fetch(e, { signal: AbortSignal.timeout(2e3) })).ok;
20
- } catch {
21
- return !1;
22
- }
19
+ async function h(e = {}) {
20
+ let t = e.host ?? process.env.CODEMEM_VIEWER_HOST ?? l, r = e.port ?? process.env.CODEMEM_VIEWER_PORT ?? u;
21
+ if (!/^\d+$/.test(r.trim())) return !1;
22
+ let i = Number.parseInt(r, 10);
23
+ return !Number.isInteger(i) || i <= 0 || i > 65535 ? !1 : (await n({
24
+ host: t,
25
+ port: i
26
+ }, {
27
+ fetch: e.fetchImpl ?? fetch,
28
+ timeoutMs: d
29
+ })).state === "live";
23
30
  }
24
- async function f() {
25
- if (process.env.CODEMEM_VIEWER === "0" || process.env.CODEMEM_VIEWER_AUTO === "0" || await d()) return;
26
- let e = u();
27
- if (e) try {
28
- let t = e.endsWith(".js"), n = t ? process.execPath : e, r = t ? [
29
- e,
31
+ async function g(e = {}) {
32
+ let t = e.env ?? process.env;
33
+ if (t.CODEMEM_VIEWER === "0" || t.CODEMEM_VIEWER_AUTO === "0") return;
34
+ let n = e.host ?? t.CODEMEM_VIEWER_HOST ?? l, r = e.port ?? t.CODEMEM_VIEWER_PORT ?? u, i = {
35
+ host: n,
36
+ port: r,
37
+ fetchImpl: e.fetchImpl
38
+ };
39
+ if (await h(i)) return;
40
+ let a = (e.resolveCliPath ?? m)();
41
+ if (a) try {
42
+ let o = a.endsWith(".js"), s = o ? e.execPath ?? process.execPath : a, d = o ? [
43
+ a,
30
44
  "serve",
31
45
  "start"
32
46
  ] : ["serve", "start"];
33
- c !== "127.0.0.1" && r.push("--host", c), l !== "38888" && r.push("--port", l);
34
- let i = o(n, r, {
47
+ n !== l && d.push("--host", n), r !== u && d.push("--port", r);
48
+ let m = (e.spawnImpl ?? ((e, t, n) => c(e, t, n)))(s, d, {
35
49
  detached: !0,
36
50
  stdio: "ignore",
37
51
  env: {
38
- ...process.env,
52
+ ...t,
39
53
  CODEMEM_PLUGIN_IGNORE: "1"
40
54
  }
41
55
  });
42
- i.on("error", () => {}), i.unref();
43
- for (let e = 0; e < 5; e++) if (await new Promise((e) => setTimeout(e, 1e3)), await d()) return;
56
+ m.on("error", () => {}), m.unref();
57
+ let g = e.sleep ?? ((e) => new Promise((t) => setTimeout(t, e)));
58
+ for (let e = 0; e < p; e++) if (await g(f), await h(i)) return;
44
59
  } catch {}
45
60
  }
46
- async function p() {
47
- let r = new t(n());
61
+ //#endregion
62
+ //#region src/stdio.ts
63
+ async function _() {
64
+ let n = new t(r());
48
65
  try {
49
- f().catch(() => {});
50
- let t = e(r), n = new s(), i = () => {
66
+ g().catch(() => {});
67
+ let t = e(n), r = new s(), i = () => {
51
68
  try {
52
- r.close();
69
+ n.close();
53
70
  } catch {}
54
71
  process.exit(0);
55
72
  };
56
- process.on("SIGINT", i), process.on("SIGTERM", i), await t.connect(n);
73
+ process.on("SIGINT", i), process.on("SIGTERM", i), await t.connect(r);
57
74
  } catch (e) {
58
75
  try {
59
- r.close();
76
+ n.close();
60
77
  } catch {}
61
78
  throw e;
62
79
  }
63
80
  }
64
- p().catch((e) => {
81
+ _().catch((e) => {
65
82
  console.error("codemem MCP server failed to start:", e), process.exit(1);
66
83
  });
67
84
  //#endregion
package/dist/stdio.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","names":[],"sources":["../src/stdio.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * @codemem/mcp — MCP stdio server bootstrap.\n *\n * Runs as a separate process spawned by the host (OpenCode/Claude).\n * Owns its own better-sqlite3 connection. Communicates via stdio JSON-RPC.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { MemoryStore, resolveDbPath } from \"@codemem/core\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createCodememMcpServer } from \"./server.js\";\n\n/** Default viewer host/port for health checks. */\nconst VIEWER_HOST = process.env.CODEMEM_VIEWER_HOST ?? \"127.0.0.1\";\nconst VIEWER_PORT = process.env.CODEMEM_VIEWER_PORT ?? \"38888\";\n\n/** Resolve the `codemem` CLI binary path. Checks package-local paths first, then PATH. */\nfunction resolveCliPath(): string | null {\n\t// When installed via npm/npx, the MCP server and CLI share node_modules.\n\t// Walk up from this file to find the CLI's bin entry.\n\tconst candidates: string[] = [];\n\tconst selfDir = dirname(import.meta.dirname ?? \".\");\n\t// Monorepo dev: sibling package\n\tcandidates.push(join(selfDir, \"..\", \"cli\", \"dist\", \"index.js\"));\n\t// npm install: node_modules/.bin/codemem\n\tcandidates.push(join(selfDir, \"..\", \".bin\", \"codemem\"));\n\t// npm install: deeper node_modules\n\tcandidates.push(join(selfDir, \"..\", \"..\", \".bin\", \"codemem\"));\n\tfor (const p of candidates) {\n\t\tif (existsSync(p)) return p;\n\t}\n\t// Fall back to PATH\n\treturn \"codemem\";\n}\n\n/** Check if the viewer is already running. */\nasync function isViewerHealthy(): Promise<boolean> {\n\ttry {\n\t\tconst url = `http://${VIEWER_HOST}:${VIEWER_PORT}/api/health`;\n\t\tconst response = await fetch(url, { signal: AbortSignal.timeout(2_000) });\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Attempt to start the viewer server as a detached background process.\n * Best-effort: failures are logged to stderr but never block MCP startup.\n */\nasync function ensureViewer(): Promise<void> {\n\tif (process.env.CODEMEM_VIEWER === \"0\" || process.env.CODEMEM_VIEWER_AUTO === \"0\") return;\n\n\tif (await isViewerHealthy()) return;\n\n\tconst cli = resolveCliPath();\n\tif (!cli) return;\n\n\ttry {\n\t\t// If the resolved path is a .js file, run it with node.\n\t\t// Otherwise it's a bin script that can run directly.\n\t\tconst isJsFile = cli.endsWith(\".js\");\n\t\tconst cmd = isJsFile ? process.execPath : cli;\n\t\tconst args = isJsFile ? [cli, \"serve\", \"start\"] : [\"serve\", \"start\"];\n\t\t// Pass non-default host/port so the spawned viewer matches what we health-check.\n\t\tif (VIEWER_HOST !== \"127.0.0.1\") args.push(\"--host\", VIEWER_HOST);\n\t\tif (VIEWER_PORT !== \"38888\") args.push(\"--port\", VIEWER_PORT);\n\n\t\tconst child = spawn(cmd, args, {\n\t\t\tdetached: true,\n\t\t\tstdio: \"ignore\",\n\t\t\tenv: { ...process.env, CODEMEM_PLUGIN_IGNORE: \"1\" },\n\t\t});\n\t\tchild.on(\"error\", () => {}); // swallow — best effort\n\t\tchild.unref();\n\n\t\t// Brief health check loop — don't block for long\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tawait new Promise((r) => setTimeout(r, 1_000));\n\t\t\tif (await isViewerHealthy()) return;\n\t\t}\n\t} catch {\n\t\t// Best effort — MCP server continues regardless\n\t}\n}\n\nasync function main() {\n\tconst dbPath = resolveDbPath();\n\tconst store = new MemoryStore(dbPath);\n\n\ttry {\n\t\t// Auto-start viewer in background (belt and suspenders with any hook-based start)\n\t\tensureViewer().catch(() => {});\n\n\t\tconst server = createCodememMcpServer(store);\n\t\tconst transport = new StdioServerTransport();\n\n\t\tconst shutdown = () => {\n\t\t\ttry {\n\t\t\t\tstore.close();\n\t\t\t} catch {\n\t\t\t\t// Best effort — process is exiting\n\t\t\t}\n\t\t\tprocess.exit(0);\n\t\t};\n\n\t\tprocess.on(\"SIGINT\", shutdown);\n\t\tprocess.on(\"SIGTERM\", shutdown);\n\n\t\tawait server.connect(transport);\n\t} catch (err) {\n\t\t// Close the store before bubbling so a startup failure does not leave\n\t\t// the SQLite journal behind. Signal handlers are not yet attached if we\n\t\t// crash before `server.connect` resolves.\n\t\ttry {\n\t\t\tstore.close();\n\t\t} catch {\n\t\t\t// best effort\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nmain().catch((err) => {\n\tconsole.error(\"codemem MCP server failed to start:\", err);\n\tprocess.exit(1);\n});\n"],"mappings":";;;;;;;;AAiBA,IAAM,IAAc,QAAQ,IAAI,uBAAuB,aACjD,IAAc,QAAQ,IAAI,uBAAuB;AAGvD,SAAS,IAAgC;CAGxC,IAAM,IAAuB,CAAC,GACxB,IAAU,EAAQ,OAAO,KAAK,WAAW,GAAG;CAMlD,AAJA,EAAW,KAAK,EAAK,GAAS,MAAM,OAAO,QAAQ,UAAU,CAAC,GAE9D,EAAW,KAAK,EAAK,GAAS,MAAM,QAAQ,SAAS,CAAC,GAEtD,EAAW,KAAK,EAAK,GAAS,MAAM,MAAM,QAAQ,SAAS,CAAC;CAC5D,KAAK,IAAM,KAAK,GACf,IAAI,EAAW,CAAC,GAAG,OAAO;CAG3B,OAAO;AACR;AAGA,eAAe,IAAoC;CAClD,IAAI;EACH,IAAM,IAAM,UAAU,EAAY,GAAG,EAAY;EAEjD,QAAO,MADgB,MAAM,GAAK,EAAE,QAAQ,YAAY,QAAQ,GAAK,EAAE,CAAC,GACxD;CACjB,QAAQ;EACP,OAAO;CACR;AACD;AAMA,eAAe,IAA8B;CAG5C,IAFI,QAAQ,IAAI,mBAAmB,OAAO,QAAQ,IAAI,wBAAwB,OAE1E,MAAM,EAAgB,GAAG;CAE7B,IAAM,IAAM,EAAe;CACtB,OAEL,IAAI;EAGH,IAAM,IAAW,EAAI,SAAS,KAAK,GAC7B,IAAM,IAAW,QAAQ,WAAW,GACpC,IAAO,IAAW;GAAC;GAAK;GAAS;EAAO,IAAI,CAAC,SAAS,OAAO;EAGnE,AADI,MAAgB,eAAa,EAAK,KAAK,UAAU,CAAW,GAC5D,MAAgB,WAAS,EAAK,KAAK,UAAU,CAAW;EAE5D,IAAM,IAAQ,EAAM,GAAK,GAAM;GAC9B,UAAU;GACV,OAAO;GACP,KAAK;IAAE,GAAG,QAAQ;IAAK,uBAAuB;GAAI;EACnD,CAAC;EAED,AADA,EAAM,GAAG,eAAe,CAAC,CAAC,GAC1B,EAAM,MAAM;EAGZ,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAEtB,IADA,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,GAAK,CAAC,GACzC,MAAM,EAAgB,GAAG;CAE/B,QAAQ,CAER;AACD;AAEA,eAAe,IAAO;CAErB,IAAM,IAAQ,IAAI,EADH,EACe,CAAM;CAEpC,IAAI;EAEH,EAAa,EAAE,YAAY,CAAC,CAAC;EAE7B,IAAM,IAAS,EAAuB,CAAK,GACrC,IAAY,IAAI,EAAqB,GAErC,UAAiB;GACtB,IAAI;IACH,EAAM,MAAM;GACb,QAAQ,CAER;GACA,QAAQ,KAAK,CAAC;EACf;EAKA,AAHA,QAAQ,GAAG,UAAU,CAAQ,GAC7B,QAAQ,GAAG,WAAW,CAAQ,GAE9B,MAAM,EAAO,QAAQ,CAAS;CAC/B,SAAS,GAAK;EAIb,IAAI;GACH,EAAM,MAAM;EACb,QAAQ,CAER;EACA,MAAM;CACP;AACD;AAEA,EAAK,EAAE,OAAO,MAAQ;CAErB,AADA,QAAQ,MAAM,uCAAuC,CAAG,GACxD,QAAQ,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"stdio.js","names":[],"sources":["../src/stdio-viewer.ts","../src/stdio.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { probeCodememViewerLiveness } from \"@codemem/core\";\n\nconst DEFAULT_VIEWER_HOST = \"127.0.0.1\";\nconst DEFAULT_VIEWER_PORT = \"38888\";\nconst VIEWER_PROBE_TIMEOUT_MS = 2_000;\nconst VIEWER_POLL_INTERVAL_MS = 1_000;\nconst VIEWER_POLL_ATTEMPTS = 5;\n\nexport interface ViewerChildProcess {\n\ton(event: \"error\", listener: () => void): unknown;\n\tunref(): void;\n}\n\nexport type SpawnViewerProcess = (\n\tcommand: string,\n\targs: string[],\n\toptions: {\n\t\tdetached: true;\n\t\tstdio: \"ignore\";\n\t\tenv: NodeJS.ProcessEnv;\n\t},\n) => ViewerChildProcess;\n\nexport interface ViewerProbeOptions {\n\thost?: string;\n\tport?: string;\n\tfetchImpl?: typeof fetch;\n}\n\nexport interface EnsureViewerOptions extends ViewerProbeOptions {\n\tenv?: NodeJS.ProcessEnv;\n\texecPath?: string;\n\tresolveCliPath?: () => string | null;\n\tsleep?: (milliseconds: number) => Promise<void>;\n\tspawnImpl?: SpawnViewerProcess;\n}\n\n/** Resolve the `codemem` CLI binary path. Checks package-local paths first, then PATH. */\nexport function resolveCliPath(): string | null {\n\tconst selfDir = dirname(import.meta.dirname ?? \".\");\n\tconst candidates = [\n\t\tjoin(selfDir, \"..\", \"cli\", \"dist\", \"index.js\"),\n\t\tjoin(selfDir, \"..\", \".bin\", \"codemem\"),\n\t\tjoin(selfDir, \"..\", \"..\", \".bin\", \"codemem\"),\n\t];\n\tfor (const candidate of candidates) {\n\t\tif (existsSync(candidate)) return candidate;\n\t}\n\treturn \"codemem\";\n}\n\n/** Check for a live CodeMem viewer via the shared core probe contract. */\nexport async function isViewerHealthy(options: ViewerProbeOptions = {}): Promise<boolean> {\n\tconst host = options.host ?? process.env.CODEMEM_VIEWER_HOST ?? DEFAULT_VIEWER_HOST;\n\tconst rawPort = options.port ?? process.env.CODEMEM_VIEWER_PORT ?? DEFAULT_VIEWER_PORT;\n\t// Strict parse: \"38888abc\" must not silently probe a different port than\n\t// the `--port` value forwarded to `serve start`.\n\tif (!/^\\d+$/.test(rawPort.trim())) return false;\n\tconst port = Number.parseInt(rawPort, 10);\n\tif (!Number.isInteger(port) || port <= 0 || port > 65_535) return false;\n\n\t// A degraded-but-live viewer must not trigger redundant detached starts.\n\tconst probe = await probeCodememViewerLiveness(\n\t\t{ host, port },\n\t\t{ fetch: options.fetchImpl ?? fetch, timeoutMs: VIEWER_PROBE_TIMEOUT_MS },\n\t);\n\treturn probe.state === \"live\";\n}\n\n/** Attempt to start the viewer as a detached, best-effort background process. */\nexport async function ensureViewer(options: EnsureViewerOptions = {}): Promise<void> {\n\tconst env = options.env ?? process.env;\n\tif (env.CODEMEM_VIEWER === \"0\" || env.CODEMEM_VIEWER_AUTO === \"0\") return;\n\n\tconst host = options.host ?? env.CODEMEM_VIEWER_HOST ?? DEFAULT_VIEWER_HOST;\n\tconst port = options.port ?? env.CODEMEM_VIEWER_PORT ?? DEFAULT_VIEWER_PORT;\n\tconst probeOptions = { host, port, fetchImpl: options.fetchImpl };\n\tif (await isViewerHealthy(probeOptions)) return;\n\n\tconst cli = (options.resolveCliPath ?? resolveCliPath)();\n\tif (!cli) return;\n\n\ttry {\n\t\tconst isJsFile = cli.endsWith(\".js\");\n\t\tconst command = isJsFile ? (options.execPath ?? process.execPath) : cli;\n\t\tconst args = isJsFile ? [cli, \"serve\", \"start\"] : [\"serve\", \"start\"];\n\t\tif (host !== DEFAULT_VIEWER_HOST) args.push(\"--host\", host);\n\t\tif (port !== DEFAULT_VIEWER_PORT) args.push(\"--port\", port);\n\n\t\tconst spawnImpl: SpawnViewerProcess =\n\t\t\toptions.spawnImpl ?? ((command, args, spawnOptions) => spawn(command, args, spawnOptions));\n\t\tconst child = spawnImpl(command, args, {\n\t\t\tdetached: true,\n\t\t\tstdio: \"ignore\",\n\t\t\tenv: { ...env, CODEMEM_PLUGIN_IGNORE: \"1\" },\n\t\t});\n\t\tchild.on(\"error\", () => {});\n\t\tchild.unref();\n\n\t\tconst sleep =\n\t\t\toptions.sleep ??\n\t\t\t((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));\n\t\tfor (let attempt = 0; attempt < VIEWER_POLL_ATTEMPTS; attempt++) {\n\t\t\tawait sleep(VIEWER_POLL_INTERVAL_MS);\n\t\t\tif (await isViewerHealthy(probeOptions)) return;\n\t\t}\n\t} catch {\n\t\t// Best effort — MCP server continues regardless.\n\t}\n}\n","#!/usr/bin/env node\n\n/**\n * @codemem/mcp — MCP stdio server bootstrap.\n *\n * Runs as a separate process spawned by the host (OpenCode/Claude).\n * Owns its own better-sqlite3 connection. Communicates via stdio JSON-RPC.\n */\n\nimport { MemoryStore, resolveDbPath } from \"@codemem/core\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createCodememMcpServer } from \"./server.js\";\nimport { ensureViewer } from \"./stdio-viewer.js\";\n\nasync function main() {\n\tconst dbPath = resolveDbPath();\n\tconst store = new MemoryStore(dbPath);\n\n\ttry {\n\t\t// Auto-start viewer in background (belt and suspenders with any hook-based start)\n\t\tensureViewer().catch(() => {});\n\n\t\tconst server = createCodememMcpServer(store);\n\t\tconst transport = new StdioServerTransport();\n\n\t\tconst shutdown = () => {\n\t\t\ttry {\n\t\t\t\tstore.close();\n\t\t\t} catch {\n\t\t\t\t// Best effort — process is exiting\n\t\t\t}\n\t\t\tprocess.exit(0);\n\t\t};\n\n\t\tprocess.on(\"SIGINT\", shutdown);\n\t\tprocess.on(\"SIGTERM\", shutdown);\n\n\t\tawait server.connect(transport);\n\t} catch (err) {\n\t\t// Close the store before bubbling so a startup failure does not leave\n\t\t// the SQLite journal behind. Signal handlers are not yet attached if we\n\t\t// crash before `server.connect` resolves.\n\t\ttry {\n\t\t\tstore.close();\n\t\t} catch {\n\t\t\t// best effort\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nmain().catch((err) => {\n\tconsole.error(\"codemem MCP server failed to start:\", err);\n\tprocess.exit(1);\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemem/mcp",
3
- "version": "0.40.2",
3
+ "version": "0.42.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -31,7 +31,7 @@
31
31
  "express": "^5.2.1",
32
32
  "express-rate-limit": "^8.5.2",
33
33
  "zod": "^4.4.3",
34
- "@codemem/core": "^0.40.2"
34
+ "@codemem/core": "^0.42.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/express": "^5.0.6",