@uzqw/mcp 0.1.0 → 0.1.1

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/index.js CHANGED
@@ -237,7 +237,7 @@ var CALLBACK_PORT = 8199;
237
237
  var LOGIN_TIMEOUT_MS = 12e4;
238
238
  var sub = process.argv[2];
239
239
  if (sub === "install" || sub === "uninstall") {
240
- const { runInstall, runUninstall } = await import("./install-VFWABBBV.js");
240
+ const { runInstall, runUninstall } = await import("./install-2GRD6TZR.js");
241
241
  const args = process.argv.slice(3);
242
242
  const yes = args.includes("--yes") || args.includes("-y");
243
243
  const noLogin = args.includes("--no-login");
@@ -12,10 +12,13 @@ import { dirname, join } from "path";
12
12
  import { spawnSync } from "child_process";
13
13
  import { createInterface } from "readline/promises";
14
14
  import open from "open";
15
- import { RecolxClient, runOAuthCallback } from "@uzqw/shared";
15
+ import { RecolxClient, DEFAULT_API_BASE, runOAuthCallback } from "@uzqw/shared";
16
16
  var SERVER_NAME = "recolx";
17
17
  var CALLBACK_PORT = 8199;
18
18
  var LOGIN_TIMEOUT_MS = 12e4;
19
+ function resolveApiBase() {
20
+ return (process.env.RECOLX_API_BASE?.trim() || DEFAULT_API_BASE).replace(/\/+$/, "");
21
+ }
19
22
  function findNodeBinCommand(command) {
20
23
  const commandName = platform() === "win32" ? `${command}.cmd` : command;
21
24
  const sibling = join(dirname(process.execPath), commandName);
@@ -30,10 +33,8 @@ function isLocalPackageSpec(spec) {
30
33
  }
31
34
  function getMcpEntry() {
32
35
  const packageSpec = process.env.RECOLX_MCP_PACKAGE_SPEC?.trim() || "@uzqw/mcp@latest";
33
- if (isLocalPackageSpec(packageSpec)) {
34
- return { command: findNodeBinCommand("npm"), args: ["exec", "--yes", "--package", packageSpec, "--", "recolx-mcp"] };
35
- }
36
- return { command: findNodeBinCommand("npx"), args: ["-y", packageSpec] };
36
+ const base = isLocalPackageSpec(packageSpec) ? { command: findNodeBinCommand("npm"), args: ["exec", "--yes", "--package", packageSpec, "--", "recolx-mcp"] } : { command: findNodeBinCommand("npx"), args: ["-y", packageSpec] };
37
+ return { ...base, env: { RECOLX_API_BASE: resolveApiBase() } };
37
38
  }
38
39
  function commandPathIsStale(command) {
39
40
  if (!command) return false;
@@ -64,6 +65,9 @@ function configPaths() {
64
65
  claudeCodeDir: join(h, ".claude")
65
66
  };
66
67
  }
68
+ function entryEnv(e) {
69
+ return e.env && Object.keys(e.env).length > 0 ? e.env : void 0;
70
+ }
67
71
  function isJsonObject(v) {
68
72
  return typeof v === "object" && v !== null && !Array.isArray(v);
69
73
  }
@@ -75,9 +79,18 @@ function entryAutoUpdates(entry) {
75
79
  const args = Array.isArray(entry.args) ? entry.args : [];
76
80
  return args.some((a) => typeof a === "string" && a.includes("@uzqw/mcp@latest"));
77
81
  }
82
+ function entryEnvMatches(entry, expected) {
83
+ if (!isJsonObject(entry)) return false;
84
+ const env = isJsonObject(entry.env) ? entry.env : {};
85
+ if (!expected) return true;
86
+ return Object.entries(expected).every(([k, v]) => env[k] === v);
87
+ }
78
88
  function jsonAdapter(spec) {
79
89
  const rootKey = spec.rootKey ?? "mcpServers";
80
- const toEntry = spec.toEntry ?? ((e) => ({ command: e.command, args: e.args }));
90
+ const toEntry = spec.toEntry ?? ((e) => {
91
+ const env = entryEnv(e);
92
+ return env ? { command: e.command, args: e.args, env } : { command: e.command, args: e.args };
93
+ });
81
94
  async function load() {
82
95
  try {
83
96
  const parsed = JSON.parse(await readFile(spec.configPath, "utf8"));
@@ -106,7 +119,7 @@ function jsonAdapter(spec) {
106
119
  const existing = servers[SERVER_NAME];
107
120
  if (existing && entryAutoUpdates(existing)) {
108
121
  const cmd = isJsonObject(existing) && typeof existing.command === "string" ? existing.command : void 0;
109
- if (!commandPathIsStale(cmd)) {
122
+ if (!commandPathIsStale(cmd) && entryEnvMatches(existing, entryEnv(ctx.entry))) {
110
123
  return { status: "already-configured", message: "\u5DF2\u914D\u7F6E\uFF08npx @latest \u81EA\u52A8\u66F4\u65B0\uFF09", restartHint: spec.restartHint };
111
124
  }
112
125
  }
@@ -136,9 +149,25 @@ function codexBlockText(entry) {
136
149
  const win = platform() === "win32";
137
150
  const command = win ? normalizeWindowsPath(entry.command, "win32") : entry.command;
138
151
  const args = (win ? entry.args.map((a) => normalizeWindowsPath(a, "win32")) : entry.args).map((a) => JSON.stringify(a)).join(", ");
152
+ const env = entryEnv(entry);
153
+ const envLine = env ? `
154
+ env = { ${Object.entries(env).map(([k, v]) => `${JSON.stringify(k)} = ${JSON.stringify(v)}`).join(", ")} }` : "";
139
155
  return `${CODEX_HEADER}
140
156
  command = ${JSON.stringify(command)}
141
- args = [${args}]`;
157
+ args = [${args}]${envLine}`;
158
+ }
159
+ function extractTomlEnv(lines) {
160
+ for (const line of lines) {
161
+ const m = line.match(/^\s*env\s*=\s*\{(.*)\}\s*$/);
162
+ if (!m) continue;
163
+ const env = {};
164
+ for (const pair of m[1].split(",")) {
165
+ const kv = pair.match(/"([^"]+)"\s*=\s*"([^"]*)"/);
166
+ if (kv) env[kv[1]] = kv[2];
167
+ }
168
+ return env;
169
+ }
170
+ return void 0;
142
171
  }
143
172
  function codexBlockRange(lines) {
144
173
  const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
@@ -184,13 +213,13 @@ function codexAdapter(configPath) {
184
213
  const block = codexBlockText(ctx.entry);
185
214
  if (range) {
186
215
  const existingCommand = extractTomlCommand(lines.slice(range.start, range.end));
187
- if (!commandPathIsStale(existingCommand)) {
216
+ if (!commandPathIsStale(existingCommand) && entryEnvMatches({ env: extractTomlEnv(lines.slice(range.start, range.end)) }, entryEnv(ctx.entry))) {
188
217
  return { status: "already-configured", message: "\u5DF2\u914D\u7F6E\uFF08npx @latest \u81EA\u52A8\u66F4\u65B0\uFF09" };
189
218
  }
190
219
  const rebuilt = [...lines.slice(0, range.start), ...block.split("\n"), ...lines.slice(range.end)].join("\n");
191
220
  await mkdir(dirname(configPath), { recursive: true });
192
221
  await writeFile(configPath, rebuilt, "utf8");
193
- return { status: "configured", message: "\u5DF2\u4FEE\u590D\u5931\u6548\u542F\u52A8\u8DEF\u5F84\uFF08npx @latest\uFF09" };
222
+ return { status: "configured", message: "\u5DF2\u4FEE\u590D\u542F\u52A8\u8DEF\u5F84/env\uFF08npx @latest\uFF09" };
194
223
  }
195
224
  await mkdir(dirname(configPath), { recursive: true });
196
225
  await writeFile(configPath, `${content}${content && !content.endsWith("\n") ? "\n" : ""}
@@ -297,13 +326,16 @@ function zedAdapter(configPath) {
297
326
  const existing = servers[SERVER_NAME];
298
327
  if (existing && entryAutoUpdates(existing)) {
299
328
  const path = isJsonObject(existing) && isJsonObject(existing.command) && typeof existing.command.path === "string" ? existing.command.path : void 0;
300
- if (!commandPathIsStale(path)) {
329
+ const existingCommand = isJsonObject(existing) && isJsonObject(existing.command) ? existing.command : void 0;
330
+ if (!commandPathIsStale(path) && entryEnvMatches(existingCommand, entryEnv(ctx.entry))) {
301
331
  return { status: "already-configured", message: "\u5DF2\u914D\u7F6E\uFF08npx @latest \u81EA\u52A8\u66F4\u65B0\uFF09" };
302
332
  }
303
333
  }
334
+ const env = entryEnv(ctx.entry);
335
+ const commandObj = env ? { path: ctx.entry.command, args: ctx.entry.args, env } : { path: ctx.entry.command, args: ctx.entry.args };
304
336
  config.context_servers = {
305
337
  ...servers,
306
- [SERVER_NAME]: { command: { path: ctx.entry.command, args: ctx.entry.args } }
338
+ [SERVER_NAME]: { command: commandObj }
307
339
  };
308
340
  await mkdir(dirname(configPath), { recursive: true });
309
341
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
@@ -370,7 +402,10 @@ function detectClients() {
370
402
  label: "VS Code",
371
403
  configPath: p.vsCode,
372
404
  rootKey: "servers",
373
- toEntry: (e) => ({ type: "stdio", command: e.command, args: e.args }),
405
+ toEntry: (e) => {
406
+ const env = entryEnv(e);
407
+ return env ? { type: "stdio", command: e.command, args: e.args, env } : { type: "stdio", command: e.command, args: e.args };
408
+ },
374
409
  configuredMessage: "\u5DF2\u5199\u5165 .vscode/mcp.json",
375
410
  restartHint: RESTART_HINTS.vscode
376
411
  }),
@@ -604,8 +639,10 @@ export {
604
639
  codexBlockRange,
605
640
  codexBlockText,
606
641
  commandPathIsStale,
642
+ extractTomlEnv,
607
643
  getMcpEntry,
608
644
  normalizeWindowsPath,
645
+ resolveApiBase,
609
646
  runInstall,
610
647
  runUninstall
611
648
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzqw/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Recolx read-only MCP server (stdio) — 7 read-only tools, readOnlyHint: true",
5
5
  "mcpName": "io.github.uzqw/recolx-mcp",
6
6
  "publishConfig": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "type": "module",
10
10
  "bin": {
11
- "recolx-mcp": "./dist/index.js"
11
+ "recolx-mcp": "dist/index.js"
12
12
  },
13
13
  "main": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts",
@@ -18,7 +18,10 @@
18
18
  "import": "./dist/index.js"
19
19
  }
20
20
  },
21
- "files": ["dist", "skills"],
21
+ "files": [
22
+ "dist",
23
+ "skills"
24
+ ],
22
25
  "scripts": {
23
26
  "build": "tsup src/index.ts --format esm --dts --clean",
24
27
  "test": "tsx --test test/*.test.ts"