@llmnesia/mcp 0.1.5 → 0.1.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.
- package/README.md +6 -3
- package/dist/{chunk-YVBAQ5VA.js → chunk-JBCYML3I.js} +1 -1
- package/dist/{cli-TVE63AWC.js → cli-LNUL4QWX.js} +114 -60
- package/dist/cli.js +2 -2
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,6 +52,9 @@ The installer:
|
|
|
52
52
|
Cursor, and Codex), and
|
|
53
53
|
4. registers that copy with each detected client — using the client's own CLI
|
|
54
54
|
where available, or with an automatic backup before editing its config file.
|
|
55
|
+
It also prepares the standard Claude Desktop, Cursor, and Codex/ChatGPT
|
|
56
|
+
config paths, so installing one of those desktop apps later does not require
|
|
57
|
+
running setup again.
|
|
55
58
|
|
|
56
59
|
The `install` command is the approval to perform those setup steps. It does not
|
|
57
60
|
interrupt setup with a separate prompt for every detected app, and it never
|
|
@@ -119,9 +122,9 @@ received data — check it before concluding a chat does not exist.
|
|
|
119
122
|
| Client | How `install` wires it |
|
|
120
123
|
| --- | --- |
|
|
121
124
|
| Claude Code | Runs `claude mcp add --scope user` with the absolute Node and installed runtime paths, making it available in every project (detects `claude` on PATH). |
|
|
122
|
-
| Claude Desktop | Edits `claude_desktop_config.json` at the standard OS-specific path. |
|
|
123
|
-
| Cursor | Edits `~/.cursor/mcp.json
|
|
124
|
-
| Codex | Runs `codex mcp add` with the absolute runtime paths when available. If
|
|
125
|
+
| Claude Desktop | Edits `claude_desktop_config.json` at the standard OS-specific path, preparing it in advance if Claude Desktop is installed later. |
|
|
126
|
+
| Cursor | Edits `~/.cursor/mcp.json`, preparing it in advance if Cursor is installed later. |
|
|
127
|
+
| 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. |
|
|
125
128
|
| Others (Windsurf, Zed, LM Studio, …) | Prints the generic stdio block for you to paste. |
|
|
126
129
|
|
|
127
130
|
**Cloud-only clients cannot reach a local stdio server** (web ChatGPT, Lovable,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
__commonJS,
|
|
16
16
|
__require,
|
|
17
17
|
__toESM
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-JBCYML3I.js";
|
|
19
19
|
|
|
20
20
|
// ../node_modules/isexe/windows.js
|
|
21
21
|
var require_windows = __commonJS({
|
|
@@ -2606,7 +2606,7 @@ async function watchInbox(corpus, onDrain) {
|
|
|
2606
2606
|
// src/clientRegistration.ts
|
|
2607
2607
|
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
2608
2608
|
import { execFile as execFile2 } from "child_process";
|
|
2609
|
-
import { access, readFile as readFile2, stat as stat5, writeFile as writeFile2 } from "fs/promises";
|
|
2609
|
+
import { access, mkdir as mkdir2, readFile as readFile2, stat as stat5, writeFile as writeFile2 } from "fs/promises";
|
|
2610
2610
|
import { homedir as homedir2, platform as platform2 } from "os";
|
|
2611
2611
|
import { dirname as dirname4, join as join4 } from "path";
|
|
2612
2612
|
import { promisify as promisify2 } from "util";
|
|
@@ -2955,15 +2955,19 @@ claude mcp add --scope user ${SERVER_NAME} -- ${serve.command} ${serve.args.join
|
|
|
2955
2955
|
function sameServeCommand(left, right) {
|
|
2956
2956
|
return left.command === right.command && JSON.stringify(left.args) === JSON.stringify(right.args);
|
|
2957
2957
|
}
|
|
2958
|
-
async function registerCodex(serve, cli, configPath, confirm, now) {
|
|
2958
|
+
async function registerCodex(serve, cli, configPath, confirm, now, prepareMissing = false) {
|
|
2959
2959
|
if (!await cli.detect()) {
|
|
2960
|
-
|
|
2960
|
+
const configDirExists = await clientDirExists(configPath);
|
|
2961
|
+
if (!configDirExists && !prepareMissing) {
|
|
2961
2962
|
return {
|
|
2962
2963
|
client: "Codex",
|
|
2963
2964
|
action: "skipped",
|
|
2964
2965
|
detail: `Not installed (no Codex CLI and no ${dirname4(configPath)}).`
|
|
2965
2966
|
};
|
|
2966
2967
|
}
|
|
2968
|
+
if (!configDirExists) {
|
|
2969
|
+
await mkdir2(dirname4(configPath), { recursive: true });
|
|
2970
|
+
}
|
|
2967
2971
|
try {
|
|
2968
2972
|
const raw = await readFile2(configPath, "utf8");
|
|
2969
2973
|
if (renderCodexConfig(raw, serve) === raw) {
|
|
@@ -2986,8 +2990,8 @@ ${codexBlock(serve)}`
|
|
|
2986
2990
|
const update = await writeCodexConfig(configPath, serve, now);
|
|
2987
2991
|
return {
|
|
2988
2992
|
client: "Codex",
|
|
2989
|
-
action: update.changed ? "registered" : "skipped",
|
|
2990
|
-
detail: update.changed ? `Wrote ${configPath}${update.backupPath ? ` (backup: ${update.backupPath})` : ""}.` : `Already registered at ${configPath}.`
|
|
2993
|
+
action: update.changed ? configDirExists ? "registered" : "prepared" : "skipped",
|
|
2994
|
+
detail: update.changed ? configDirExists ? `Wrote ${configPath}${update.backupPath ? ` (backup: ${update.backupPath})` : ""}.` : `Prepared ${configPath} for a future Codex or ChatGPT installation.` : `Already registered at ${configPath}.`
|
|
2991
2995
|
};
|
|
2992
2996
|
} catch (err) {
|
|
2993
2997
|
return { client: "Codex", action: "error", detail: `Could not update ${configPath}: ${errorMessage(err)}` };
|
|
@@ -3068,14 +3072,22 @@ codex mcp add ${SERVER_NAME} -- ${serve.command} ${serve.args.join(" ")}`
|
|
|
3068
3072
|
detail: `Ran: codex mcp add ${SERVER_NAME} -- ${serve.command} ${serve.args.join(" ")}`
|
|
3069
3073
|
};
|
|
3070
3074
|
}
|
|
3071
|
-
async function registerJsonClient(clientLabel, configPath, serve, confirm, now) {
|
|
3072
|
-
|
|
3075
|
+
async function registerJsonClient(clientLabel, configPath, serve, confirm, now, prepareMissing = false) {
|
|
3076
|
+
const configDirExists = await clientDirExists(configPath);
|
|
3077
|
+
if (!configDirExists && !prepareMissing) {
|
|
3073
3078
|
return {
|
|
3074
3079
|
client: clientLabel,
|
|
3075
3080
|
action: "skipped",
|
|
3076
3081
|
detail: `Not installed (no ${dirname4(configPath)}). Re-run install after setting it up.`
|
|
3077
3082
|
};
|
|
3078
3083
|
}
|
|
3084
|
+
if (!configDirExists) {
|
|
3085
|
+
try {
|
|
3086
|
+
await mkdir2(dirname4(configPath), { recursive: true });
|
|
3087
|
+
} catch (err) {
|
|
3088
|
+
return { client: clientLabel, action: "error", detail: `Could not prepare ${configPath}: ${errorMessage(err)}` };
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3079
3091
|
let data;
|
|
3080
3092
|
let existed;
|
|
3081
3093
|
let raw;
|
|
@@ -3112,7 +3124,11 @@ ${preview}`;
|
|
|
3112
3124
|
const nextData = { ...data, mcpServers: { ...servers, [SERVER_NAME]: nextEntry } };
|
|
3113
3125
|
await writeFile2(configPath, `${JSON.stringify(nextData, null, 2)}
|
|
3114
3126
|
`, "utf8");
|
|
3115
|
-
return {
|
|
3127
|
+
return {
|
|
3128
|
+
client: clientLabel,
|
|
3129
|
+
action: configDirExists ? "registered" : "prepared",
|
|
3130
|
+
detail: configDirExists ? `Wrote ${configPath}.` : `Prepared ${configPath} for a future ${clientLabel} installation.`
|
|
3131
|
+
};
|
|
3116
3132
|
} catch (err) {
|
|
3117
3133
|
return { client: clientLabel, action: "error", detail: `Could not write ${configPath}: ${errorMessage(err)}` };
|
|
3118
3134
|
}
|
|
@@ -3205,6 +3221,7 @@ async function registerClients(options) {
|
|
|
3205
3221
|
const home2 = options.home ?? homedir2();
|
|
3206
3222
|
const osPlatform2 = options.osPlatform ?? platform2();
|
|
3207
3223
|
const now = options.now ?? (() => Date.now());
|
|
3224
|
+
const prepareMissing = options.prepareMissingDesktopApps === true;
|
|
3208
3225
|
const { claudeDesktop, cursor, codex } = paths(home2, osPlatform2);
|
|
3209
3226
|
const results = [];
|
|
3210
3227
|
const safe = async (client, fn) => {
|
|
@@ -3224,15 +3241,17 @@ async function registerClients(options) {
|
|
|
3224
3241
|
results.push(
|
|
3225
3242
|
await safe(
|
|
3226
3243
|
"Claude Desktop",
|
|
3227
|
-
() => registerJsonClient("Claude Desktop", claudeDesktop, options.serve, options.confirm, now)
|
|
3244
|
+
() => registerJsonClient("Claude Desktop", claudeDesktop, options.serve, options.confirm, now, prepareMissing)
|
|
3228
3245
|
)
|
|
3229
3246
|
);
|
|
3230
3247
|
}
|
|
3231
|
-
results.push(
|
|
3248
|
+
results.push(
|
|
3249
|
+
await safe("Cursor", () => registerJsonClient("Cursor", cursor, options.serve, options.confirm, now, prepareMissing))
|
|
3250
|
+
);
|
|
3232
3251
|
results.push(
|
|
3233
3252
|
await safe(
|
|
3234
3253
|
"Codex",
|
|
3235
|
-
() => registerCodex(options.serve, options.codexCli ?? realCodexCli, codex, options.confirm, now)
|
|
3254
|
+
() => registerCodex(options.serve, options.codexCli ?? realCodexCli, codex, options.confirm, now, prepareMissing)
|
|
3236
3255
|
)
|
|
3237
3256
|
);
|
|
3238
3257
|
results.push({
|
|
@@ -3378,7 +3397,7 @@ import { delimiter, dirname as dirname6, isAbsolute as isAbsolute2, join as join
|
|
|
3378
3397
|
import { homedir as homedir4, platform as platform3 } from "os";
|
|
3379
3398
|
|
|
3380
3399
|
// src/runtimeInstall.ts
|
|
3381
|
-
import { cp, mkdir as
|
|
3400
|
+
import { cp, mkdir as mkdir3, readdir as readdir3, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
3382
3401
|
import { existsSync } from "fs";
|
|
3383
3402
|
import { homedir as homedir3 } from "os";
|
|
3384
3403
|
import { dirname as dirname5, join as join5 } from "path";
|
|
@@ -3404,10 +3423,10 @@ async function installRuntime(options = {}) {
|
|
|
3404
3423
|
const nonce = `${process.pid}-${Date.now()}`;
|
|
3405
3424
|
const stagingDir = join5(parentDir, `.bin-install-${nonce}`);
|
|
3406
3425
|
const backupDir = join5(parentDir, `.bin-backup-${nonce}`);
|
|
3407
|
-
await
|
|
3426
|
+
await mkdir3(parentDir, { recursive: true });
|
|
3408
3427
|
await rm2(stagingDir, { recursive: true, force: true });
|
|
3409
3428
|
await rm2(backupDir, { recursive: true, force: true });
|
|
3410
|
-
await
|
|
3429
|
+
await mkdir3(stagingDir, { recursive: true });
|
|
3411
3430
|
try {
|
|
3412
3431
|
for (const name of await readdir3(sourceDir)) {
|
|
3413
3432
|
await cp(join5(sourceDir, name), join5(stagingDir, name), { recursive: true });
|
|
@@ -3793,66 +3812,90 @@ ${message}
|
|
|
3793
3812
|
rl.close();
|
|
3794
3813
|
}
|
|
3795
3814
|
}
|
|
3796
|
-
function
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
if (
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
if (
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3815
|
+
function summarizeClientRegistrations(results) {
|
|
3816
|
+
const connected = [];
|
|
3817
|
+
const prepared = [];
|
|
3818
|
+
const errors = [];
|
|
3819
|
+
for (const result of results) {
|
|
3820
|
+
if (result.client.startsWith("Other MCP clients")) continue;
|
|
3821
|
+
if (result.action === "registered" || result.action === "skipped" && result.detail.startsWith("Already registered")) {
|
|
3822
|
+
connected.push(result.client);
|
|
3823
|
+
} else if (result.action === "prepared") {
|
|
3824
|
+
prepared.push(result.client);
|
|
3825
|
+
} else if (result.action === "error") {
|
|
3826
|
+
errors.push({ client: result.client, detail: result.detail });
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
return { connected, prepared, errors };
|
|
3830
|
+
}
|
|
3831
|
+
function naturalList(items) {
|
|
3832
|
+
if (items.length <= 1) return items[0] ?? "";
|
|
3833
|
+
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
3834
|
+
return `${items.slice(0, -1).join(", ")}, and ${items.at(-1)}`;
|
|
3835
|
+
}
|
|
3836
|
+
function formatInstallSuccess(connectedClients, preparedClients = []) {
|
|
3837
|
+
const apps = naturalList(connectedClients);
|
|
3838
|
+
const restart = apps ? `Fully close and reopen your browser and ${apps}.
|
|
3839
|
+
` : "Fully close and reopen your browser.\n";
|
|
3840
|
+
const future = preparedClients.length > 0 ? `${naturalList(preparedClients)} will also be ready if you install ${preparedClients.length === 1 ? "it" : "them"} later.
|
|
3841
|
+
` : "";
|
|
3842
|
+
return `
|
|
3843
|
+
Setup complete.
|
|
3844
|
+
${restart}${future}LLMnesia Settings will confirm when your history is ready.
|
|
3845
|
+
`;
|
|
3821
3846
|
}
|
|
3822
3847
|
async function runInstall(positionals, options, flags) {
|
|
3823
3848
|
let hadProblems = false;
|
|
3824
|
-
process.stdout.write("
|
|
3849
|
+
process.stdout.write("Connecting your browser...\n");
|
|
3825
3850
|
try {
|
|
3826
|
-
|
|
3851
|
+
await installNativeHost({
|
|
3827
3852
|
ids: positionals,
|
|
3828
3853
|
corpus: options.corpus,
|
|
3829
3854
|
force: flags.has("force"),
|
|
3830
3855
|
quiet: true
|
|
3831
3856
|
});
|
|
3832
|
-
process.stdout.write(
|
|
3833
|
-
`);
|
|
3857
|
+
process.stdout.write("Browser connected.\n");
|
|
3834
3858
|
} catch (err) {
|
|
3835
3859
|
hadProblems = true;
|
|
3836
|
-
process.stderr.write(
|
|
3860
|
+
process.stderr.write(`Could not connect your browser: ${err instanceof Error ? err.message : String(err)}
|
|
3837
3861
|
`);
|
|
3838
3862
|
}
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
"\nDesktop helper installed. Fully quit and reopen your MCP apps and browser.\nBrowser sync is required or your MCP history will be empty. If you did not start in LLMnesia Settings, open it and click \u2018Enable automatic sync\u2019 once. LLMnesia will finish the first sync automatically.\n"
|
|
3846
|
-
);
|
|
3847
|
-
const npxCommand = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
3848
|
-
process.stdout.write(`Then run \`${npxCommand} -y @llmnesia/mcp@latest doctor\` to check what's wired up.
|
|
3863
|
+
process.stdout.write("Connecting your desktop AI apps...\n");
|
|
3864
|
+
let runtime;
|
|
3865
|
+
try {
|
|
3866
|
+
runtime = await installRuntime();
|
|
3867
|
+
} catch (err) {
|
|
3868
|
+
process.stderr.write(`Could not install LLMnesia: ${err instanceof Error ? err.message : String(err)}
|
|
3849
3869
|
`);
|
|
3850
|
-
|
|
3870
|
+
process.stderr.write("Setup needs attention. Fix the message above, then run the same command again.\n");
|
|
3871
|
+
return 1;
|
|
3872
|
+
}
|
|
3873
|
+
const results = await registerClients({
|
|
3874
|
+
serve: runtime.serve,
|
|
3875
|
+
confirm: async () => true,
|
|
3876
|
+
prepareMissingDesktopApps: true
|
|
3877
|
+
});
|
|
3878
|
+
const registrations = summarizeClientRegistrations(results);
|
|
3879
|
+
for (const client of registrations.connected) {
|
|
3880
|
+
process.stdout.write(`${client} connected.
|
|
3881
|
+
`);
|
|
3882
|
+
}
|
|
3883
|
+
for (const error of registrations.errors) {
|
|
3884
|
+
process.stderr.write(`Could not connect ${error.client}: ${error.detail}
|
|
3885
|
+
`);
|
|
3886
|
+
}
|
|
3887
|
+
hadProblems ||= registrations.errors.length > 0;
|
|
3888
|
+
if (registrations.connected.length === 0 && registrations.prepared.length === 0) {
|
|
3889
|
+
hadProblems = true;
|
|
3851
3890
|
process.stderr.write(
|
|
3852
|
-
"
|
|
3891
|
+
"No supported desktop AI app was found. Install or open Codex, Claude Desktop, or Cursor, then run the same command again.\n"
|
|
3853
3892
|
);
|
|
3893
|
+
}
|
|
3894
|
+
if (hadProblems) {
|
|
3895
|
+
process.stderr.write("\nSetup needs attention. Fix the message above, then run the same command again.\n");
|
|
3854
3896
|
return 1;
|
|
3855
3897
|
}
|
|
3898
|
+
process.stdout.write(formatInstallSuccess(registrations.connected, registrations.prepared));
|
|
3856
3899
|
return 0;
|
|
3857
3900
|
}
|
|
3858
3901
|
async function runUninstall(flags) {
|
|
@@ -3867,10 +3910,19 @@ async function runUninstall(flags) {
|
|
|
3867
3910
|
}
|
|
3868
3911
|
}
|
|
3869
3912
|
const results = await unregisterClients();
|
|
3870
|
-
|
|
3913
|
+
for (const result of results) {
|
|
3914
|
+
if (result.client.startsWith("Other MCP clients")) continue;
|
|
3915
|
+
if (result.action === "registered") {
|
|
3916
|
+
process.stdout.write(`${result.client} disconnected.
|
|
3917
|
+
`);
|
|
3918
|
+
} else if (result.action === "error") {
|
|
3919
|
+
process.stderr.write(`Could not disconnect ${result.client}: ${result.detail}
|
|
3920
|
+
`);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3871
3923
|
if (results.some((result) => result.action === "error")) {
|
|
3872
3924
|
process.stderr.write(
|
|
3873
|
-
"Uninstall stopped before removing the
|
|
3925
|
+
"Uninstall stopped before removing the remaining files. Fix the message above and try again.\n"
|
|
3874
3926
|
);
|
|
3875
3927
|
return 1;
|
|
3876
3928
|
}
|
|
@@ -4062,7 +4114,9 @@ function collapse(text) {
|
|
|
4062
4114
|
return text.replace(/\s+/g, " ").trim();
|
|
4063
4115
|
}
|
|
4064
4116
|
export {
|
|
4065
|
-
|
|
4117
|
+
formatInstallSuccess,
|
|
4118
|
+
main,
|
|
4119
|
+
summarizeClientRegistrations
|
|
4066
4120
|
};
|
|
4067
4121
|
/*! Bundled license information:
|
|
4068
4122
|
|
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-JBCYML3I.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-LNUL4QWX.js");
|
|
145
145
|
return main();
|
|
146
146
|
}
|
|
147
147
|
bootstrap().then((code) => {
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnesia/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.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.",
|