lace-mcp 0.0.2-alpha.6 → 0.0.2-alpha.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/dist/cli.js +98 -24
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -56,6 +56,25 @@ function getEditorConfigPath(target) {
|
|
|
56
56
|
if (target === "cursor") return join(home, ".cursor", "mcp.json");
|
|
57
57
|
return join(home, ".codex", "config.toml");
|
|
58
58
|
}
|
|
59
|
+
function getProjectMcpConfigPath(cwd = process.cwd()) {
|
|
60
|
+
return join(cwd, ".mcp.json");
|
|
61
|
+
}
|
|
62
|
+
function getProjectCursorConfigPath(cwd = process.cwd()) {
|
|
63
|
+
return join(cwd, ".cursor", "mcp.json");
|
|
64
|
+
}
|
|
65
|
+
function getJsonMcpRefreshConfigPaths(cwd = process.cwd()) {
|
|
66
|
+
return [
|
|
67
|
+
getEditorConfigPath("claude"),
|
|
68
|
+
getProjectMcpConfigPath(cwd),
|
|
69
|
+
getEditorConfigPath("cursor"),
|
|
70
|
+
getProjectCursorConfigPath(cwd)
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
function getJsonMcpConfigPathsForTarget(target, cwd = process.cwd()) {
|
|
74
|
+
if (target === "claude") return [getEditorConfigPath("claude"), getProjectMcpConfigPath(cwd)];
|
|
75
|
+
if (target === "cursor") return [getEditorConfigPath("cursor"), getProjectCursorConfigPath(cwd)];
|
|
76
|
+
return [getEditorConfigPath("claude-desktop")];
|
|
77
|
+
}
|
|
59
78
|
var LACE_DATA, DEFAULT_API_BASE;
|
|
60
79
|
var init_paths = __esm({
|
|
61
80
|
"src/paths.ts"() {
|
|
@@ -65,6 +84,33 @@ var init_paths = __esm({
|
|
|
65
84
|
}
|
|
66
85
|
});
|
|
67
86
|
|
|
87
|
+
// src/prompt.ts
|
|
88
|
+
import { createInterface } from "node:readline";
|
|
89
|
+
function promptNumberedList(header, items, label) {
|
|
90
|
+
return new Promise((resolve2) => {
|
|
91
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
92
|
+
console.log(`
|
|
93
|
+
${header}
|
|
94
|
+
`);
|
|
95
|
+
for (let i = 0; i < items.length; i++) {
|
|
96
|
+
console.log(` ${i + 1}. ${label(items[i])}`);
|
|
97
|
+
}
|
|
98
|
+
rl.question("\n > ", (answer) => {
|
|
99
|
+
rl.close();
|
|
100
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
101
|
+
const selected = items[idx] ?? items[0];
|
|
102
|
+
if (!items[idx]) console.log(` Defaulting to ${label(selected)}`);
|
|
103
|
+
console.log("");
|
|
104
|
+
resolve2(selected);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
var init_prompt = __esm({
|
|
109
|
+
"src/prompt.ts"() {
|
|
110
|
+
"use strict";
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
68
114
|
// src/auth.ts
|
|
69
115
|
var auth_exports = {};
|
|
70
116
|
__export(auth_exports, {
|
|
@@ -158,6 +204,20 @@ async function exchangeAuthCode(apiBase, code, state) {
|
|
|
158
204
|
throw new Error(`Failed to exchange auth code: ${err instanceof Error ? err.message : "network error"}`);
|
|
159
205
|
}
|
|
160
206
|
}
|
|
207
|
+
async function switchOrg(apiBase, currentToken, orgId) {
|
|
208
|
+
const res = await fetch(`${apiBase}/authkit/mcp-token`, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: { "Content-Type": "application/json" },
|
|
211
|
+
body: JSON.stringify({ currentToken, orgId }),
|
|
212
|
+
signal: AbortSignal.timeout(AUTH_FETCH_TIMEOUT_MS)
|
|
213
|
+
});
|
|
214
|
+
if (!res.ok) throw new Error(`Org switch failed: ${res.status}`);
|
|
215
|
+
return await res.json();
|
|
216
|
+
}
|
|
217
|
+
async function promptOrg(organizations) {
|
|
218
|
+
const selected = await promptNumberedList("Which organization?", organizations, (o) => o.name);
|
|
219
|
+
return selected.id;
|
|
220
|
+
}
|
|
161
221
|
async function browserLogin(apiBase = DEFAULT_API_BASE) {
|
|
162
222
|
const { authUrl, state } = await startAuthFlow(apiBase);
|
|
163
223
|
console.log("\n Opening browser to sign in...");
|
|
@@ -165,7 +225,17 @@ async function browserLogin(apiBase = DEFAULT_API_BASE) {
|
|
|
165
225
|
console.log(` \u2192 ${authUrl}
|
|
166
226
|
`);
|
|
167
227
|
const code = await pollForAuthCode(apiBase, state);
|
|
168
|
-
const
|
|
228
|
+
const exchangeResult = await exchangeAuthCode(apiBase, code, state);
|
|
229
|
+
let { token, orgId } = exchangeResult;
|
|
230
|
+
const organizations = exchangeResult.organizations ?? [];
|
|
231
|
+
if (organizations.length > 1) {
|
|
232
|
+
const selectedOrgId = await promptOrg(organizations);
|
|
233
|
+
if (selectedOrgId !== orgId) {
|
|
234
|
+
const switched = await switchOrg(apiBase, token, selectedOrgId);
|
|
235
|
+
token = switched.token;
|
|
236
|
+
orgId = switched.orgId;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
169
239
|
const credentials = { apiBase, token, orgId };
|
|
170
240
|
saveCachedCredentials(credentials);
|
|
171
241
|
console.log(" \u2713 Signed in successfully\n");
|
|
@@ -183,6 +253,7 @@ var init_auth = __esm({
|
|
|
183
253
|
"src/auth.ts"() {
|
|
184
254
|
"use strict";
|
|
185
255
|
init_paths();
|
|
256
|
+
init_prompt();
|
|
186
257
|
AUTH_FILE_NAME = "mcp-auth.json";
|
|
187
258
|
AUTH_FETCH_TIMEOUT_MS = 1e4;
|
|
188
259
|
POLL_INTERVAL_MS = 2e3;
|
|
@@ -256,7 +327,6 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
256
327
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
257
328
|
import { homedir as homedir2 } from "node:os";
|
|
258
329
|
import { join as join3, resolve } from "node:path";
|
|
259
|
-
import { createInterface } from "node:readline";
|
|
260
330
|
function parseFlags(args) {
|
|
261
331
|
const flags = {};
|
|
262
332
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -270,21 +340,7 @@ function parseFlags(args) {
|
|
|
270
340
|
}
|
|
271
341
|
function promptScope(target) {
|
|
272
342
|
const available = target === "claude" ? SCOPES : ["user", "project"];
|
|
273
|
-
return
|
|
274
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
275
|
-
console.log("\n Where should Lace be installed?\n");
|
|
276
|
-
for (let i = 0; i < available.length; i++) {
|
|
277
|
-
console.log(` ${i + 1}. ${SCOPE_LABELS[available[i]]}`);
|
|
278
|
-
}
|
|
279
|
-
rl.question("\n > ", (answer) => {
|
|
280
|
-
rl.close();
|
|
281
|
-
const idx = parseInt(answer.trim(), 10) - 1;
|
|
282
|
-
const scope = available[idx] ?? available[0];
|
|
283
|
-
if (!available[idx]) console.log(` Defaulting to ${scope}`);
|
|
284
|
-
console.log("");
|
|
285
|
-
promptResolve(scope);
|
|
286
|
-
});
|
|
287
|
-
});
|
|
343
|
+
return promptNumberedList("Where should Lace be installed?", available, (s) => SCOPE_LABELS[s]);
|
|
288
344
|
}
|
|
289
345
|
async function resolveCredentials(flags) {
|
|
290
346
|
if (flags.token && flags.org) {
|
|
@@ -336,6 +392,18 @@ function readJsonConfig(configPath) {
|
|
|
336
392
|
}
|
|
337
393
|
return {};
|
|
338
394
|
}
|
|
395
|
+
function refreshJsonMcpEntry(configPath, env) {
|
|
396
|
+
const config2 = readJsonConfig(configPath);
|
|
397
|
+
const mcpServers = config2.mcpServers;
|
|
398
|
+
if (!mcpServers?.lace) return;
|
|
399
|
+
mcpServers.lace.env = env;
|
|
400
|
+
writeFileSync2(configPath, JSON.stringify(config2, null, 2));
|
|
401
|
+
}
|
|
402
|
+
function refreshAllLaceConfigs(env) {
|
|
403
|
+
for (const configPath of getJsonMcpRefreshConfigPaths()) {
|
|
404
|
+
refreshJsonMcpEntry(configPath, env);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
339
407
|
function writeJsonMcpEntry(configPath, entry) {
|
|
340
408
|
const dir = resolve(configPath, "..");
|
|
341
409
|
mkdirSync2(dir, { recursive: true });
|
|
@@ -402,23 +470,23 @@ function installClaude(env, scope = "user") {
|
|
|
402
470
|
console.log(` \u2713 MCP server registered via claude CLI (scope: ${scope})`);
|
|
403
471
|
} catch {
|
|
404
472
|
const configPath = getEditorConfigPath("claude");
|
|
405
|
-
writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("claude"), env });
|
|
473
|
+
writeJsonMcpEntry(configPath, { type: "stdio", command: "npx", args: mcpArgs("claude"), env });
|
|
406
474
|
}
|
|
407
475
|
installClaudeSkill();
|
|
408
476
|
}
|
|
409
477
|
function installClaudeDesktop(env) {
|
|
410
478
|
const npx = resolveNpxPath();
|
|
411
479
|
const configPath = getEditorConfigPath("claude-desktop");
|
|
412
|
-
writeJsonMcpEntry(configPath, { command: npx, args: mcpArgs("claude-desktop"), env });
|
|
480
|
+
writeJsonMcpEntry(configPath, { type: "stdio", command: npx, args: mcpArgs("claude-desktop"), env });
|
|
413
481
|
installClaudeSkill();
|
|
414
482
|
}
|
|
415
483
|
function getCursorConfigPath(scope) {
|
|
416
|
-
if (scope === "project") return
|
|
484
|
+
if (scope === "project") return getProjectCursorConfigPath();
|
|
417
485
|
return getEditorConfigPath("cursor");
|
|
418
486
|
}
|
|
419
487
|
function installCursor(env, scope = "user") {
|
|
420
488
|
const configPath = getCursorConfigPath(scope);
|
|
421
|
-
writeJsonMcpEntry(configPath, { command: "npx", args: mcpArgs("cursor"), env });
|
|
489
|
+
writeJsonMcpEntry(configPath, { type: "stdio", command: "npx", args: mcpArgs("cursor"), env });
|
|
422
490
|
installCursorRule();
|
|
423
491
|
}
|
|
424
492
|
function installCodex(env) {
|
|
@@ -457,6 +525,7 @@ Installing Lace MCP for ${target}...`);
|
|
|
457
525
|
else if (target === "claude-desktop") installClaudeDesktop(env);
|
|
458
526
|
else if (target === "cursor") installCursor(env, scope);
|
|
459
527
|
else installCodex(env);
|
|
528
|
+
refreshAllLaceConfigs(env);
|
|
460
529
|
console.log('\nDone. Start a new session and say "implement the Lace decision" or type /lace.\n');
|
|
461
530
|
}
|
|
462
531
|
var TARGETS, PACKAGE_NAME, SCOPES, SCOPE_LABELS, SKILL_VERSION;
|
|
@@ -466,12 +535,13 @@ var init_install = __esm({
|
|
|
466
535
|
init_auth();
|
|
467
536
|
init_codexConfig();
|
|
468
537
|
init_paths();
|
|
538
|
+
init_prompt();
|
|
469
539
|
TARGETS = ["claude", "claude-desktop", "cursor", "codex"];
|
|
470
540
|
PACKAGE_NAME = "lace-mcp";
|
|
471
541
|
SCOPES = ["user", "project", "local"];
|
|
472
542
|
SCOPE_LABELS = {
|
|
473
543
|
user: "User \u2014 available in all projects",
|
|
474
|
-
project: "Project \u2014 this project only
|
|
544
|
+
project: "Project \u2014 this project only",
|
|
475
545
|
local: "Local \u2014 this machine only (not shared)"
|
|
476
546
|
};
|
|
477
547
|
SKILL_VERSION = 1;
|
|
@@ -517,7 +587,9 @@ function uninstallClaude() {
|
|
|
517
587
|
execSync3("claude mcp remove lace", { stdio: "inherit" });
|
|
518
588
|
console.log(" \u2713 MCP server removed via claude CLI");
|
|
519
589
|
} catch {
|
|
520
|
-
|
|
590
|
+
}
|
|
591
|
+
for (const configPath of getJsonMcpConfigPathsForTarget("claude")) {
|
|
592
|
+
removeJsonMcpEntry(configPath);
|
|
521
593
|
}
|
|
522
594
|
removeSkillFile("claude");
|
|
523
595
|
}
|
|
@@ -531,7 +603,9 @@ function uninstallTarget(target) {
|
|
|
531
603
|
console.log(` \u2713 Removed lace from ${configPath}`);
|
|
532
604
|
}
|
|
533
605
|
} else {
|
|
534
|
-
|
|
606
|
+
for (const configPath of getJsonMcpConfigPathsForTarget(target)) {
|
|
607
|
+
removeJsonMcpEntry(configPath);
|
|
608
|
+
}
|
|
535
609
|
}
|
|
536
610
|
removeSkillFile(target);
|
|
537
611
|
}
|