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.
159 lines
3.9 KiB
JavaScript
159 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
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) {
|
|
const resolved = path.resolve(envBin);
|
|
if (isGoMcpBinary(resolved)) return resolved;
|
|
}
|
|
|
|
const onPath = findOnPath('opsxdb-mcp');
|
|
if (onPath) return onPath;
|
|
|
|
const home = os.homedir();
|
|
const candidates = [
|
|
path.join(home, 'devops', 'opsxdb', 'bin', 'opsxdb-mcp'),
|
|
path.join(home, '.opsxdb', 'bin', 'opsxdb-mcp'),
|
|
path.join(home, '.local', 'bin', 'opsxdb-mcp'),
|
|
'/usr/local/bin/opsxdb-mcp',
|
|
'/opt/homebrew/bin/opsxdb-mcp',
|
|
];
|
|
for (const c of candidates) {
|
|
if (isGoMcpBinary(c)) return c;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findOnPath(name) {
|
|
const pathEnv = process.env.PATH || '';
|
|
for (const dir of pathEnv.split(path.delimiter)) {
|
|
if (!dir) continue;
|
|
const p = path.join(dir, name);
|
|
if (isGoMcpBinary(p)) return p;
|
|
if (process.platform === 'win32') {
|
|
const pe = p + '.exe';
|
|
if (isGoMcpBinary(pe)) return pe;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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 {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
This npm package (@rlwj-lab/opsxdb-mcp) is a Node launcher for the OpsxDB
|
|
MCP server (stdio). The real server is a Go binary named opsxdb-mcp.
|
|
|
|
Build it from the opsxdb repo:
|
|
|
|
cd server && go build -o ../bin/opsxdb-mcp ./cmd/opsxdb-mcp
|
|
|
|
Then either:
|
|
- 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:
|
|
|
|
{
|
|
"mcpServers": {
|
|
"opsxdb": {
|
|
"command": "npx",
|
|
"args": ["-y", "@rlwj-lab/opsxdb-mcp"],
|
|
"env": {
|
|
"OPSXDB_MCP_BIN": "/absolute/path/to/opsxdb-mcp"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Config files (same as native binary):
|
|
~/.opsxdb/mcp.json
|
|
~/.opsxdb/connections.json
|
|
`;
|
|
}
|
|
|
|
module.exports = {
|
|
resolveBinary,
|
|
missingBinaryMessage,
|
|
isGoMcpBinary,
|
|
};
|