@llmnesia/mcp 0.2.7 → 0.2.8

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 CHANGED
@@ -24,12 +24,19 @@ Claude Desktop step on Windows:
24
24
 
25
25
  1. Install LLMnesia and import the chat history you want available.
26
26
  2. Open **Full settings → Connect desktop AI**.
27
- 3. Follow every step shown for your computer, including fully quitting and
28
- reopening the browser and connected AI app when instructed.
27
+ 3. Run the exact connector command shown there.
28
+ 4. Fully quit and reopen the browser.
29
+ 5. Return to **Connect desktop AI** and enable automatic sync.
30
+
31
+ > **Installing the npm package is not setup.** Running
32
+ > `npm install @llmnesia/mcp` only downloads package files; it does not register
33
+ > the native browser connector or configure desktop AI apps. Use the exact
34
+ > `npx -y @llmnesia/mcp@latest install` command below (or `npx.cmd` on Windows),
35
+ > including the final `install` argument.
29
36
 
30
37
  The commands below are the connector-install step from that guided flow. They
31
- are also provided as a reference for technical users; running one by itself is
32
- not the complete LLMnesia setup.
38
+ are also provided as a reference for technical users; the browser restart and
39
+ permission step are still required afterward.
33
40
 
34
41
  ### Connector command
35
42
 
@@ -64,7 +71,7 @@ The installer:
64
71
  corpus,
65
72
  2. copies the server into `~/.llmnesia/bin` so it has a permanent home,
66
73
  3. sets up the supported desktop AI apps (Claude Code, Cursor,
67
- and Codex/ChatGPT), and
74
+ Codex/ChatGPT, and ZCode), and
68
75
  4. registers that copy using the client's own CLI where available, or with an
69
76
  automatic backup before editing its documented user config. It prepares all
70
77
  of those standard config paths, so an app installed later does not require
@@ -205,6 +212,7 @@ received data — check it before concluding a chat does not exist.
205
212
  | Claude Desktop | macOS uses the working local JSON registration. On Windows, run `install` for browser sync, then install the official `llmnesia.mcpb` once in Claude Desktop; Claude exposes no supported external installation API for custom desktop extensions. |
206
213
  | Cursor | Edits `~/.cursor/mcp.json`, preparing it in advance if Cursor is installed later. |
207
214
  | Codex / ChatGPT desktop | Runs `codex mcp add` with the absolute runtime paths when available. If the bundled CLI is unavailable, the installer safely edits `~/.codex/config.toml` itself and backs up existing settings first, including preparing it for a later installation. Read-only LLMnesia tools are configured to run without repeated approval prompts; writes still ask. |
215
+ | ZCode | Edits the user-scope `~/.zcode/cli/config.json`, adding the server under ZCode's nested `mcp.servers` key with a backup first, and prepares that file in advance if ZCode is installed later. ZCode validates this file strictly and connects servers from it automatically at session start. |
208
216
  | Others (Windsurf, Zed, LM Studio, …) | Prints the generic stdio block for you to paste. |
209
217
 
210
218
  **Cloud-only clients cannot reach a local stdio server** (web ChatGPT, Lovable,
@@ -3,7 +3,7 @@ import { createRequire as __llmnesiaCreateRequire } from "node:module";
3
3
  const require = __llmnesiaCreateRequire(import.meta.url);
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-CMQHGLAD.js";
6
+ } from "./chunk-RJJRIPHA.js";
7
7
 
8
8
  // src/config.ts
9
9
  import { readFileSync } from "fs";
@@ -198,6 +198,8 @@ function canonicalConversationDoc(c) {
198
198
  indexLevel: c.indexLevel,
199
199
  estimatedBytes: c.estimatedBytes
200
200
  };
201
+ assignIfDefined(out, "localTitle", c.localTitle);
202
+ assignIfDefined(out, "localTitleUpdatedAt", c.localTitleUpdatedAt);
201
203
  assignIfDefined(out, "searchDocLength", c.searchDocLength);
202
204
  assignIfDefined(out, "contentHash", c.contentHash);
203
205
  assignIfDefined(out, "truncated", c.truncated);
