@atlassian/mcp-compressor 0.31.5 → 0.31.7

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/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, Option } from "commander";
3
3
  import { spawn } from "node:child_process";
4
- import { existsSync } from "node:fs";
5
- import { dirname, join } from "node:path";
4
+ import { accessSync, closeSync, constants, existsSync, openSync, readSync } from "node:fs";
5
+ import { delimiter, dirname, join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { VERSION } from "./version.js";
8
8
  function parseBackendArg(backendArgs) {
@@ -121,12 +121,85 @@ function parseClearOAuthArgs(args) {
121
121
  const options = clearProgram.opts();
122
122
  return { ...(target ? { target } : {}), all: options.all ?? false };
123
123
  }
124
+ /**
125
+ * Resolve a bare binary name to an absolute path using the PATH environment
126
+ * variable. Returns null when the name cannot be found in any PATH directory.
127
+ */
128
+ function resolveFromPath(name) {
129
+ const pathEnv = process.env.PATH ?? "";
130
+ const dirs = pathEnv.split(delimiter);
131
+ const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";") : [""];
132
+ for (const dir of dirs) {
133
+ for (const ext of exts) {
134
+ const candidate = join(dir, name + ext);
135
+ try {
136
+ accessSync(candidate, constants.X_OK);
137
+ return candidate;
138
+ }
139
+ catch {
140
+ // not executable or not found — try next
141
+ }
142
+ }
143
+ }
144
+ return null;
145
+ }
146
+ /**
147
+ * Return true when the file at `filePath` begins with a native-executable
148
+ * magic number (ELF, Mach-O, or PE). A JS shim that starts with a `#!`
149
+ * shebang (or any other non-native header) returns false.
150
+ *
151
+ * This guards against the fork-bomb that occurs when the npm-installed
152
+ * `mcp-compressor` bin shim is the only entry on PATH: spawning it would
153
+ * re-invoke this very script rather than the Rust binary.
154
+ */
155
+ function isNativeExecutable(filePath) {
156
+ let fd;
157
+ try {
158
+ fd = openSync(filePath, "r");
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ try {
164
+ const buf = Buffer.alloc(4);
165
+ const n = readSync(fd, buf, 0, 4, 0);
166
+ if (n < 4)
167
+ return false;
168
+ // ELF (Linux/BSD): \x7fELF
169
+ if (buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)
170
+ return true;
171
+ // PE (Windows): MZ
172
+ if (buf[0] === 0x4d && buf[1] === 0x5a)
173
+ return true;
174
+ // Mach-O (macOS) — big-endian and little-endian variants, including fat/universal
175
+ const be = buf.readUInt32BE(0);
176
+ if (be === 0xfeedface || // Mach-O 32-bit BE
177
+ be === 0xfeedfacf || // Mach-O 64-bit BE
178
+ be === 0xcefaedfe || // Mach-O 32-bit LE
179
+ be === 0xcffaedfe || // Mach-O 64-bit LE
180
+ be === 0xcafebabe || // Mach-O fat binary
181
+ be === 0xbebafeca // Mach-O fat binary LE
182
+ )
183
+ return true;
184
+ return false;
185
+ }
186
+ finally {
187
+ closeSync(fd);
188
+ }
189
+ }
124
190
  function candidateCoreBinaries() {
125
191
  const candidates = [];
126
192
  if (process.env.MCP_COMPRESSOR_BINARY) {
127
193
  candidates.push(process.env.MCP_COMPRESSOR_BINARY);
128
194
  }
129
- candidates.push("mcp-compressor");
195
+ // Resolve the bare name from PATH and only include it when the resolved
196
+ // file is a native binary, not a JS shim. Without this check the npm bin
197
+ // shim (which maps `mcp-compressor` back to this very script) would be
198
+ // spawned indefinitely, creating a fork bomb.
199
+ const resolved = resolveFromPath("mcp-compressor");
200
+ if (resolved !== null && isNativeExecutable(resolved)) {
201
+ candidates.push(resolved);
202
+ }
130
203
  const here = dirname(fileURLToPath(import.meta.url));
131
204
  candidates.push(join(here, "..", "..", "target", "debug", process.platform === "win32" ? "mcp-compressor.exe" : "mcp-compressor"));
132
205
  candidates.push(join(process.cwd(), "..", "target", "debug", process.platform === "win32" ? "mcp-compressor.exe" : "mcp-compressor"));
@@ -141,7 +214,7 @@ function translateArgsForRust(args) {
141
214
  }
142
215
  async function runRustCoreCli(args) {
143
216
  for (const binary of candidateCoreBinaries()) {
144
- if (binary !== "mcp-compressor" && !existsSync(binary)) {
217
+ if (!existsSync(binary)) {
145
218
  continue;
146
219
  }
147
220
  const child = spawn(binary, translateArgsForRust(args), { stdio: "inherit" });
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlassian/mcp-compressor",
3
- "version": "0.31.5",
3
+ "version": "0.31.7",
4
4
  "description": "TypeScript MCP server wrapper for reducing tokens consumed by MCP tools.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/atlassian-labs/mcp-compressor",