@n0zer0d4y/vulcan-file-ops 1.2.6 → 1.2.8

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,19 +2,8 @@
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
- //
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
- // MCP mode detection: suppress console if ANY of these are true:
10
- // 1. stdin is piped/redirected (not a TTY) - MCP clients pipe JSON-RPC via stdin
11
- // 2. stdout is piped/redirected (not a TTY) - MCP clients read JSON-RPC from stdout
12
- // 3. argv contains "mcp" or "stdio" - explicit MCP mode indicator
13
- // Using OR (||) instead of AND (&&) because MCP clients may only pipe one direction
14
- const isMCP = !isHelpOrVersion &&
15
- (!process.stdin.isTTY ||
16
- !process.stdout.isTTY ||
17
- process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio")));
5
+ const isMCP = (!process.stdin.isTTY && !process.stdout.isTTY) ||
6
+ process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio"));
18
7
  if (isMCP) {
19
8
  // Suppress all console methods (but NOT stdout/stderr streams - MCP SDK needs those)
20
9
  const noop = () => { };
@@ -1,33 +1,7 @@
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
- // MCP mode detection: suppress console if ANY of these are true:
12
- // 1. stdin is piped/redirected (not a TTY) - MCP clients pipe JSON-RPC via stdin
13
- // 2. stdout is piped/redirected (not a TTY) - MCP clients read JSON-RPC from stdout
14
- // 3. argv contains "mcp" or "stdio" - explicit MCP mode indicator
15
- // Using OR (||) instead of AND (&&) because MCP clients may only pipe one direction
16
- const _mcpMode = !_isHelpOrVersion &&
17
- (!process.stdin.isTTY ||
18
- !process.stdout.isTTY ||
19
- process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio")));
20
- if (_mcpMode) {
21
- const noop = () => { };
22
- console.log = noop;
23
- console.error = noop;
24
- console.warn = noop;
25
- console.info = noop;
26
- console.debug = noop;
27
- }
28
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
29
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
30
- import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, PingRequestSchema, RootsListChangedNotificationSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, PingRequestSchema, RootsListChangedNotificationSchema, LATEST_PROTOCOL_VERSION, } from "@modelcontextprotocol/sdk/types.js";
31
5
  import fs from "fs/promises";
32
6
  import path from "path";
33
7
  import dotenv from "dotenv";
@@ -209,13 +183,22 @@ function parseArguments() {
209
183
  }
210
184
  return directories;
211
185
  }
212
- // Defer parseArguments() call to runServer() to avoid module-level side effects
213
- let directoryArgs = [];
186
+ const directoryArgs = parseArguments();
214
187
  // Async initialization function to be called in runServer()
215
188
  async function initializeDirectories() {
216
- // Use the already-determined MCP mode from top of file
217
- // Console suppression is already done at module load time via _mcpMode
218
- if (!_mcpMode &&
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 &&
219
202
  (approvedFoldersFromArgs.length > 0 ||
220
203
  directoryArgs.length > 0 ||
221
204
  ignoredFolders.length > 0 ||
@@ -265,7 +248,7 @@ async function initializeDirectories() {
265
248
  const errorMsg = `Error: Approved folder ${dir} is not a directory`;
266
249
  console.error(errorMsg);
267
250
  // In MCP mode, don't exit - just skip invalid directories
268
- if (!_mcpMode) {
251
+ if (!isMCP) {
269
252
  process.exit(1);
270
253
  }
271
254
  }
@@ -274,13 +257,13 @@ async function initializeDirectories() {
274
257
  const errorMsg = `Error accessing approved folder ${dir}: ${error}`;
275
258
  console.error(errorMsg);
276
259
  // In MCP mode, don't exit - just skip invalid directories
277
- if (!_mcpMode) {
260
+ if (!isMCP) {
278
261
  process.exit(1);
279
262
  }
280
263
  }
281
264
  }));
282
265
  // Only log initialization if not running under MCP
283
- if (!_mcpMode) {
266
+ if (!isMCP) {
284
267
  console.error(`Initialized with ${allowedDirectories.length} approved ${allowedDirectories.length === 1 ? "directory" : "directories"}`);
285
268
  }
286
269
  }
@@ -308,7 +291,7 @@ async function initializeDirectories() {
308
291
  const errorMsg = `Error: Legacy directory ${dir} is not a directory`;
309
292
  console.error(errorMsg);
310
293
  // In MCP mode, don't exit - just skip invalid directories
311
- if (!_mcpMode) {
294
+ if (!isMCP) {
312
295
  process.exit(1);
313
296
  }
314
297
  }
@@ -317,7 +300,7 @@ async function initializeDirectories() {
317
300
  const errorMsg = `Error accessing legacy directory ${dir}: ${error}`;
318
301
  console.error(errorMsg);
319
302
  // In MCP mode, don't exit - just skip invalid directories
320
- if (!_mcpMode) {
303
+ if (!isMCP) {
321
304
  process.exit(1);
322
305
  }
323
306
  }
@@ -339,7 +322,7 @@ async function initializeDirectories() {
339
322
  // Priority 1: --approved-commands from CLI (supersedes .env)
340
323
  if (approvedCommandsFromArgs.length > 0) {
341
324
  finalApprovedCommands = approvedCommandsFromArgs;
342
- if (!_mcpMode) {
325
+ if (!isMCP) {
343
326
  console.error(` Approved commands (from CLI): ${finalApprovedCommands.join(", ")}`);
344
327
  }
345
328
  }
@@ -347,19 +330,18 @@ async function initializeDirectories() {
347
330
  // Priority 2: Load from .env file
348
331
  try {
349
332
  const envPath = path.join(process.cwd(), ".env");
350
- // CRITICAL: quiet: true prevents dotenv from logging to stdout which corrupts MCP JSON-RPC
351
- dotenv.config({ path: envPath, quiet: true });
333
+ dotenv.config({ path: envPath });
352
334
  if (process.env.APPROVED_COMMANDS) {
353
335
  finalApprovedCommands = process.env.APPROVED_COMMANDS.split(",")
354
336
  .map((c) => c.trim())
355
337
  .filter((c) => c.length > 0);
356
- if (!_mcpMode) {
338
+ if (!isMCP) {
357
339
  console.error(` Approved commands (from .env): ${finalApprovedCommands.join(", ")}`);
358
340
  }
359
341
  }
360
342
  }
361
343
  catch (error) {
362
- if (!_mcpMode) {
344
+ if (!isMCP) {
363
345
  console.error(" Note: Could not load .env file (this is okay if using CLI args)");
364
346
  }
365
347
  }
@@ -367,14 +349,14 @@ async function initializeDirectories() {
367
349
  // Initialize shell tool with approved commands
368
350
  if (finalApprovedCommands.length > 0) {
369
351
  initializeShellTool(finalApprovedCommands);
370
- if (!_mcpMode) {
352
+ if (!isMCP) {
371
353
  console.error(`Initialized shell tool with ${finalApprovedCommands.length} approved command(s)`);
372
354
  }
373
355
  }
374
356
  else {
375
357
  initializeShellTool([]);
376
358
  // Only log shell initialization if not running under MCP
377
- if (!_mcpMode) {
359
+ if (!isMCP) {
378
360
  console.error("Shell tool initialized with no pre-approved commands (all commands require approval)");
379
361
  }
380
362
  }
@@ -400,21 +382,21 @@ const server = new Server({
400
382
  tools: {
401
383
  listChanged: true,
402
384
  },
385
+ resources: {},
386
+ prompts: {},
403
387
  },
404
388
  });
405
389
  // Initialize handler - required for MCP protocol
406
390
  server.setRequestHandler(InitializeRequestSchema, async (request) => {
407
391
  const clientCapabilities = request.params.capabilities;
408
392
  return {
409
- // CRITICAL FIX: Use 2025-06-18 for Claude Desktop compatibility
410
- // Claude Desktop (as of 2026-01-21) only supports protocol version 2025-06-18
411
- // Using LATEST_PROTOCOL_VERSION (2025-11-25) causes immediate disconnection
412
- // See: RCA-Claude-Desktop-MCP-Connection-Failure-2026-01-21.md
413
- protocolVersion: "2025-06-18",
393
+ protocolVersion: LATEST_PROTOCOL_VERSION,
414
394
  capabilities: {
415
395
  tools: {
416
396
  listChanged: true,
417
397
  },
398
+ resources: {},
399
+ prompts: {},
418
400
  },
419
401
  serverInfo: {
420
402
  name: "vulcan-file-ops",
@@ -643,8 +625,6 @@ server.oninitialized = async () => {
643
625
  };
644
626
  // Start server
645
627
  export async function runServer() {
646
- // Parse arguments here (not at module level) to ensure console suppression is active
647
- directoryArgs = parseArguments();
648
628
  // Initialize directories before starting server
649
629
  // BUT: Don't exit on errors during MCP mode - just log and continue
650
630
  try {
@@ -653,7 +633,9 @@ export async function runServer() {
653
633
  catch (error) {
654
634
  // In MCP mode, don't crash the server on init errors
655
635
  // Just continue with empty configuration
656
- if (!_mcpMode) {
636
+ const isMCP = (!process.stdin.isTTY && !process.stdout.isTTY) ||
637
+ process.argv.some((arg) => arg.includes("mcp") || arg.includes("stdio"));
638
+ if (!isMCP) {
657
639
  // In non-MCP mode, we can show errors and exit
658
640
  throw error;
659
641
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n0zer0d4y/vulcan-file-ops",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
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",
@@ -65,7 +65,7 @@
65
65
  "lint": "npm run lint:json"
66
66
  },
67
67
  "dependencies": {
68
- "@modelcontextprotocol/sdk": "^1.25.2",
68
+ "@modelcontextprotocol/sdk": "1.20.0",
69
69
  "@turbodocx/html-to-docx": "^1.16.0",
70
70
  "diff": "^8.0.3",
71
71
  "docx": "^9.5.1",