@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-
|
|
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
|
-
|
|
34
|
-
|
|
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) =>
|
|
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\
|
|
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
|
-
|
|
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:
|
|
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) =>
|
|
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.
|
|
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": "
|
|
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": [
|
|
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"
|