@dmytro-prototypes/cadbridge-mcp 0.1.7 → 0.1.9

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 (3) hide show
  1. package/README.md +15 -5
  2. package/bin/cli.js +57 -2
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -20,11 +20,21 @@ commands and AutoLISP, and capture the current view.
20
20
  npx -y @dmytro-prototypes/cadbridge-mcp
21
21
  ```
22
22
 
23
- This launches the platform binary that npm installed alongside this package
24
- (picked automatically via `optionalDependencies` — nothing is downloaded at
25
- run time). `npx` is the only supported way to run it: it fetches over an
26
- HTTP client rather than a browser, so the binary is never quarantined by
27
- Gatekeeper or blocked by SmartScreen, and needs no code-signing certificate.
23
+ The platform binary ships alongside this package, selected automatically via
24
+ `optionalDependencies`. The launcher installs and runs a per-user copy:
25
+
26
+ - macOS: `~/Library/Application Support/CADBridge/bin/cadbridge-mcp`
27
+ - Windows: `%LOCALAPPDATA%\Programs\CADBridge\cadbridge-mcp.exe`
28
+
29
+ If `LOCALAPPDATA` is unset, Windows uses `%USERPROFILE%\AppData\Local`.
30
+ Later launches reuse it when its contents match the packaged binary; a different
31
+ package version replaces it atomically. The copy preserves the packaged code
32
+ signature. If Windows blocks an update because the executable is running, close
33
+ the CADBridge MCP connections in your clients and retry; the existing executable
34
+ is preserved when replacement fails.
35
+
36
+ Continue using the same `npx` command for startup and updates; no separate
37
+ installation command is needed.
28
38
 
29
39
  Supported platforms are macOS Apple Silicon and Windows x64 — the platforms
30
40
  AutoCAD itself runs on.
package/bin/cli.js CHANGED
@@ -11,8 +11,10 @@
11
11
  // private, so the URL would 404 for every user. Shipping the binaries inside
12
12
  // npm also keeps installs working behind proxies that only allow the registry.
13
13
  import { spawn } from "node:child_process"
14
- import { existsSync, realpathSync } from "node:fs"
14
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync } from "node:fs"
15
15
  import { createRequire } from "node:module"
16
+ import { homedir } from "node:os"
17
+ import { join } from "node:path"
16
18
  import { fileURLToPath } from "node:url"
17
19
 
18
20
  const require = createRequire(import.meta.url)
@@ -51,8 +53,61 @@ export function resolveBinary(key = platformKey()) {
51
53
  }
52
54
  }
53
55
 
56
+ // Keep the executable at one path across npm cache changes. Compare the
57
+ // actual bytes so unchanged launches preserve the installed file and updates
58
+ // retain the packaged signature. Rename a complete copy, never overwrite a
59
+ // binary that another MCP client may still be running.
60
+ export function installBinary(source, {
61
+ platform = process.platform,
62
+ home = homedir(),
63
+ localAppData = process.env.LOCALAPPDATA || join(home, "AppData", "Local"),
64
+ } = {}) {
65
+ if (platform !== "darwin" && platform !== "win32") return source
66
+
67
+ const windows = platform === "win32"
68
+ const dir = windows
69
+ ? join(localAppData, "Programs", "CADBridge")
70
+ : join(home, "Library", "Application Support", "CADBridge", "bin")
71
+ const destination = join(dir, windows ? "cadbridge-mcp.exe" : "cadbridge-mcp")
72
+ const packaged = readFileSync(source)
73
+ try {
74
+ if (readFileSync(destination).equals(packaged)) return destination
75
+ } catch (error) {
76
+ if (error.code !== "ENOENT") throw error
77
+ }
78
+
79
+ mkdirSync(dir, { recursive: true, mode: 0o700 })
80
+ const staging = mkdtempSync(join(dir, ".install-"))
81
+ try {
82
+ const temporary = join(staging, "cadbridge-mcp")
83
+ copyFileSync(source, temporary)
84
+ if (!windows) chmodSync(temporary, 0o755)
85
+ try {
86
+ renameSync(temporary, destination)
87
+ } catch (error) {
88
+ // Another launcher may have installed the same binary while we copied it.
89
+ try {
90
+ if (readFileSync(destination).equals(packaged)) return destination
91
+ } catch {
92
+ // Preserve the replacement error if the destination cannot be read.
93
+ }
94
+ if (windows && ["EACCES", "EPERM", "EBUSY"].includes(error.code)) {
95
+ throw new Error(
96
+ `Cannot update ${destination}. Close running CADBridge MCP connections in your ` +
97
+ `MCP clients and retry. If it still fails, check the folder permissions.`,
98
+ { cause: error },
99
+ )
100
+ }
101
+ throw error
102
+ }
103
+ } finally {
104
+ rmSync(staging, { recursive: true, force: true })
105
+ }
106
+ return destination
107
+ }
108
+
54
109
  export function run(argv = process.argv.slice(2)) {
55
- const binary = resolveBinary()
110
+ const binary = installBinary(resolveBinary())
56
111
  // stdio: "inherit" — this process is a stdio MCP server; the client's pipes
57
112
  // must reach the real binary untouched. Nothing here may write to stdout.
58
113
  const child = spawn(binary, argv, { stdio: "inherit" })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmytro-prototypes/cadbridge-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "mcpName": "net.dmytro-prototypes/cadbridge-mcp-autocad",
5
5
  "description": "Drive a live AutoCAD session from an AI client through MCP",
6
6
  "type": "module",
@@ -30,8 +30,8 @@
30
30
  "LICENSE.txt"
31
31
  ],
32
32
  "optionalDependencies": {
33
- "@dmytro-prototypes/cadbridge-mcp-darwin-arm64": "0.1.7",
34
- "@dmytro-prototypes/cadbridge-mcp-win32-x64": "0.1.7"
33
+ "@dmytro-prototypes/cadbridge-mcp-darwin-arm64": "0.1.9",
34
+ "@dmytro-prototypes/cadbridge-mcp-win32-x64": "0.1.9"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"