From b9c1df25b85362af0950f1859eacdb2fbc8a5d62 Mon Sep 17 00:00:00 2001 From: Karstein Phobic Nyvold Kvistad Date: Mon, 27 Apr 2026 20:31:21 +0200 Subject: [PATCH] feat(postinstall): dump full .mcp.json snippet after global npm install Runs after `npm install -g codesys-mcp-sp21-plus`. Detects every CODESYS install on PATH and prints the ready-to-paste .mcp.json block per install (same output as `--print-config`). Guards: - Skipped during local installs / dev clones (npm_config_global != true) - Skipped in CI (CI=true or npm_config_ci=true) - Wrapped in try/catch + 'node ... || true' so a banner failure never blocks the install - Non-Windows: prints a note and exits cleanly - Zero CODESYS installs: prints a hint pointing at --print-config Resolves the awkward 'now run these two commands to verify and get your config' step from the README. --- package.json | 3 +- src/postinstall.ts | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/postinstall.ts diff --git a/package.json b/package.json index 818e7be..88af7d2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "test": "vitest --run", "test:watch": "vitest", "typecheck": "tsc --noEmit", - "prepublishOnly": "npm run build && npm test" + "prepublishOnly": "npm run build && npm test", + "postinstall": "node dist/postinstall.js || true" }, "keywords": [ "codesys", diff --git a/src/postinstall.ts b/src/postinstall.ts new file mode 100644 index 0000000..1fbb4fc --- /dev/null +++ b/src/postinstall.ts @@ -0,0 +1,88 @@ +#!/usr/bin/env node +/** + * Runs after `npm install [-g] codesys-mcp-sp21-plus`. + * + * Goals: + * - Confirm to the user that the install worked + show the version + * - Dump the FULL ready-to-paste .mcp.json snippet for every detected install + * - Stay silent during dev (`npm install` inside a clone of the repo) and + * during CI to avoid polluting output. + * + * Failure-tolerant: any error (no Windows, no CODESYS, fs perms) is caught + * and downgraded to a friendly note. We never block the install. + */ + +function isDevOrCi(): boolean { + // Skip during local installs / dev clones. + if (process.env.npm_config_global !== 'true') return true; + // Skip in CI. + if (process.env.CI === 'true') return true; + if (process.env.npm_config_ci === 'true') return true; + return false; +} + +async function main(): Promise { + if (isDevOrCi()) { + return; + } + + let version = 'unknown'; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + version = require('../package.json').version; + } catch { + // ignore + } + + process.stdout.write(`\n=== codesys-mcp-sp21-plus v${version} installed ===\n\n`); + + if (process.platform !== 'win32') { + process.stdout.write( + `(CODESYS detection only runs on Windows -- this server requires Windows + CODESYS.)\n\n` + ); + return; + } + + let installs: Array<{ serverName: string; profileName: string }>; + let printConfig: ( + installs: unknown[], + opts?: Record + ) => string; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const detectMod = require('./detect'); + installs = detectMod.detectInstalls(); + printConfig = detectMod.printConfig; + } catch (err) { + process.stdout.write( + `(CODESYS install detection failed: ${(err as Error).message})\n\n` + ); + return; + } + + if (installs.length === 0) { + process.stdout.write( + `No CODESYS installations detected under "C:\\Program Files" / "C:\\Program Files (x86)".\n` + + `Install CODESYS first, then run \`codesys-mcp-sp21-plus --print-config\` to generate\n` + + `the .mcp.json snippet.\n\n` + ); + return; + } + + process.stdout.write(`Add the following to your .mcp.json (Claude Code configuration):\n\n`); + try { + process.stdout.write(printConfig(installs) + '\n\n'); + } catch (err) { + process.stdout.write(`(printConfig failed: ${(err as Error).message})\n\n`); + return; + } + process.stdout.write( + `Re-run anytime with: codesys-mcp-sp21-plus --print-config\n` + + `Filter to one SP family with: codesys-mcp-sp21-plus --print-config --sp 22\n\n` + ); +} + +main().catch((err) => { + // Never fail the install over a banner. + process.stdout.write(`\n(post-install banner failed: ${(err as Error).message})\n\n`); +});