@n0zer0d4y/vulcan-file-ops 1.2.4 → 1.2.5

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
@@ -2,8 +2,13 @@
2
2
  // CRITICAL: Detect MCP mode and suppress console output BEFORE any imports
3
3
  // MCP servers use stdin/stdout for JSON-RPC via stdio transport
4
4
  // Detection: stdin/stdout are NOT TTY (piped/redirected) = MCP mode
5
- const isMCP = (!process.stdin.isTTY && !process.stdout.isTTY) ||
6
- process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio"));
5
+ //
6
+ // EXCEPTION: --help and --version flags should always output to console
7
+ // even when stdin/stdout are piped (e.g., in scripts or CI environments)
8
+ const isHelpOrVersion = process.argv.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v");
9
+ const isMCP = !isHelpOrVersion &&
10
+ ((!process.stdin.isTTY && !process.stdout.isTTY) ||
11
+ process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio")));
7
12
  if (isMCP) {
8
13
  // Suppress all console methods (but NOT stdout/stderr streams - MCP SDK needs those)
9
14
  const noop = () => { };
@@ -1,4 +1,24 @@
1
1
  #!/usr/bin/env node
2
+ // CRITICAL: Suppress console output IMMEDIATELY, before any imports
3
+ // This MUST be the first executable code due to ES Module execution order.
4
+ // With static imports, child modules are evaluated BEFORE the importing
5
+ // module's top-level code runs. By placing suppression here at the very top
6
+ // of the main server module, we ensure no console output can corrupt the
7
+ // MCP JSON-RPC protocol stream on stdout.
8
+ // See: RCA-Claude-Desktop-MCP-Toggle-Failure-2026-01-31.md
9
+ // Check for CLI info flags BEFORE suppression - these should always work
10
+ const _isHelpOrVersion = process.argv.some((arg) => arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v");
11
+ const _mcpMode = !_isHelpOrVersion &&
12
+ ((!process.stdin.isTTY && !process.stdout.isTTY) ||
13
+ process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio")));
14
+ if (_mcpMode) {
15
+ const noop = () => { };
16
+ console.log = noop;
17
+ console.error = noop;
18
+ console.warn = noop;
19
+ console.info = noop;
20
+ console.debug = noop;
21
+ }
2
22
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
23
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
24
  import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, PingRequestSchema, RootsListChangedNotificationSchema, } from "@modelcontextprotocol/sdk/types.js";
@@ -183,22 +203,13 @@ function parseArguments() {
183
203
  }
184
204
  return directories;
185
205
  }
186
- const directoryArgs = parseArguments();
206
+ // Defer parseArguments() call to runServer() to avoid module-level side effects
207
+ let directoryArgs = [];
187
208
  // Async initialization function to be called in runServer()
188
209
  async function initializeDirectories() {
189
- // Detect MCP mode: stdin/stdout are NOT TTY (piped) = MCP mode
190
- const isMCP = (!process.stdin.isTTY && !process.stdout.isTTY) ||
191
- process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio"));
192
- // During MCP operation, suppress ALL console output to prevent protocol corruption
193
- if (isMCP) {
194
- const noop = () => { };
195
- console.error = noop;
196
- console.log = noop;
197
- console.warn = noop;
198
- console.info = noop;
199
- console.debug = noop;
200
- }
201
- if (!isMCP &&
210
+ // Use the already-determined MCP mode from top of file
211
+ // Console suppression is already done at module load time via _mcpMode
212
+ if (!_mcpMode &&
202
213
  (approvedFoldersFromArgs.length > 0 ||
203
214
  directoryArgs.length > 0 ||
204
215
  ignoredFolders.length > 0 ||
@@ -248,7 +259,7 @@ async function initializeDirectories() {
248
259
  const errorMsg = `Error: Approved folder ${dir} is not a directory`;
249
260
  console.error(errorMsg);
250
261
  // In MCP mode, don't exit - just skip invalid directories
251
- if (!isMCP) {
262
+ if (!_mcpMode) {
252
263
  process.exit(1);
253
264
  }
254
265
  }
@@ -257,13 +268,13 @@ async function initializeDirectories() {
257
268
  const errorMsg = `Error accessing approved folder ${dir}: ${error}`;
258
269
  console.error(errorMsg);
259
270
  // In MCP mode, don't exit - just skip invalid directories
260
- if (!isMCP) {
271
+ if (!_mcpMode) {
261
272
  process.exit(1);
262
273
  }
263
274
  }
264
275
  }));
265
276
  // Only log initialization if not running under MCP
266
- if (!isMCP) {
277
+ if (!_mcpMode) {
267
278
  console.error(`Initialized with ${allowedDirectories.length} approved ${allowedDirectories.length === 1 ? "directory" : "directories"}`);
268
279
  }
269
280
  }
@@ -291,7 +302,7 @@ async function initializeDirectories() {
291
302
  const errorMsg = `Error: Legacy directory ${dir} is not a directory`;
292
303
  console.error(errorMsg);
293
304
  // In MCP mode, don't exit - just skip invalid directories
294
- if (!isMCP) {
305
+ if (!_mcpMode) {
295
306
  process.exit(1);
296
307
  }
297
308
  }
@@ -300,7 +311,7 @@ async function initializeDirectories() {
300
311
  const errorMsg = `Error accessing legacy directory ${dir}: ${error}`;
301
312
  console.error(errorMsg);
302
313
  // In MCP mode, don't exit - just skip invalid directories
303
- if (!isMCP) {
314
+ if (!_mcpMode) {
304
315
  process.exit(1);
305
316
  }
306
317
  }
@@ -322,7 +333,7 @@ async function initializeDirectories() {
322
333
  // Priority 1: --approved-commands from CLI (supersedes .env)
323
334
  if (approvedCommandsFromArgs.length > 0) {
324
335
  finalApprovedCommands = approvedCommandsFromArgs;
325
- if (!isMCP) {
336
+ if (!_mcpMode) {
326
337
  console.error(` Approved commands (from CLI): ${finalApprovedCommands.join(", ")}`);
327
338
  }
328
339
  }
@@ -335,13 +346,13 @@ async function initializeDirectories() {
335
346
  finalApprovedCommands = process.env.APPROVED_COMMANDS.split(",")
336
347
  .map((c) => c.trim())
337
348
  .filter((c) => c.length > 0);
338
- if (!isMCP) {
349
+ if (!_mcpMode) {
339
350
  console.error(` Approved commands (from .env): ${finalApprovedCommands.join(", ")}`);
340
351
  }
341
352
  }
342
353
  }
343
354
  catch (error) {
344
- if (!isMCP) {
355
+ if (!_mcpMode) {
345
356
  console.error(" Note: Could not load .env file (this is okay if using CLI args)");
346
357
  }
347
358
  }
@@ -349,14 +360,14 @@ async function initializeDirectories() {
349
360
  // Initialize shell tool with approved commands
350
361
  if (finalApprovedCommands.length > 0) {
351
362
  initializeShellTool(finalApprovedCommands);
352
- if (!isMCP) {
363
+ if (!_mcpMode) {
353
364
  console.error(`Initialized shell tool with ${finalApprovedCommands.length} approved command(s)`);
354
365
  }
355
366
  }
356
367
  else {
357
368
  initializeShellTool([]);
358
369
  // Only log shell initialization if not running under MCP
359
- if (!isMCP) {
370
+ if (!_mcpMode) {
360
371
  console.error("Shell tool initialized with no pre-approved commands (all commands require approval)");
361
372
  }
362
373
  }
@@ -625,6 +636,8 @@ server.oninitialized = async () => {
625
636
  };
626
637
  // Start server
627
638
  export async function runServer() {
639
+ // Parse arguments here (not at module level) to ensure console suppression is active
640
+ directoryArgs = parseArguments();
628
641
  // Initialize directories before starting server
629
642
  // BUT: Don't exit on errors during MCP mode - just log and continue
630
643
  try {
@@ -633,9 +646,7 @@ export async function runServer() {
633
646
  catch (error) {
634
647
  // In MCP mode, don't crash the server on init errors
635
648
  // Just continue with empty configuration
636
- const isMCP = (!process.stdin.isTTY && !process.stdout.isTTY) ||
637
- process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio"));
638
- if (!isMCP) {
649
+ if (!_mcpMode) {
639
650
  // In non-MCP mode, we can show errors and exit
640
651
  throw error;
641
652
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n0zer0d4y/vulcan-file-ops",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "mcpName": "io.github.n0zer0d4y/vulcan-file-ops",
5
5
  "description": "MCP server for AI assistants: read, write, edit, and manage files securely on local filesystem.",
6
6
  "license": "MIT",