@llmnesia/mcp 0.1.10 → 0.1.13
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 +45 -2
- package/dist/{chunk-LA3BU5ZR.js → chunk-7DAYOGB6.js} +1 -1
- package/dist/{cli-VUTW2WGY.js → cli-OJM47LQR.js} +16 -8
- package/dist/cli.js +2 -2
- package/dist/llmnesia.mcpb +0 -0
- package/dist/mcp.js +137 -65
- package/package.json +6 -3
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -48,13 +48,39 @@ The installer:
|
|
|
48
48
|
1. sets up the native host that bridges the browser extension to your local
|
|
49
49
|
corpus,
|
|
50
50
|
2. copies the server into `~/.llmnesia/bin` so it has a permanent home,
|
|
51
|
-
3. sets up the supported desktop AI apps (Claude Code,
|
|
51
|
+
3. sets up the supported desktop AI apps (Claude Code, Cursor,
|
|
52
52
|
and Codex/ChatGPT), and
|
|
53
53
|
4. registers that copy using the client's own CLI where available, or with an
|
|
54
54
|
automatic backup before editing its documented user config. It prepares all
|
|
55
55
|
of those standard config paths, so an app installed later does not require
|
|
56
56
|
running setup again.
|
|
57
57
|
|
|
58
|
+
Claude Desktop on Windows does not provide a supported API for third-party
|
|
59
|
+
installers to add a local desktop extension. LLMnesia therefore leaves any
|
|
60
|
+
existing Claude configuration untouched rather than claiming that Claude has
|
|
61
|
+
been connected.
|
|
62
|
+
|
|
63
|
+
### Claude Desktop on Windows (current one-time manual step)
|
|
64
|
+
|
|
65
|
+
Until LLMnesia is available in Claude's extension directory, Claude Desktop on
|
|
66
|
+
Windows has one extra, manual step. First run `install` above so the local
|
|
67
|
+
browser bridge and corpus exist. Then download the official LLMnesia
|
|
68
|
+
`llmnesia.mcpb` desktop-extension file from LLMnesia and use Claude Desktop's
|
|
69
|
+
**Settings → Extensions → Install extension** flow to install it. Fully quit
|
|
70
|
+
and reopen Claude Desktop afterwards.
|
|
71
|
+
|
|
72
|
+
This is deliberately separate from the npm command: Claude does not provide a
|
|
73
|
+
supported Windows API for another installer to silently add an `.mcpb`
|
|
74
|
+
extension. The command therefore never edits Claude's internal extension state
|
|
75
|
+
or reports a false success. The packaged extension itself is read-only: it can
|
|
76
|
+
search, list, and read the local corpus but cannot modify it.
|
|
77
|
+
|
|
78
|
+
The future Claude Directory listing is a convenience distribution route, not a
|
|
79
|
+
dependency for the general LLMnesia release. It will replace this one manual
|
|
80
|
+
Claude step with Claude's own in-app install button if Anthropic selects the
|
|
81
|
+
extension. See [`docs/CLAUDE_DESKTOP_RELEASE_PLAN.md`](../docs/CLAUDE_DESKTOP_RELEASE_PLAN.md)
|
|
82
|
+
for the release and directory-submission plan.
|
|
83
|
+
|
|
58
84
|
The `install` command is the approval to perform those setup steps. It does not
|
|
59
85
|
interrupt setup with a separate prompt for every detected app, and it never
|
|
60
86
|
prints existing client configurations or their credentials.
|
|
@@ -121,7 +147,7 @@ received data — check it before concluding a chat does not exist.
|
|
|
121
147
|
| Client | How `install` wires it |
|
|
122
148
|
| --- | --- |
|
|
123
149
|
| Claude Code | Runs `claude mcp add --scope user` with the absolute Node and installed runtime paths when available. Otherwise it writes the documented user-scope `~/.claude.json` entry, so Windows users do not need the `claude` command on PATH. |
|
|
124
|
-
| Claude Desktop |
|
|
150
|
+
| 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. |
|
|
125
151
|
| Cursor | Edits `~/.cursor/mcp.json`, preparing it in advance if Cursor is installed later. |
|
|
126
152
|
| 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. |
|
|
127
153
|
| Others (Windsurf, Zed, LM Studio, …) | Prints the generic stdio block for you to paste. |
|
|
@@ -178,6 +204,23 @@ llmnesia-mcp --version # print the installed version
|
|
|
178
204
|
|
|
179
205
|
Questions and bug reports: [hello@llmnesia.com](mailto:hello@llmnesia.com).
|
|
180
206
|
|
|
207
|
+
## Privacy Policy
|
|
208
|
+
|
|
209
|
+
The MCP server reads and writes only the LLMnesia corpus stored on the user's
|
|
210
|
+
own computer. It does not upload conversation content, titles, URLs, prompts,
|
|
211
|
+
responses, or search queries to LLMnesia or to any third party, and it makes no
|
|
212
|
+
analytics or advertising requests of its own.
|
|
213
|
+
|
|
214
|
+
The browser extension separately sends limited, non-content product analytics
|
|
215
|
+
to PostHog. Conversation data and MCP searches are excluded. The complete
|
|
216
|
+
policy explains collection, local storage, third-party processing, retention,
|
|
217
|
+
deletion, and contact details:
|
|
218
|
+
[LLMnesia Privacy Policy](https://www.llmnesia.com/privacy-policy).
|
|
219
|
+
|
|
220
|
+
The local MCP corpus remains on the device until the user deletes it. Running
|
|
221
|
+
`uninstall` deliberately leaves that corpus intact so uninstalling the helper
|
|
222
|
+
cannot accidentally destroy the user's history.
|
|
223
|
+
|
|
181
224
|
## License
|
|
182
225
|
|
|
183
226
|
MIT — see [LICENSE](./LICENSE).
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
__commonJS,
|
|
16
16
|
__require,
|
|
17
17
|
__toESM
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7DAYOGB6.js";
|
|
19
19
|
|
|
20
20
|
// ../node_modules/isexe/windows.js
|
|
21
21
|
var require_windows = __commonJS({
|
|
@@ -3258,12 +3258,20 @@ async function registerClients(options) {
|
|
|
3258
3258
|
)
|
|
3259
3259
|
);
|
|
3260
3260
|
if (claudeDesktop) {
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
"Claude Desktop",
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3261
|
+
if (osPlatform2 === "win32") {
|
|
3262
|
+
results.push({
|
|
3263
|
+
client: "Claude Desktop",
|
|
3264
|
+
action: "skipped",
|
|
3265
|
+
detail: "Automatic Claude Desktop setup is not available on Windows; existing configuration was left unchanged."
|
|
3266
|
+
});
|
|
3267
|
+
} else {
|
|
3268
|
+
results.push(
|
|
3269
|
+
await safe(
|
|
3270
|
+
"Claude Desktop",
|
|
3271
|
+
() => registerJsonClient("Claude Desktop", claudeDesktop, options.serve, options.confirm, now, prepareMissing, false, false)
|
|
3272
|
+
)
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3267
3275
|
}
|
|
3268
3276
|
results.push(
|
|
3269
3277
|
await safe("Cursor", () => registerJsonClient("Cursor", cursor, options.serve, options.confirm, now, prepareMissing))
|
|
@@ -3849,7 +3857,7 @@ function summarizeClientRegistrations(results) {
|
|
|
3849
3857
|
return { connected, prepared, errors };
|
|
3850
3858
|
}
|
|
3851
3859
|
function formatInstallSuccess() {
|
|
3852
|
-
return "\nSetup complete.\nFully close and reopen your browser and any desktop AI app you use.\nLLMnesia will be available in
|
|
3860
|
+
return "\nSetup complete.\nFully close and reopen your browser and any desktop AI app you use.\nLLMnesia will be available in the desktop AI apps connected during setup.\nLLMnesia Settings will confirm when your history is ready.\n";
|
|
3853
3861
|
}
|
|
3854
3862
|
async function runInstall(positionals, options, flags) {
|
|
3855
3863
|
let hadProblems = false;
|
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-
|
|
6
|
+
} from "./chunk-7DAYOGB6.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-
|
|
144
|
+
const { main } = await import("./cli-OJM47LQR.js");
|
|
145
145
|
return main();
|
|
146
146
|
}
|
|
147
147
|
bootstrap().then((code) => {
|
|
Binary file
|
package/dist/mcp.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
__commonJS,
|
|
14
14
|
__export,
|
|
15
15
|
__toESM
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7DAYOGB6.js";
|
|
17
17
|
|
|
18
18
|
// ../node_modules/ajv/dist/compile/codegen/code.js
|
|
19
19
|
var require_code = __commonJS({
|
|
@@ -3211,8 +3211,8 @@ var require_utils = __commonJS({
|
|
|
3211
3211
|
}
|
|
3212
3212
|
return ind;
|
|
3213
3213
|
}
|
|
3214
|
-
function removeDotSegments(
|
|
3215
|
-
let input =
|
|
3214
|
+
function removeDotSegments(path2) {
|
|
3215
|
+
let input = path2;
|
|
3216
3216
|
const output = [];
|
|
3217
3217
|
let nextSlash = -1;
|
|
3218
3218
|
let len = 0;
|
|
@@ -3464,8 +3464,8 @@ var require_schemes = __commonJS({
|
|
|
3464
3464
|
wsComponent.secure = void 0;
|
|
3465
3465
|
}
|
|
3466
3466
|
if (wsComponent.resourceName) {
|
|
3467
|
-
const [
|
|
3468
|
-
wsComponent.path =
|
|
3467
|
+
const [path2, query] = wsComponent.resourceName.split("?");
|
|
3468
|
+
wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
|
|
3469
3469
|
wsComponent.query = query;
|
|
3470
3470
|
wsComponent.resourceName = void 0;
|
|
3471
3471
|
}
|
|
@@ -7375,8 +7375,8 @@ function getErrorMap() {
|
|
|
7375
7375
|
|
|
7376
7376
|
// ../node_modules/zod/v3/helpers/parseUtil.js
|
|
7377
7377
|
var makeIssue = (params) => {
|
|
7378
|
-
const { data, path, errorMaps, issueData } = params;
|
|
7379
|
-
const fullPath = [...
|
|
7378
|
+
const { data, path: path2, errorMaps, issueData } = params;
|
|
7379
|
+
const fullPath = [...path2, ...issueData.path || []];
|
|
7380
7380
|
const fullIssue = {
|
|
7381
7381
|
...issueData,
|
|
7382
7382
|
path: fullPath
|
|
@@ -7492,11 +7492,11 @@ var errorUtil;
|
|
|
7492
7492
|
|
|
7493
7493
|
// ../node_modules/zod/v3/types.js
|
|
7494
7494
|
var ParseInputLazyPath = class {
|
|
7495
|
-
constructor(parent, value,
|
|
7495
|
+
constructor(parent, value, path2, key) {
|
|
7496
7496
|
this._cachedPath = [];
|
|
7497
7497
|
this.parent = parent;
|
|
7498
7498
|
this.data = value;
|
|
7499
|
-
this._path =
|
|
7499
|
+
this._path = path2;
|
|
7500
7500
|
this._key = key;
|
|
7501
7501
|
}
|
|
7502
7502
|
get path() {
|
|
@@ -11133,10 +11133,10 @@ function assignProp(target, prop, value) {
|
|
|
11133
11133
|
configurable: true
|
|
11134
11134
|
});
|
|
11135
11135
|
}
|
|
11136
|
-
function getElementAtPath(obj,
|
|
11137
|
-
if (!
|
|
11136
|
+
function getElementAtPath(obj, path2) {
|
|
11137
|
+
if (!path2)
|
|
11138
11138
|
return obj;
|
|
11139
|
-
return
|
|
11139
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
11140
11140
|
}
|
|
11141
11141
|
function promiseAllObject(promisesObj) {
|
|
11142
11142
|
const keys = Object.keys(promisesObj);
|
|
@@ -11456,11 +11456,11 @@ function aborted(x, startIndex = 0) {
|
|
|
11456
11456
|
}
|
|
11457
11457
|
return false;
|
|
11458
11458
|
}
|
|
11459
|
-
function prefixIssues(
|
|
11459
|
+
function prefixIssues(path2, issues) {
|
|
11460
11460
|
return issues.map((iss) => {
|
|
11461
11461
|
var _a;
|
|
11462
11462
|
(_a = iss).path ?? (_a.path = []);
|
|
11463
|
-
iss.path.unshift(
|
|
11463
|
+
iss.path.unshift(path2);
|
|
11464
11464
|
return iss;
|
|
11465
11465
|
});
|
|
11466
11466
|
}
|
|
@@ -14871,11 +14871,11 @@ function normalizeObjectSchema(schema) {
|
|
|
14871
14871
|
}
|
|
14872
14872
|
return void 0;
|
|
14873
14873
|
}
|
|
14874
|
-
function getDotPath(
|
|
14875
|
-
if (
|
|
14874
|
+
function getDotPath(path2) {
|
|
14875
|
+
if (path2.length === 0) {
|
|
14876
14876
|
return "object root";
|
|
14877
14877
|
}
|
|
14878
|
-
return
|
|
14878
|
+
return path2.reduce((acc, seg, index) => {
|
|
14879
14879
|
if (index === 0) {
|
|
14880
14880
|
return String(seg);
|
|
14881
14881
|
}
|
|
@@ -21131,6 +21131,11 @@ var StdioServerTransport = class {
|
|
|
21131
21131
|
}
|
|
21132
21132
|
};
|
|
21133
21133
|
|
|
21134
|
+
// src/mcp.ts
|
|
21135
|
+
import { realpathSync } from "fs";
|
|
21136
|
+
import { fileURLToPath } from "url";
|
|
21137
|
+
import path from "path";
|
|
21138
|
+
|
|
21134
21139
|
// src/saveSchema.ts
|
|
21135
21140
|
import { createHash } from "crypto";
|
|
21136
21141
|
var saveMessageSchema = external_exports.object({
|
|
@@ -21360,6 +21365,51 @@ var CompanionService = class _CompanionService {
|
|
|
21360
21365
|
function noop() {
|
|
21361
21366
|
}
|
|
21362
21367
|
|
|
21368
|
+
// src/lazyResource.ts
|
|
21369
|
+
var ResourcePreparingError = class extends Error {
|
|
21370
|
+
constructor() {
|
|
21371
|
+
super("Resource is still preparing");
|
|
21372
|
+
this.name = "ResourcePreparingError";
|
|
21373
|
+
}
|
|
21374
|
+
};
|
|
21375
|
+
var LazyResource = class {
|
|
21376
|
+
constructor(open) {
|
|
21377
|
+
this.open = open;
|
|
21378
|
+
}
|
|
21379
|
+
open;
|
|
21380
|
+
resource = null;
|
|
21381
|
+
failure;
|
|
21382
|
+
completion = null;
|
|
21383
|
+
start() {
|
|
21384
|
+
if (this.completion) return;
|
|
21385
|
+
this.completion = Promise.resolve().then(() => this.open()).then(
|
|
21386
|
+
(resource) => {
|
|
21387
|
+
this.resource = resource;
|
|
21388
|
+
},
|
|
21389
|
+
(error2) => {
|
|
21390
|
+
this.failure = error2;
|
|
21391
|
+
}
|
|
21392
|
+
);
|
|
21393
|
+
}
|
|
21394
|
+
async get(waitMs) {
|
|
21395
|
+
this.start();
|
|
21396
|
+
if (this.resource) return this.resource;
|
|
21397
|
+
let timeout;
|
|
21398
|
+
await Promise.race([
|
|
21399
|
+
this.completion,
|
|
21400
|
+
new Promise((resolve) => {
|
|
21401
|
+
timeout = setTimeout(resolve, waitMs);
|
|
21402
|
+
})
|
|
21403
|
+
]);
|
|
21404
|
+
if (timeout) clearTimeout(timeout);
|
|
21405
|
+
if (this.resource) return this.resource;
|
|
21406
|
+
if (this.failure) {
|
|
21407
|
+
throw this.failure;
|
|
21408
|
+
}
|
|
21409
|
+
throw new ResourcePreparingError();
|
|
21410
|
+
}
|
|
21411
|
+
};
|
|
21412
|
+
|
|
21363
21413
|
// src/mcp.ts
|
|
21364
21414
|
function jsonResult(payload) {
|
|
21365
21415
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
@@ -21387,13 +21437,25 @@ function parseDate(value) {
|
|
|
21387
21437
|
}
|
|
21388
21438
|
async function runServe() {
|
|
21389
21439
|
const root = resolveCorpusDir(process.env.LLMNESIA_CORPUS_DIR);
|
|
21390
|
-
const service =
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
21440
|
+
const service = new LazyResource(
|
|
21441
|
+
() => CompanionService.open(root, {
|
|
21442
|
+
// stdout is the MCP JSON-RPC channel, so diagnostics go to stderr.
|
|
21443
|
+
onDrainError: (error2) => process.stderr.write(
|
|
21444
|
+
`llmnesia: inbox drain failed, serving the existing index: ${error2 instanceof Error ? error2.message : String(error2)}
|
|
21394
21445
|
`
|
|
21395
|
-
|
|
21396
|
-
|
|
21446
|
+
)
|
|
21447
|
+
})
|
|
21448
|
+
);
|
|
21449
|
+
const withService = async (operation) => {
|
|
21450
|
+
try {
|
|
21451
|
+
return await operation(await service.get(5e3));
|
|
21452
|
+
} catch (err) {
|
|
21453
|
+
if (err instanceof ResourcePreparingError) {
|
|
21454
|
+
return errorResult("LLMnesia is still preparing the local conversation index. Retry this tool shortly.");
|
|
21455
|
+
}
|
|
21456
|
+
return errorResult(`LLMnesia could not open the local conversation index: ${err instanceof Error ? err.message : String(err)}`);
|
|
21457
|
+
}
|
|
21458
|
+
};
|
|
21397
21459
|
const server = new McpServer(
|
|
21398
21460
|
{ name: "llmnesia", version: VERSION },
|
|
21399
21461
|
{
|
|
@@ -21423,7 +21485,7 @@ async function runServe() {
|
|
|
21423
21485
|
openWorldHint: false
|
|
21424
21486
|
}
|
|
21425
21487
|
},
|
|
21426
|
-
async (args) => {
|
|
21488
|
+
async (args) => withService(async (service2) => {
|
|
21427
21489
|
const filters = {
|
|
21428
21490
|
platform: args.platform,
|
|
21429
21491
|
title: args.title,
|
|
@@ -21432,8 +21494,8 @@ async function runServe() {
|
|
|
21432
21494
|
limit: args.limit,
|
|
21433
21495
|
matchMode: args.match_mode
|
|
21434
21496
|
};
|
|
21435
|
-
const hits = await
|
|
21436
|
-
const status = await
|
|
21497
|
+
const hits = await service2.search(args.query, filters);
|
|
21498
|
+
const status = await service2.corpusStatus({ refresh: false });
|
|
21437
21499
|
return jsonResult({
|
|
21438
21500
|
count: hits.length,
|
|
21439
21501
|
corpus: corpusField(status),
|
|
@@ -21450,7 +21512,7 @@ async function runServe() {
|
|
|
21450
21512
|
messageCount: h.messageCount
|
|
21451
21513
|
}))
|
|
21452
21514
|
});
|
|
21453
|
-
}
|
|
21515
|
+
})
|
|
21454
21516
|
);
|
|
21455
21517
|
server.registerTool(
|
|
21456
21518
|
"get_conversation",
|
|
@@ -21467,8 +21529,8 @@ async function runServe() {
|
|
|
21467
21529
|
openWorldHint: false
|
|
21468
21530
|
}
|
|
21469
21531
|
},
|
|
21470
|
-
async (args) => {
|
|
21471
|
-
const record2 = await
|
|
21532
|
+
async (args) => withService(async (service2) => {
|
|
21533
|
+
const record2 = await service2.getConversation(args.id);
|
|
21472
21534
|
if (!record2) {
|
|
21473
21535
|
return errorResult(`No conversation found with id '${args.id}'.`);
|
|
21474
21536
|
}
|
|
@@ -21481,7 +21543,7 @@ async function runServe() {
|
|
|
21481
21543
|
text: m.formattedText ?? m.text
|
|
21482
21544
|
}))
|
|
21483
21545
|
});
|
|
21484
|
-
}
|
|
21546
|
+
})
|
|
21485
21547
|
);
|
|
21486
21548
|
server.registerTool(
|
|
21487
21549
|
"list_recent",
|
|
@@ -21498,9 +21560,9 @@ async function runServe() {
|
|
|
21498
21560
|
openWorldHint: false
|
|
21499
21561
|
}
|
|
21500
21562
|
},
|
|
21501
|
-
async (args) => {
|
|
21502
|
-
const entries = await
|
|
21503
|
-
const status = await
|
|
21563
|
+
async (args) => withService(async (service2) => {
|
|
21564
|
+
const entries = await service2.listRecent(args.limit ?? 10);
|
|
21565
|
+
const status = await service2.corpusStatus({ refresh: false });
|
|
21504
21566
|
return jsonResult({
|
|
21505
21567
|
count: entries.length,
|
|
21506
21568
|
corpus: corpusField(status),
|
|
@@ -21514,7 +21576,7 @@ async function runServe() {
|
|
|
21514
21576
|
summary: e.summary ?? e.preview
|
|
21515
21577
|
}))
|
|
21516
21578
|
});
|
|
21517
|
-
}
|
|
21579
|
+
})
|
|
21518
21580
|
);
|
|
21519
21581
|
server.registerTool(
|
|
21520
21582
|
"list_conversations",
|
|
@@ -21532,9 +21594,9 @@ async function runServe() {
|
|
|
21532
21594
|
openWorldHint: false
|
|
21533
21595
|
}
|
|
21534
21596
|
},
|
|
21535
|
-
async (args) => {
|
|
21536
|
-
const entries = await
|
|
21537
|
-
const status = await
|
|
21597
|
+
async (args) => withService(async (service2) => {
|
|
21598
|
+
const entries = await service2.listConversations(args.limit ?? 10, args.sort ?? "recent");
|
|
21599
|
+
const status = await service2.corpusStatus({ refresh: false });
|
|
21538
21600
|
return jsonResult({
|
|
21539
21601
|
count: entries.length,
|
|
21540
21602
|
sort: args.sort ?? "recent",
|
|
@@ -21550,7 +21612,7 @@ async function runServe() {
|
|
|
21550
21612
|
summary: e.summary ?? e.preview
|
|
21551
21613
|
}))
|
|
21552
21614
|
});
|
|
21553
|
-
}
|
|
21615
|
+
})
|
|
21554
21616
|
);
|
|
21555
21617
|
server.registerTool(
|
|
21556
21618
|
"corpus_status",
|
|
@@ -21565,39 +21627,49 @@ async function runServe() {
|
|
|
21565
21627
|
openWorldHint: false
|
|
21566
21628
|
}
|
|
21567
21629
|
},
|
|
21568
|
-
async () => {
|
|
21569
|
-
const status = await
|
|
21630
|
+
async () => withService(async (service2) => {
|
|
21631
|
+
const status = await service2.corpusStatus();
|
|
21570
21632
|
return jsonResult(status);
|
|
21571
|
-
}
|
|
21572
|
-
);
|
|
21573
|
-
server.registerTool(
|
|
21574
|
-
"save_conversation",
|
|
21575
|
-
{
|
|
21576
|
-
title: "Save conversation",
|
|
21577
|
-
description: "Add or update a conversation in the user's local corpus. Provide platform, title, and the messages; identity is keyed on docId (or platform:sourceId), so re-saving the same conversation updates it in place.",
|
|
21578
|
-
inputSchema: saveConversationSchema.shape,
|
|
21579
|
-
annotations: {
|
|
21580
|
-
readOnlyHint: false,
|
|
21581
|
-
destructiveHint: false,
|
|
21582
|
-
idempotentHint: false,
|
|
21583
|
-
openWorldHint: false
|
|
21584
|
-
}
|
|
21585
|
-
},
|
|
21586
|
-
async (args) => {
|
|
21587
|
-
try {
|
|
21588
|
-
const result = await service.saveConversation(args);
|
|
21589
|
-
return jsonResult(result);
|
|
21590
|
-
} catch (err) {
|
|
21591
|
-
return errorResult(`save_conversation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21592
|
-
}
|
|
21593
|
-
}
|
|
21633
|
+
})
|
|
21594
21634
|
);
|
|
21635
|
+
if (process.env.LLMNESIA_READ_ONLY !== "1") {
|
|
21636
|
+
server.registerTool(
|
|
21637
|
+
"save_conversation",
|
|
21638
|
+
{
|
|
21639
|
+
title: "Save conversation",
|
|
21640
|
+
description: "Add or update a conversation in the user's local corpus. Provide platform, title, and the messages; identity is keyed on docId (or platform:sourceId), so re-saving the same conversation updates it in place.",
|
|
21641
|
+
inputSchema: saveConversationSchema.shape,
|
|
21642
|
+
annotations: {
|
|
21643
|
+
readOnlyHint: false,
|
|
21644
|
+
destructiveHint: false,
|
|
21645
|
+
idempotentHint: false,
|
|
21646
|
+
openWorldHint: false
|
|
21647
|
+
}
|
|
21648
|
+
},
|
|
21649
|
+
async (args) => withService(async (service2) => {
|
|
21650
|
+
try {
|
|
21651
|
+
const result = await service2.saveConversation(args);
|
|
21652
|
+
return jsonResult(result);
|
|
21653
|
+
} catch (err) {
|
|
21654
|
+
return errorResult(`save_conversation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21655
|
+
}
|
|
21656
|
+
})
|
|
21657
|
+
);
|
|
21658
|
+
}
|
|
21659
|
+
server.server.oninitialized = () => service.start();
|
|
21595
21660
|
const transport = new StdioServerTransport();
|
|
21596
21661
|
await server.connect(transport);
|
|
21597
|
-
process.stderr.write(`llmnesia MCP
|
|
21662
|
+
process.stderr.write(`llmnesia MCP protocol ready; waiting for client initialization (${root})
|
|
21598
21663
|
`);
|
|
21599
21664
|
}
|
|
21600
|
-
var isMain =
|
|
21665
|
+
var isMain = (() => {
|
|
21666
|
+
if (!process.argv[1]) return false;
|
|
21667
|
+
try {
|
|
21668
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
|
|
21669
|
+
} catch {
|
|
21670
|
+
return path.resolve(fileURLToPath(import.meta.url)) === path.resolve(process.argv[1]);
|
|
21671
|
+
}
|
|
21672
|
+
})();
|
|
21601
21673
|
if (isMain) {
|
|
21602
21674
|
runServe().catch((err) => {
|
|
21603
21675
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnesia/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
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.",
|
|
@@ -39,12 +39,14 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"build": "tsup",
|
|
42
|
+
"build": "tsup && npm run build:mcpb",
|
|
43
|
+
"build:mcpb": "node scripts/build-mcpb.mjs",
|
|
43
44
|
"prepack": "npm run build",
|
|
44
45
|
"dev": "tsx src/cli.ts",
|
|
45
46
|
"mcp": "tsx src/mcp.ts",
|
|
46
47
|
"typecheck": "tsc --noEmit",
|
|
47
|
-
"test": "vitest run"
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:mcpb": "node scripts/smoke-mcpb.mjs"
|
|
48
50
|
},
|
|
49
51
|
"comment:deps": "This package publishes with ZERO runtime dependencies — everything is bundled into dist by tsup (see noExternal in tsup.config.ts). That is not just an optimisation: `install` copies dist/ to ~/.llmnesia/bin so MCP clients can spawn it by absolute path, and nothing puts a node_modules beside that copy, so any unbundled import would fail there with ERR_MODULE_NOT_FOUND. Listing them as runtime deps would also be false comfort — npm would install them next to the package, not next to the copy that actually runs. The workspace packages additionally must not appear in the install graph at all: shared-types does not exist on the registry, and the published claude-code-host is an older build without the exports this package needs.",
|
|
50
52
|
"devDependencies": {
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
"@llmnesia/shared-types": "*",
|
|
53
55
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54
56
|
"@types/node": "^22.13.0",
|
|
57
|
+
"adm-zip": "^0.5.18",
|
|
55
58
|
"chokidar": "^4.0.3",
|
|
56
59
|
"cross-spawn": "^7.0.6",
|
|
57
60
|
"fast-uri": "^3.1.5",
|
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.1.
|
|
6
|
+
"version": "0.1.13",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
11
11
|
"identifier": "@llmnesia/mcp",
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.13",
|
|
13
13
|
"transport": {
|
|
14
14
|
"type": "stdio"
|
|
15
15
|
}
|