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.
Files changed (2) hide show
  1. package/dist/cli.js +98 -24
  2. 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 { token, orgId } = await exchangeAuthCode(apiBase, code, state);
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 new Promise((promptResolve) => {
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 join3(process.cwd(), ".cursor", "mcp.json");
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 (shared via .mcp.json)",
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
- removeJsonMcpEntry(getEditorConfigPath("claude"));
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
- removeJsonMcpEntry(getEditorConfigPath(target));
606
+ for (const configPath of getJsonMcpConfigPathsForTarget(target)) {
607
+ removeJsonMcpEntry(configPath);
608
+ }
535
609
  }
536
610
  removeSkillFile(target);
537
611
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lace-mcp",
3
- "version": "0.0.2-alpha.6",
3
+ "version": "0.0.2-alpha.8",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/cli.js",