@wrongstack/mcp 0.317.2 → 0.319.0

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/index.js +42 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -817,10 +817,11 @@ function boundedServerName(value) {
817
817
 
818
818
  // src/client.ts
819
819
  import { spawn as spawn2 } from "node:child_process";
820
- import { buildChildEnv, buildWin32CmdShimInvocation, toErrorMessage } from "@wrongstack/core/utils";
820
+ import { buildChildEnv as buildChildEnv2, buildWin32CmdShimInvocation, toErrorMessage } from "@wrongstack/core/utils";
821
821
 
822
822
  // src/client-process.ts
823
823
  import { spawn } from "node:child_process";
824
+ import { buildChildEnv } from "@wrongstack/core/utils";
824
825
  function forceKillTree(child) {
825
826
  if (child.pid === void 0) {
826
827
  try {
@@ -832,6 +833,9 @@ function forceKillTree(child) {
832
833
  if (process.platform === "win32") {
833
834
  const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
834
835
  stdio: "ignore",
836
+ // H-8 convention (spawn-convention test): taskkill needs PATH only —
837
+ // do not hand it the credential-bearing parent environment.
838
+ env: buildChildEnv(),
835
839
  windowsHide: true
836
840
  });
837
841
  killer.once("error", () => {
@@ -2284,7 +2288,7 @@ var MCPClient = class _MCPClient {
2284
2288
  }
2285
2289
  const isWin = process.platform === "win32";
2286
2290
  const rawArgs = this.opts.args ?? [];
2287
- const spawnEnv = buildChildEnv({ extra: extraEnv });
2291
+ const spawnEnv = buildChildEnv2({ extra: extraEnv });
2288
2292
  const stdio = ["pipe", "pipe", "pipe"];
2289
2293
  const child = isWin ? (() => {
2290
2294
  const shim = buildWin32CmdShimInvocation(this.opts.command, rawArgs);
@@ -3036,6 +3040,15 @@ async function persist(configPath, full, servers) {
3036
3040
  full.mcpServers = servers;
3037
3041
  await writeConfig(configPath, full);
3038
3042
  }
3043
+ var UNSAFE_SERVER_NAMES = /* @__PURE__ */ new Set([
3044
+ "__proto__",
3045
+ "constructor",
3046
+ "prototype"
3047
+ ]);
3048
+ function unsafeServerNameResult(name) {
3049
+ if (!UNSAFE_SERVER_NAMES.has(name)) return void 0;
3050
+ return { ok: false, message: `Invalid server name "${name}"` };
3051
+ }
3039
3052
  function normalizeTransport(t) {
3040
3053
  if (t === "sse") return "sse";
3041
3054
  if (t === "http" || t === "streamable-http") return "streamable-http";
@@ -3124,11 +3137,13 @@ async function listMcp(deps) {
3124
3137
  }
3125
3138
  async function addMcp(input, deps) {
3126
3139
  if (!input.name) return { ok: false, message: "Server name is required" };
3140
+ const unsafe = unsafeServerNameResult(input.name);
3141
+ if (unsafe) return unsafe;
3127
3142
  const { full, servers } = await readServers(deps.configPath);
3128
- if (servers[input.name]) {
3143
+ if (Object.hasOwn(servers, input.name)) {
3129
3144
  return { ok: false, message: `Server "${input.name}" already exists` };
3130
3145
  }
3131
- const preset = deps.presets?.[input.name];
3146
+ const preset = deps.presets && Object.hasOwn(deps.presets, input.name) ? deps.presets[input.name] : void 0;
3132
3147
  const hasExplicitConfig = !!(input.transport || input.command || input.url);
3133
3148
  const cfg = hasExplicitConfig ? buildConfig(input, preset) : preset ? buildConfig({ ...input, name: input.name }, preset) : buildConfig(input);
3134
3149
  if (!hasExplicitConfig && !preset) {
@@ -3153,7 +3168,12 @@ async function addMcp(input, deps) {
3153
3168
  }
3154
3169
  async function updateMcp(input, deps) {
3155
3170
  if (!input.name) return { ok: false, message: "Server name is required" };
3171
+ const unsafe = unsafeServerNameResult(input.name);
3172
+ if (unsafe) return unsafe;
3156
3173
  const { full, servers } = await readServers(deps.configPath);
3174
+ if (!Object.hasOwn(servers, input.name)) {
3175
+ return { ok: false, message: `Server "${input.name}" not found` };
3176
+ }
3157
3177
  const existing = servers[input.name];
3158
3178
  if (!existing) return { ok: false, message: `Server "${input.name}" not found` };
3159
3179
  const cfg = buildConfig(input, { ...existing, name: input.name });
@@ -3172,8 +3192,10 @@ async function updateMcp(input, deps) {
3172
3192
  }
3173
3193
  async function removeMcp(name, deps) {
3174
3194
  if (!name) return { ok: false, message: "Server name is required" };
3195
+ const unsafe = unsafeServerNameResult(name);
3196
+ if (unsafe) return unsafe;
3175
3197
  const { full, servers } = await readServers(deps.configPath);
3176
- if (!servers[name]) return { ok: false, message: `Server "${name}" not found` };
3198
+ if (!Object.hasOwn(servers, name)) return { ok: false, message: `Server "${name}" not found` };
3177
3199
  await safeStop(name, deps);
3178
3200
  forgetRegistryState(deps.registry, name);
3179
3201
  delete servers[name];
@@ -3182,7 +3204,12 @@ async function removeMcp(name, deps) {
3182
3204
  }
3183
3205
  async function enableMcp(name, deps) {
3184
3206
  if (!name) return { ok: false, message: "Server name is required" };
3207
+ const unsafe = unsafeServerNameResult(name);
3208
+ if (unsafe) return unsafe;
3185
3209
  const { full, servers } = await readServers(deps.configPath);
3210
+ if (!Object.hasOwn(servers, name)) {
3211
+ return { ok: false, message: `Server "${name}" is not in config. Add it first.` };
3212
+ }
3186
3213
  const cfg = servers[name];
3187
3214
  if (!cfg) {
3188
3215
  return { ok: false, message: `Server "${name}" is not in config. Add it first.` };
@@ -3194,7 +3221,12 @@ async function enableMcp(name, deps) {
3194
3221
  }
3195
3222
  async function disableMcp(name, deps) {
3196
3223
  if (!name) return { ok: false, message: "Server name is required" };
3224
+ const unsafe = unsafeServerNameResult(name);
3225
+ if (unsafe) return unsafe;
3197
3226
  const { full, servers } = await readServers(deps.configPath);
3227
+ if (!Object.hasOwn(servers, name)) {
3228
+ return { ok: false, message: `Server "${name}" is not in config.` };
3229
+ }
3198
3230
  const cfg = servers[name];
3199
3231
  if (!cfg) return { ok: false, message: `Server "${name}" is not in config.` };
3200
3232
  await safeStop(name, deps);
@@ -3210,6 +3242,8 @@ async function disableMcp(name, deps) {
3210
3242
  }
3211
3243
  async function restartMcp(name, deps) {
3212
3244
  if (!name) return { ok: false, message: "Server name is required" };
3245
+ const unsafe = unsafeServerNameResult(name);
3246
+ if (unsafe) return unsafe;
3213
3247
  const registered = deps.registry.list().some((s) => s.name === name);
3214
3248
  if (registered) {
3215
3249
  try {
@@ -3221,6 +3255,9 @@ async function restartMcp(name, deps) {
3221
3255
  }
3222
3256
  }
3223
3257
  const { servers } = await readServers(deps.configPath);
3258
+ if (!Object.hasOwn(servers, name)) {
3259
+ return { ok: false, message: `Server "${name}" is not in config.` };
3260
+ }
3224
3261
  const cfg = servers[name];
3225
3262
  if (!cfg) return { ok: false, message: `Server "${name}" is not in config.` };
3226
3263
  return startServer(name, { ...cfg, name }, deps, `Server "${name}" started`, { restart: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/mcp",
3
- "version": "0.317.2",
3
+ "version": "0.319.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack Model Context Protocol client and registry: stdio, SSE, and streamable HTTP transports.",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "!dist/**/*.map"
27
27
  ],
28
28
  "dependencies": {
29
- "@wrongstack/core": "0.317.2"
29
+ "@wrongstack/core": "0.319.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^26.2.0",