fix(mcp-npm): v0.1.1 skip npm shim when resolving Go binary
CI / Go server (push) Canceled after 0s
CI / Web + core packages (push) Canceled after 0s
CI / Desktop (Tauri) cargo check (push) Canceled after 0s

npx installs a bin also named opsxdb-mcp; the launcher now only execs
native ELF/Mach-O/PE binaries so it no longer recurses into itself.
This commit is contained in:
logan committed 2026-10-15 13:30:05 +08:00
1 parent 979efb606d
commit 0aeee2b692
3 files changed
+79 -9

No files matched your search

+6 -2
View File
@@ -2,14 +2,16 @@
MCP (Model Context Protocol) launcher for OpsxDB.
This package ships a small Node CLI that **spawns the Go `opsxdb-mcp` binary** over stdio. Build the binary from the OpsxDB repo, then point the launcher at it.
This package ships a small Node CLI that **spawns the Go `opsxdb-mcp` binary** over stdio. Build the binary from the OpsxDB repo, then point the launcher at it (or put the Go binary on `PATH`).
> The npm bin is also named `opsxdb-mcp`. The launcher only execs a **native** Go/ELF/Mach-O/PE binary, so it will not recurse into its own npm shim.
## Install
```bash
npm i -g @rlwj-lab/opsxdb-mcp
# or
npx -y @rlwj-lab/opsxdb-mcp
npx -y @rlwj-lab/opsxdb-mcp@0.1.1
```
## Build the Go binary
@@ -26,6 +28,8 @@ Put `bin/opsxdb-mcp` on `PATH`, or set:
export OPSXDB_MCP_BIN=/absolute/path/to/opsxdb-mcp
```
If the Go binary is missing, the CLI exits with a clear error (not `command not found`).
## Cursor / Claude Desktop
```json
+72 -6
View File
@@ -7,11 +7,16 @@ const os = require('os');
/**
* Resolve the OpsxDB Go MCP binary path.
* Order: OPSXDB_MCP_BIN → PATH → common install locations.
* Skips this package's Node launcher and any #! script shims (npm/npx bin
* is also named opsxdb-mcp and must not be mistaken for the Go server).
* @returns {string|null}
*/
function resolveBinary() {
const envBin = process.env.OPSXDB_MCP_BIN;
if (envBin && isExecutable(envBin)) return path.resolve(envBin);
if (envBin) {
const resolved = path.resolve(envBin);
if (isGoMcpBinary(resolved)) return resolved;
}
const onPath = findOnPath('opsxdb-mcp');
if (onPath) return onPath;
@@ -25,7 +30,7 @@ function resolveBinary() {
'/opt/homebrew/bin/opsxdb-mcp',
];
for (const c of candidates) {
if (isExecutable(c)) return c;
if (isGoMcpBinary(c)) return c;
}
return null;
}
@@ -35,16 +40,38 @@ function findOnPath(name) {
for (const dir of pathEnv.split(path.delimiter)) {
if (!dir) continue;
const p = path.join(dir, name);
if (isExecutable(p)) return p;
if (process.platform === 'win32' && isExecutable(p + '.exe')) return p + '.exe';
if (isGoMcpBinary(p)) return p;
if (process.platform === 'win32') {
const pe = p + '.exe';
if (isGoMcpBinary(pe)) return pe;
}
}
return null;
}
function isExecutable(filePath) {
function selfCliPath() {
return path.resolve(__dirname, '..', 'bin', 'cli.js');
}
function isSameFile(a, b) {
try {
return fs.realpathSync(a) === fs.realpathSync(b);
} catch {
return false;
}
}
/**
* True only for a real Go/native opsxdb-mcp binary (ELF / Mach-O / PE),
* not npm bin shims or this package's Node cli.js.
*/
function isGoMcpBinary(filePath) {
try {
if (!filePath) return false;
const st = fs.statSync(filePath);
if (!st.isFile()) return false;
if (isSameFile(filePath, selfCliPath())) return false;
if (!isLikelyNativeBinary(filePath)) return false;
fs.accessSync(filePath, fs.constants.X_OK);
return true;
} catch {
@@ -52,6 +79,44 @@ function isExecutable(filePath) {
}
}
function isLikelyNativeBinary(filePath) {
let fd;
try {
fd = fs.openSync(filePath, 'r');
const buf = Buffer.alloc(8);
const n = fs.readSync(fd, buf, 0, 8, 0);
if (n < 2) return false;
// Reject scripts (npm shims, node shebang)
if (buf[0] === 0x23 && buf[1] === 0x21) return false; // #!
// ELF
if (n >= 4 && buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46) {
return true;
}
// PE
if (buf[0] === 0x4d && buf[1] === 0x5a) return true;
if (n < 4) return false;
const be = buf.readUInt32BE(0);
const le = buf.readUInt32LE(0);
// Mach-O 64 / 32 (both endians) + fat
const macho = new Set([
0xfeedfacf, 0xcffaedfe, 0xfeedface, 0xcefaedfe,
0xcafebabe, 0xbebafeca, 0xcafebabf, 0xbfbafeca,
]);
if (macho.has(be) || macho.has(le)) return true;
return false;
} catch {
return false;
} finally {
if (fd !== undefined) {
try {
fs.closeSync(fd);
} catch {
/* ignore */
}
}
}
}
function missingBinaryMessage() {
return `opsxdb-mcp: Go binary not found.
@@ -63,7 +128,7 @@ Build it from the opsxdb repo:
cd server && go build -o ../bin/opsxdb-mcp ./cmd/opsxdb-mcp
Then either:
- put bin/opsxdb-mcp on your PATH, or
- put that binary on your PATH (ahead of any npm shim), or
- export OPSXDB_MCP_BIN=/absolute/path/to/opsxdb-mcp
Cursor / Claude Desktop example:
@@ -89,4 +154,5 @@ Config files (same as native binary):
module.exports = {
resolveBinary,
missingBinaryMessage,
isGoMcpBinary,
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@rlwj-lab/opsxdb-mcp",
"version": "0.1.0",
"version": "0.1.1",
"description": "MCP server for opsxdb",
"main": "index.js",
"bin": {