@llmnesia/mcp 0.2.5 → 0.2.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.
@@ -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-BDR4JVMF.js";
6
+ } from "./chunk-YRK2DRGG.js";
7
7
 
8
8
  // src/config.ts
9
9
  import { readFileSync } from "fs";
@@ -13,14 +13,22 @@ var CORPUS_FORMAT_VERSION = 2;
13
13
  function nativeHostConfigPath() {
14
14
  return join(homedir(), ".llmnesia", "config.json");
15
15
  }
16
- function readNativeHostCorpusRoot() {
16
+ function readNativeHostConfig() {
17
17
  try {
18
18
  const parsed = JSON.parse(readFileSync(nativeHostConfigPath(), "utf8"));
19
- return typeof parsed.corpusRoot === "string" ? parsed.corpusRoot : void 0;
19
+ return parsed && typeof parsed === "object" ? parsed : void 0;
20
20
  } catch {
21
21
  return void 0;
22
22
  }
23
23
  }
24
+ function readNativeHostCorpusRoot() {
25
+ const root = readNativeHostConfig()?.corpusRoot;
26
+ return typeof root === "string" ? root : void 0;
27
+ }
28
+ function readNativeHostViewerExtensionId() {
29
+ const id = readNativeHostConfig()?.viewerExtensionId;
30
+ return typeof id === "string" ? id : void 0;
31
+ }
24
32
  function resolveCorpusDir(explicit) {
25
33
  const raw = explicit ?? process.env.LLMNESIA_CORPUS_DIR ?? readNativeHostCorpusRoot() ?? "~/.llmnesia/corpus";
26
34
  return expandHome(raw);
@@ -2063,6 +2071,7 @@ function describeOutcome(outcome) {
2063
2071
  }
2064
2072
 
2065
2073
  export {
2074
+ readNativeHostViewerExtensionId,
2066
2075
  resolveCorpusDir,
2067
2076
  staleIndexFiles,
2068
2077
  Corpus,
@@ -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.5" : "0.0.0-dev";
41
+ var VERSION = true ? "0.2.6" : "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-MTPXE734.js";
20
+ } from "./chunk-HL5QE57W.js";
21
21
  import {
22
22
  VERSION,
23
23
  __commonJS,
24
24
  __require,
25
25
  __toESM
26
- } from "./chunk-BDR4JVMF.js";
26
+ } from "./chunk-YRK2DRGG.js";
27
27
 
28
28
  // ../node_modules/isexe/windows.js
29
29
  var require_windows = __commonJS({
@@ -687,6 +687,7 @@ async function installNativeHost({ ids = [], corpus, codexHome, force = false, q
687
687
  }
688
688
  const allIds = [.../* @__PURE__ */ new Set([STORE_EXTENSION_ID, ...ids])];
689
689
  const allowedOrigins = allIds.map((id) => `chrome-extension://${id}/`);
690
+ const viewerExtensionId = ids[0] ?? STORE_EXTENSION_ID;
690
691
  if (await supportedBrowserCount(isWindows2) === 0) {
691
692
  throw new Error(
692
693
  "LLMnesia couldn't find Chrome, Brave, Edge, or Chromium. Open your browser once so it creates a profile, then retry. Safari and Firefox are not supported yet. Nothing was installed."
@@ -720,7 +721,7 @@ async function installNativeHost({ ids = [], corpus, codexHome, force = false, q
720
721
  corpusRootChanged = false;
721
722
  await writeFile(
722
723
  configPath,
723
- `${JSON.stringify({ ...existing, corpusRoot: existingRoot, codexHome: resolvedCodexHome }, null, 2)}
724
+ `${JSON.stringify({ ...existing, corpusRoot: existingRoot, codexHome: resolvedCodexHome, viewerExtensionId }, null, 2)}
724
725
  `,
725
726
  { encoding: "utf8", mode: 384 }
726
727
  );
@@ -732,7 +733,7 @@ async function installNativeHost({ ids = [], corpus, codexHome, force = false, q
732
733
  } else {
733
734
  await writeFile(
734
735
  configPath,
735
- `${JSON.stringify({ ...existing ?? {}, corpusRoot, codexHome: resolvedCodexHome }, null, 2)}
736
+ `${JSON.stringify({ ...existing ?? {}, corpusRoot, codexHome: resolvedCodexHome, viewerExtensionId }, null, 2)}
736
737
  `,
737
738
  { encoding: "utf8", mode: 384 }
738
739
  );
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-BDR4JVMF.js";
6
+ } from "./chunk-YRK2DRGG.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-OXKBAUBE.js");
144
+ const { main } = await import("./cli-DRB4DVH3.js");
145
145
  return main();
146
146
  }
147
147
  bootstrap().then((code) => {
Binary file
package/dist/mcp.js CHANGED
@@ -8,16 +8,17 @@ import {
8
8
  drainInbox,
9
9
  openSearchIndex,
10
10
  pendingInboxFiles,
11
+ readNativeHostViewerExtensionId,
11
12
  resolveCorpusDir,
12
13
  runAutomaticMaintenance,
13
14
  scheduleSelfUpdate
14
- } from "./chunk-MTPXE734.js";
15
+ } from "./chunk-HL5QE57W.js";
15
16
  import {
16
17
  VERSION,
17
18
  __commonJS,
18
19
  __export,
19
20
  __toESM
20
- } from "./chunk-BDR4JVMF.js";
21
+ } from "./chunk-YRK2DRGG.js";
21
22
 
22
23
  // ../node_modules/ajv/dist/compile/codegen/code.js
23
24
  var require_code = __commonJS({
@@ -21677,7 +21678,20 @@ var LazyResource = class {
21677
21678
  function jsonResult(payload) {
21678
21679
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
21679
21680
  }
21680
- var SOURCE_LINK_INSTRUCTION = "When answering from these conversations, always include a Sources section with a clickable Markdown link to every original chat you relied on whose `url` is non-empty. Do this automatically; do not wait for the user to ask for links.";
21681
+ var SOURCE_LINK_INSTRUCTION = "When answering from these conversations, always include a Sources section with a clickable Markdown link to every chat you relied on. Link its `sourceUrl`: this is the original chat URL when available, otherwise its local LLMnesia Viewer URL. Do this automatically; do not wait for the user to ask for links.";
21682
+ var STORE_EXTENSION_ID = "leekfgbdojiaabifbjbbgiiclannjdkf";
21683
+ var EXTENSION_ID_RE = /^[a-p]{32}$/;
21684
+ function viewerExtensionId() {
21685
+ const configured = process.env.LLMNESIA_VIEWER_EXTENSION_ID?.trim() ?? readNativeHostViewerExtensionId();
21686
+ return configured && EXTENSION_ID_RE.test(configured) ? configured : STORE_EXTENSION_ID;
21687
+ }
21688
+ function viewerUrl(docId) {
21689
+ return `chrome-extension://${viewerExtensionId()}/viewer.html?docId=${encodeURIComponent(docId)}`;
21690
+ }
21691
+ function sourceLinks(docId, originalUrl) {
21692
+ const localViewerUrl = viewerUrl(docId);
21693
+ return originalUrl ? { viewerUrl: localViewerUrl, sourceUrl: originalUrl, sourceKind: "original" } : { viewerUrl: localViewerUrl, sourceUrl: localViewerUrl, sourceKind: "viewer" };
21694
+ }
21681
21695
  function sourcedJsonResult(payload) {
21682
21696
  return jsonResult({ responseInstruction: SOURCE_LINK_INSTRUCTION, ...payload });
21683
21697
  }
@@ -21743,14 +21757,14 @@ async function runServe() {
21743
21757
  const server = new McpServer(
21744
21758
  { name: "llmnesia", version: VERSION },
21745
21759
  {
21746
- instructions: "LLMnesia is the user's searchable archive of past AI chats. Whenever the user asks about 'my chats', 'my conversations', or anything they previously said, discussed, decided, or worked on, use LLMnesia before built-in task/thread lists, local file scans, or memory, unless they explicitly ask only about Codex tasks. Use list_conversations with sort='oldest' for their earliest chat, search_conversations to find relevant prior chats, get_conversation to read a full transcript, and list_recent to see what they worked on lately. Search and list results carry a `corpus` block saying when the corpus last received data; if a chat the user expects is missing, check corpus_status before concluding it does not exist. Whenever you answer using LLMnesia, always include a Sources section with clickable links to the original chats you used when their `url` fields are non-empty; never wait for the user to ask."
21760
+ instructions: "LLMnesia is the user's searchable archive of past AI chats. Whenever the user asks about 'my chats', 'my conversations', or anything they previously said, discussed, decided, or worked on, use LLMnesia before built-in task/thread lists, local file scans, or memory, unless they explicitly ask only about Codex tasks. Use list_conversations with sort='oldest' for their earliest chat, search_conversations to find relevant prior chats, get_conversation to read a full transcript, and list_recent to see what they worked on lately. Search and list results carry a `corpus` block saying when the corpus last received data; if a chat the user expects is missing, check corpus_status before concluding it does not exist. Whenever you answer using LLMnesia, always include a Sources section with clickable links to the chats you used. Link each result's `sourceUrl`, which is its original URL when available or its local LLMnesia Viewer URL otherwise; never wait for the user to ask."
21747
21761
  }
21748
21762
  );
21749
21763
  server.registerTool(
21750
21764
  "search_conversations",
21751
21765
  {
21752
21766
  title: "Search conversations",
21753
- description: "Full-text search across the user's past AI conversations. Call this whenever the user asks what they said, thought, discussed, decided, or worked on previously \u2014 even if the question doesn't say 'search' \u2014 rather than answering from conversation memory alone or claiming no record exists. Returns matching conversations with a highlighted snippet, ranked by relevance. Defaults to recall-first matching (any term); pass match_mode to answer questions where a wrong hit matters, such as whether or how often something was discussed. Every result includes its original-chat URL when available; always link the chats used in the answer.",
21767
+ description: "Full-text search across the user's past AI conversations. Call this whenever the user asks what they said, thought, discussed, decided, or worked on previously \u2014 even if the question doesn't say 'search' \u2014 rather than answering from conversation memory alone or claiming no record exists. Returns matching conversations with a highlighted snippet, ranked by relevance. Defaults to recall-first matching (any term); pass match_mode to answer questions where a wrong hit matters, such as whether or how often something was discussed. Every result includes `sourceUrl`: the original chat URL when available, otherwise its local LLMnesia Viewer URL. Always link the chats used in the answer.",
21754
21768
  inputSchema: {
21755
21769
  query: external_exports.string().describe("Search terms (natural words; FTS operators are not needed)."),
21756
21770
  match_mode: external_exports.enum(["any", "all", "phrase"]).optional().describe(
@@ -21789,6 +21803,7 @@ async function runServe() {
21789
21803
  title: h.title,
21790
21804
  platform: h.platform,
21791
21805
  url: h.url,
21806
+ ...sourceLinks(h.docId, h.url),
21792
21807
  createdAt: h.createdAt,
21793
21808
  score: Number(h.score.toFixed(4)),
21794
21809
  snippet: h.snippet,
@@ -21807,7 +21822,7 @@ async function runServe() {
21807
21822
  "get_conversation",
21808
21823
  {
21809
21824
  title: "Get conversation",
21810
- description: "Return the full transcript of one conversation by its id (the `id` from search results). The conversation includes its original-chat URL when available; always link it in an answer based on this transcript.",
21825
+ description: "Return the full transcript of one conversation by its id (the `id` from search results). The conversation includes `sourceUrl`: its original-chat URL when available, otherwise its local LLMnesia Viewer URL. Always link it in an answer based on this transcript.",
21811
21826
  inputSchema: {
21812
21827
  id: external_exports.string().describe("The conversation id (docId), e.g. 'claude:abc-123'.")
21813
21828
  },
@@ -21824,7 +21839,10 @@ async function runServe() {
21824
21839
  return errorResult(`No conversation found with id '${args.id}'.`);
21825
21840
  }
21826
21841
  return sourcedJsonResult({
21827
- conversation: record2.conversation,
21842
+ conversation: {
21843
+ ...record2.conversation,
21844
+ ...sourceLinks(record2.conversation.docId, record2.conversation.url)
21845
+ },
21828
21846
  messages: record2.messages.map((m) => ({
21829
21847
  index: m.msgIndex,
21830
21848
  role: m.role,
@@ -21838,7 +21856,7 @@ async function runServe() {
21838
21856
  "list_recent",
21839
21857
  {
21840
21858
  title: "List recent conversations",
21841
- description: "List the user's most recently updated conversations (title + summary + original-chat URL), newest first. Always link the chats used in the answer.",
21859
+ description: "List the user's most recently updated conversations (title + summary + source URL), newest first. `sourceUrl` is the original chat URL when available, otherwise its local LLMnesia Viewer URL. Always link the chats used in the answer.",
21842
21860
  inputSchema: {
21843
21861
  limit: external_exports.number().int().min(1).max(100).optional().describe("How many to return (default 10).")
21844
21862
  },
@@ -21861,6 +21879,7 @@ async function runServe() {
21861
21879
  title: e.title,
21862
21880
  platform: e.platform,
21863
21881
  url: e.url,
21882
+ ...sourceLinks(e.docId, e.url),
21864
21883
  updatedAt: e.updatedAt,
21865
21884
  messageCount: e.messageCount,
21866
21885
  pinned: e.pinned,
@@ -21877,7 +21896,7 @@ async function runServe() {
21877
21896
  "list_conversations",
21878
21897
  {
21879
21898
  title: "List conversations by date",
21880
- description: "List conversation metadata in chronological order. Use sort='oldest' for the user's earliest or oldest chat, sort='newest' for most recently created chats, and sort='recent' for most recently updated chats. This is the complete corpus chronology, so do not scan LLMnesia's private files with shell commands. Entries include original-chat URLs when available; always link the chats used in the answer.",
21899
+ description: "List conversation metadata in chronological order. Use sort='oldest' for the user's earliest or oldest chat, sort='newest' for most recently created chats, and sort='recent' for most recently updated chats. This is the complete corpus chronology, so do not scan LLMnesia's private files with shell commands. Entries include `sourceUrl`: the original chat URL when available, otherwise the local LLMnesia Viewer URL. Always link the chats used in the answer.",
21881
21900
  inputSchema: {
21882
21901
  sort: external_exports.enum(["recent", "oldest", "newest"]).optional().describe("Chronology: recent (default, updated), oldest (created), or newest (created)."),
21883
21902
  limit: external_exports.number().int().min(1).max(100).optional().describe("How many to return (default 10).")
@@ -21902,6 +21921,7 @@ async function runServe() {
21902
21921
  title: e.title,
21903
21922
  platform: e.platform,
21904
21923
  url: e.url,
21924
+ ...sourceLinks(e.docId, e.url),
21905
21925
  createdAt: e.createdAt,
21906
21926
  updatedAt: e.updatedAt,
21907
21927
  messageCount: e.messageCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnesia/mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
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.5",
6
+ "version": "0.2.6",
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.5",
12
+ "version": "0.2.6",
13
13
  "transport": {
14
14
  "type": "stdio"
15
15
  }