@@ -1208,9 +1210,14 @@ var SearchIndex = class {
1208
1210
  // transaction() helper for single upserts, or rebuildFrom's manual one).
1209
1211
  writeRecord(rec) {
1210
1212
  const { conversation, messages } = rec;
1213
+ const displayTitle = conversation.localTitle?.trim() || conversation.title;
1211
1214
  this.deleteFtsRows(conversation.docId);
1212
1215
  this.stmts.deleteConversation.run(conversation.docId);
1213
- const titleText = [conversation.title, conversation.preview].filter(Boolean).join("\n");
1216
+ const titleText = [
1217
+ ...conversation.localTitle ? [conversation.localTitle, conversation.localTitle] : [],
1218
+ conversation.title,
1219
+ conversation.preview
1220
+ ].filter(Boolean).join("\n");
1214
1221
  if (titleText.trim()) {
1215
1222
  this.insertFtsRow(conversation.docId, TITLE_ROW_INDEX, TITLE_ROLE, titleText);
1216
1223
  }
@@ -1222,7 +1229,7 @@ var SearchIndex = class {
1222
1229
  this.stmts.insertConversation.run({
1223
1230
  docId: conversation.docId,
1224
1231
  platform: conversation.platform,
1225
- title: conversation.title,
1232
+ title: displayTitle,
1226
1233
  url: conversation.url,
1227
1234
  createdAt: conversation.createdAt,
1228
1235
  updatedAt: conversation.updatedAt ?? conversation.contentUpdatedAt ?? 0,
@@ -1350,7 +1357,11 @@ var SearchIndex = class {
1350
1357
  }
1351
1358
  hydrateSnippet(row, match) {
1352
1359
  const record = this.corpus?.getFromPlatformSync(row.platform, row.docId);
1353
- const source = row.msgIndex === TITLE_ROW_INDEX ? [record?.conversation.title ?? row.title, record?.conversation.preview ?? row.preview].filter(Boolean).join("\n") : record?.messages.find((message) => message.msgIndex === row.msgIndex)?.text ?? row.preview;
1360
+ const source = row.msgIndex === TITLE_ROW_INDEX ? [
1361
+ record?.conversation.localTitle,
1362
+ record?.conversation.title ?? row.title,
1363
+ record?.conversation.preview ?? row.preview
1364
+ ].filter(Boolean).join("\n") : record?.messages.find((message) => message.msgIndex === row.msgIndex)?.text ?? row.preview;
1354
1365
  this.stmts.clearSnippetFts.run();
1355
1366
  try {
1356
1367
  this.stmts.insertSnippetFts.run(source);
@@ -38,7 +38,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  ));
39
39
 
40
40
  // src/version.ts
41
- var VERSION = true ? "0.2.7" : "0.0.0-dev";
41
+ var VERSION = true ? "0.2.8" : "0.0.0-dev";
42
42
 
43
43
  export {
44
44
  __require,
@@ -17,13 +17,13 @@ import {
17
17
  runtimeBinDir,
18
18
  staleIndexFiles,
19
19
  uninstallRuntime
20
- } from "./chunk-O34A5U2L.js";
20
+ } from "./chunk-FIH7ZNYH.js";
21
21
  import {
22
22
  VERSION,
23
23
  __commonJS,
24
24
  __require,
25
25
  __toESM
26
- } from "./chunk-CMQHGLAD.js";
26
+ } from "./chunk-RJJRIPHA.js";
27
27
 
28
28
  // ../node_modules/isexe/windows.js
29
29
  var require_windows = __commonJS({
@@ -2841,7 +2841,8 @@ function paths(home2, osPlatform2, appData) {
2841
2841
  claudeCode: join4(claudeCodeRoot, ".claude.json"),
2842
2842
  claudeDesktop,
2843
2843
  cursor: join4(home2, ".cursor", "mcp.json"),
2844
- codex: join4(home2, ".codex", "config.toml")
2844
+ codex: join4(home2, ".codex", "config.toml"),
2845
+ zcode: join4(home2, ".zcode", "cli", "config.json")
2845
2846
  };
2846
2847
  }
2847
2848
  async function readJsonFile(path) {
@@ -2873,6 +2874,38 @@ function mcpServersOf(data, configPath) {
2873
2874
  }
2874
2875
  return data.mcpServers;
2875
2876
  }
2877
+ function nestedMcpServersOf(data, configPath) {
2878
+ if (data.mcp === void 0) {
2879
+ return {};
2880
+ }
2881
+ if (!data.mcp || typeof data.mcp !== "object" || Array.isArray(data.mcp)) {
2882
+ throw new Error(`Could not update ${configPath}: "mcp" must be a JSON object. The file was left unchanged.`);
2883
+ }
2884
+ const mcp = data.mcp;
2885
+ if (mcp.servers === void 0) {
2886
+ return {};
2887
+ }
2888
+ if (!mcp.servers || typeof mcp.servers !== "object" || Array.isArray(mcp.servers)) {
2889
+ throw new Error(
2890
+ `Could not update ${configPath}: "mcp.servers" must be a JSON object. The file was left unchanged.`
2891
+ );
2892
+ }
2893
+ return mcp.servers;
2894
+ }
2895
+ function asObject(value) {
2896
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2897
+ }
2898
+ var topLevelServers = {
2899
+ read: (data, configPath) => mcpServersOf(data, configPath),
2900
+ merge: (data, servers) => ({ ...data, mcpServers: servers }),
2901
+ preview: (entry) => JSON.stringify({ mcpServers: { [SERVER_NAME]: entry } }, null, 2)
2902
+ };
2903
+ var zcodeNestedServers = {
2904
+ read: (data, configPath) => nestedMcpServersOf(data, configPath),
2905
+ // Keep any other settings ZCode stores beside `servers` inside its `mcp` key.
2906
+ merge: (data, servers) => ({ ...data, mcp: { ...asObject(data.mcp), servers } }),
2907
+ preview: (entry) => JSON.stringify({ mcp: { servers: { [SERVER_NAME]: entry } } }, null, 2)
2908
+ };
2876
2909
  function isErrnoException(value) {
2877
2910
  return value instanceof Error && "code" in value;
2878
2911
  }
@@ -3091,7 +3124,7 @@ codex mcp add ${SERVER_NAME} -- ${serve.command} ${serve.args.join(" ")}`
3091
3124
  detail: `Ran: codex mcp add ${SERVER_NAME} -- ${serve.command} ${serve.args.join(" ")}`
3092
3125
  };
3093
3126
  }
3094
- async function registerJsonClient(clientLabel, configPath, serve, confirm, now, prepareMissing = false, prepareWhenConfigAbsent = false, includeTransportType = true) {
3127
+ async function registerJsonClient(clientLabel, configPath, serve, confirm, now, prepareMissing = false, prepareWhenConfigAbsent = false, includeTransportType = true, layout = topLevelServers) {
3095
3128
  const configDirExists = await clientDirExists(configPath);
3096
3129
  if (!configDirExists && !prepareMissing) {
3097
3130
  return {
@@ -3117,7 +3150,7 @@ async function registerJsonClient(clientLabel, configPath, serve, confirm, now,
3117
3150
  }
3118
3151
  let servers;
3119
3152
  try {
3120
- servers = mcpServersOf(data, configPath);
3153
+ servers = layout.read(data, configPath);
3121
3154
  } catch (err) {
3122
3155
  return { client: clientLabel, action: "error", detail: errorMessage(err) };
3123
3156
  }
@@ -3126,7 +3159,7 @@ async function registerJsonClient(clientLabel, configPath, serve, confirm, now,
3126
3159
  if (already && JSON.stringify(already) === JSON.stringify(nextEntry)) {
3127
3160
  return { client: clientLabel, action: "skipped", detail: `Already registered at ${configPath}.` };
3128
3161
  }
3129
- const preview = JSON.stringify({ mcpServers: { [SERVER_NAME]: nextEntry } }, null, 2);
3162
+ const preview = layout.preview(nextEntry);
3130
3163
  const message = existed ? `${clientLabel}: update ${configPath} to add the "${SERVER_NAME}" MCP server? Existing servers and settings are preserved.
3131
3164
  Add:
3132
3165
  ${preview}` : `${clientLabel}: create ${configPath} with the "${SERVER_NAME}" MCP server?
@@ -3140,7 +3173,7 @@ ${preview}`;
3140
3173
  const backupPath = `${configPath}.bak-${now()}`;
3141
3174
  await writeFile2(backupPath, raw ?? JSON.stringify(data, null, 2), "utf8");
3142
3175
  }
3143
- const nextData = { ...data, mcpServers: { ...servers, [SERVER_NAME]: nextEntry } };
3176
+ const nextData = layout.merge(data, { ...servers, [SERVER_NAME]: nextEntry });
3144
3177
  await writeFile2(configPath, `${JSON.stringify(nextData, null, 2)}
3145
3178
  `, "utf8");
3146
3179
  return {
@@ -3245,7 +3278,7 @@ async function registerClients(options) {
3245
3278
  const osPlatform2 = options.osPlatform ?? platform2();
3246
3279
  const now = options.now ?? (() => Date.now());
3247
3280
  const prepareMissing = options.prepareMissingDesktopApps === true;
3248
- const { claudeCode, claudeDesktop, cursor, codex } = paths(home2, osPlatform2, options.appData);
3281
+ const { claudeCode, claudeDesktop, cursor, codex, zcode } = paths(home2, osPlatform2, options.appData);
3249
3282
  const results = [];
3250
3283
  const safe = async (client, fn) => {
3251
3284
  try {
@@ -3292,6 +3325,22 @@ async function registerClients(options) {
3292
3325
  () => registerCodex(options.serve, options.codexCli ?? realCodexCli, codex, options.confirm, now, prepareMissing)
3293
3326
  )
3294
3327
  );
3328
+ results.push(
3329
+ await safe(
3330
+ "ZCode",
3331
+ () => registerJsonClient(
3332
+ "ZCode",
3333
+ zcode,
3334
+ options.serve,
3335
+ options.confirm,
3336
+ now,
3337
+ prepareMissing,
3338
+ false,
3339
+ true,
3340
+ zcodeNestedServers
3341
+ )
3342
+ )
3343
+ );
3295
3344
  results.push({
3296
3345
  client: "Other MCP clients (Windsurf, Zed, LM Studio, ...)",
3297
3346
  action: "printed",
@@ -3368,7 +3417,7 @@ async function unregisterCodex(cli, configPath, now) {
3368
3417
  return { client: "Codex", action: "error", detail: `codex mcp remove failed: ${errorMessage(err)}` };
3369
3418
  }
3370
3419
  }
3371
- async function unregisterJsonClient(clientLabel, configPath, now) {
3420
+ async function unregisterJsonClient(clientLabel, configPath, now, layout = topLevelServers) {
3372
3421
  let data;
3373
3422
  let existed;
3374
3423
  let raw;
@@ -3382,7 +3431,7 @@ async function unregisterJsonClient(clientLabel, configPath, now) {
3382
3431
  }
3383
3432
  let servers;
3384
3433
  try {
3385
- servers = mcpServersOf(data, configPath);
3434
+ servers = layout.read(data, configPath);
3386
3435
  } catch (err) {
3387
3436
  return { client: clientLabel, action: "error", detail: errorMessage(err) };
3388
3437
  }
@@ -3393,7 +3442,7 @@ async function unregisterJsonClient(clientLabel, configPath, now) {
3393
3442
  try {
3394
3443
  await writeFile2(backupPath, raw ?? JSON.stringify(data, null, 2), "utf8");
3395
3444
  const { [SERVER_NAME]: _removed, ...rest } = servers;
3396
- const nextData = { ...data, mcpServers: rest };
3445
+ const nextData = layout.merge(data, rest);
3397
3446
  await writeFile2(configPath, `${JSON.stringify(nextData, null, 2)}
3398
3447
  `, "utf8");
3399
3448
  return { client: clientLabel, action: "registered", detail: `Removed from ${configPath} (backup: ${backupPath}).` };
@@ -3405,7 +3454,7 @@ async function unregisterClients(options = {}) {
3405
3454
  const home2 = options.home ?? homedir2();
3406
3455
  const osPlatform2 = options.osPlatform ?? platform2();
3407
3456
  const now = options.now ?? (() => Date.now());
3408
- const { claudeDesktop, cursor, codex } = paths(home2, osPlatform2);
3457
+ const { claudeDesktop, cursor, codex, zcode } = paths(home2, osPlatform2);
3409
3458
  const safe = async (client, fn) => {
3410
3459
  try {
3411
3460
  return await fn();
@@ -3421,6 +3470,7 @@ async function unregisterClients(options = {}) {
3421
3470
  }
3422
3471
  results.push(await safe("Cursor", () => unregisterJsonClient("Cursor", cursor, now)));
3423
3472
  results.push(await safe("Codex", () => unregisterCodex(options.codexCli ?? realCodexCli, codex, now)));
3473
+ results.push(await safe("ZCode", () => unregisterJsonClient("ZCode", zcode, now, zcodeNestedServers)));
3424
3474
  results.push({
3425
3475
  client: "Other MCP clients",
3426
3476
  action: "skipped",
@@ -3592,7 +3642,7 @@ function checkAutoUpdate(home2, env) {
3592
3642
  const latest = state.lastSeenVersion ? `; latest published ${state.lastSeenVersion}` : "";
3593
3643
  return { name, status: "ok", detail: `On. Last checked ${checkedAt}${latest}.` };
3594
3644
  }
3595
- async function checkJsonClient(label, configPath) {
3645
+ async function checkJsonClient(label, configPath, nestedServers = false) {
3596
3646
  if (!existsSync(dirname5(configPath))) {
3597
3647
  return { name: label, status: "warn", detail: `Not installed (no ${dirname5(configPath)}).` };
3598
3648
  }
@@ -3605,7 +3655,8 @@ async function checkJsonClient(label, configPath) {
3605
3655
  }
3606
3656
  try {
3607
3657
  const data = JSON.parse(readFileSync(configPath, "utf8"));
3608
- const entry = data.mcpServers?.[SERVER_NAME];
3658
+ const servers = nestedServers ? data.mcp?.servers : data.mcpServers;
3659
+ const entry = servers?.[SERVER_NAME];
3609
3660
  if (!entry) {
3610
3661
  return {
3611
3662
  name: label,
@@ -3741,6 +3792,7 @@ async function runDoctor(options = {}) {
3741
3792
  await run("Cursor", () => checkJsonClient("Cursor", join5(home2, ".cursor", "mcp.json")));
3742
3793
  await run("Claude Code", () => checkClaudeCode(claudeCli));
3743
3794
  await run("Codex", () => checkCodex(home2, codexCli));
3795
+ await run("ZCode", () => checkJsonClient("ZCode", join5(home2, ".zcode", "cli", "config.json"), true));
3744
3796
  checks.push({
3745
3797
  name: "Reminder",
3746
3798
  status: "warn",
@@ -3760,7 +3812,8 @@ Usage: llmnesia-mcp <command> [options]
3760
3812
 
3761
3813
  Setup:
3762
3814
  install [ext-id...] Install the native host + register this MCP server with
3763
- detected clients (Claude Code/Desktop, Cursor, Codex, ...)
3815
+ detected clients (Claude Code/Desktop, Cursor, Codex,
3816
+ ZCode, ...)
3764
3817
  doctor Diagnose: host installed? corpus present? clients wired?
3765
3818
  uninstall Remove native host + client registrations (never deletes
3766
3819
  your corpus \u2014 see the printed path if you want that too)
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __llmnesiaCreateRequire } from "node:module";
3
3
  const require = __llmnesiaCreateRequire(import.meta.url);
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-CMQHGLAD.js";
6
+ } from "./chunk-RJJRIPHA.js";
7
7
 
8
8
  // src/nodeDownload.ts
9
9
  var NODE_DOWNLOAD_URLS = {
@@ -141,7 +141,7 @@ async function bootstrap() {
141
141
  await guideUnsupportedNodeVersion(process.versions.node);
142
142
  return 1;
143
143
  }
144
- const { main } = await import("./cli-XQUMZ3BU.js");
144
+ const { main } = await import("./cli-D2IVH5S2.js");
145
145
  return main();
146
146
  }
147
147
  bootstrap().then((code) => {
Binary file
package/dist/mcp.js CHANGED
@@ -12,13 +12,13 @@ import {
12
12
  resolveCorpusDir,
13
13
  runAutomaticMaintenance,
14
14
  scheduleSelfUpdate
15
- } from "./chunk-O34A5U2L.js";
15
+ } from "./chunk-FIH7ZNYH.js";
16
16
  import {
17
17
  VERSION,
18
18
  __commonJS,
19
19
  __export,
20
20
  __toESM
21
- } from "./chunk-CMQHGLAD.js";
21
+ } from "./chunk-RJJRIPHA.js";
22
22
 
23
23
  // ../node_modules/ajv/dist/compile/codegen/code.js
24
24
  var require_code = __commonJS({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnesia/mcp",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "comment:mcpName": "The MCP Registry's ownership marker: it must equal server.json's `name`, and the namespace must match how you authenticate with mcp-publisher. `com.llmnesia/...` requires DNS auth (a TXT record on llmnesia.com) — see docs/MCP_INSTALLER_BOARD.md.",
package/server.json CHANGED
@@ -3,13 +3,13 @@
3
3
  "name": "com.llmnesia/mcp",
4
4
  "title": "LLMnesia",
5
5
  "description": "Search your AI chat history from Claude, Cursor, Codex, and other MCP clients.",
6
- "version": "0.2.7",
6
+ "version": "0.2.8",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "registryBaseUrl": "https://registry.npmjs.org",
11
11
  "identifier": "@llmnesia/mcp",
12
- "version": "0.2.7",
12
+ "version": "0.2.8",
13
13
  "transport": {
14
14
  "type": "stdio"
15
15
  }