mcp-for-i 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -89,6 +89,7 @@ In the UI, runtime buttons behave as follows:
89
89
  - `Update MCP`
90
90
  - If running from a git checkout: pulls latest changes from `origin/<current-branch>`, then installs/builds.
91
91
  - If running as npm-installed package: upgrades global npm package to latest.
92
+ - On Windows global installs, update runs in a detached updater and briefly restarts control plane to avoid file-lock (`EBUSY`) errors.
92
93
  - `Update Skills`
93
94
  - Pulls/clones the configured skills repository and branch into the local `skills` directory.
94
95
 
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "child_process";
2
2
  import fs from "fs/promises";
3
+ import os from "os";
3
4
  import path from "path";
4
5
  import { fileURLToPath } from "url";
5
6
  import { AUTOSTART_TASK_NAME, configureWindowsAutostart, getControlPlaneUrl, getWindowsAutostartStatus, isControlPlaneRunning, removeWindowsAutostart } from "./autostart.js";
@@ -26,8 +27,7 @@ export class RuntimeService {
26
27
  await this.buildIfPresent(push);
27
28
  return;
28
29
  }
29
- push(`Running global install for ${PACKAGE_NAME}@latest`);
30
- await this.runNpm(["install", "-g", `${PACKAGE_NAME}@latest`], this.rootDir, push);
30
+ await this.runGlobalPackageUpdate(push, "install");
31
31
  });
32
32
  }
33
33
  async updateMcp() {
@@ -47,8 +47,7 @@ export class RuntimeService {
47
47
  await this.buildIfPresent(push);
48
48
  return;
49
49
  }
50
- push(`Running global update for ${PACKAGE_NAME}@latest`);
51
- await this.runNpm(["install", "-g", `${PACKAGE_NAME}@latest`], this.rootDir, push);
50
+ await this.runGlobalPackageUpdate(push, "update");
52
51
  });
53
52
  }
54
53
  async updateSkills(options) {
@@ -261,6 +260,50 @@ export class RuntimeService {
261
260
  return undefined;
262
261
  }
263
262
  }
263
+ async runGlobalPackageUpdate(push, action) {
264
+ if (process.platform === "win32" && this.isLikelyGlobalInstall()) {
265
+ await this.scheduleWindowsSelfUpdate(push, action);
266
+ return;
267
+ }
268
+ push(`Running global ${action} for ${PACKAGE_NAME}@latest`);
269
+ await this.runNpm(["install", "-g", `${PACKAGE_NAME}@latest`], this.rootDir, push);
270
+ }
271
+ isLikelyGlobalInstall() {
272
+ const normalizedRoot = path.normalize(this.rootDir).toLowerCase();
273
+ const marker = `${path.sep}node_modules${path.sep}${PACKAGE_NAME}`.toLowerCase();
274
+ return normalizedRoot.includes(marker);
275
+ }
276
+ async scheduleWindowsSelfUpdate(push, action) {
277
+ const controlPlaneScript = path.join(path.dirname(fileURLToPath(import.meta.url)), "index.js");
278
+ const jobId = `${Date.now()}-${process.pid}`;
279
+ const logPath = path.join(os.tmpdir(), `${PACKAGE_NAME}-${action}-${jobId}.log`);
280
+ const scriptPath = path.join(os.tmpdir(), `${PACKAGE_NAME}-${action}-${jobId}.cmd`);
281
+ const npmCmdPath = path.join(path.dirname(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
282
+ const npmCommand = await pathExists(npmCmdPath) ? `"${npmCmdPath}"` : "npm.cmd";
283
+ const installCommand = `${npmCommand} install -g ${PACKAGE_NAME}@latest`;
284
+ const script = [
285
+ "@echo off",
286
+ "setlocal",
287
+ `set \"LOG_PATH=${logPath}\"`,
288
+ `echo [${new Date().toISOString()}] Scheduled ${action}>>\"%LOG_PATH%\"`,
289
+ "timeout /t 2 /nobreak >nul",
290
+ `taskkill /PID ${process.pid} /F >nul 2>nul`,
291
+ `${installCommand} >>\"%LOG_PATH%\" 2>&1`,
292
+ "if errorlevel 1 goto :end",
293
+ `start \"\" \"${process.execPath}\" \"${controlPlaneScript}\" serve`,
294
+ ":end",
295
+ `echo [${new Date().toISOString()}] Finished ${action}>>\"%LOG_PATH%\"`,
296
+ "del \"%~f0\" >nul 2>nul"
297
+ ].join("\r\n");
298
+ await fs.writeFile(scriptPath, script, "utf8");
299
+ spawn("cmd.exe", ["/d", "/s", "/c", scriptPath], {
300
+ detached: true,
301
+ stdio: "ignore"
302
+ }).unref();
303
+ push(`Scheduled self-${action} in background to avoid Windows file-lock errors.`);
304
+ push("Control plane will stop briefly, then restart automatically.");
305
+ push(`Background update log: ${logPath}`);
306
+ }
264
307
  }
265
308
  function makeJob(id) {
266
309
  return {
@@ -399,6 +399,7 @@ export function renderControlPlaneHtml() {
399
399
  <div id="autostartInfo" class="hint">Startup status: checking...</div>
400
400
  <div id="logs" class="logs">No jobs yet.</div>
401
401
  <div class="hint">Install/Repair uses local git checkout when available, otherwise global npm install. Update MCP follows the same mode.</div>
402
+ <div class="hint">Windows global updates may briefly restart the local control-plane service.</div>
402
403
  <div class="hint">Skills update can clone or pull from the repo and branch shown above.</div>
403
404
  </section>
404
405
 
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { getTools, handleTool } from "./mcp/tools.js";
6
6
  import { McpContext } from "./mcp/context.js";
7
7
  import { log } from "./mcp/logger.js";
8
8
  import { validateToolInput } from "./mcp/validation.js";
9
- const server = new Server({ name: "mcp-for-i", version: "0.1.0" }, { capabilities: { tools: {} } });
9
+ const server = new Server({ name: "mcp-for-i", version: "0.1.3" }, { capabilities: { tools: {} } });
10
10
  const ctx = new McpContext();
11
11
  const tools = getTools();
12
12
  const toolMap = new Map(tools.map(tool => [tool.name, tool]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-for-i",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "description": "Local MCP server and control plane for IBM i workflows",
6
6
  "type": "module",