@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 +2 -13
- package/dist/server/index.js +34 -52
- package/package.json +2 -2
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
|
-
|
|
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 = () => { };
|
package/dist/server/index.js
CHANGED
|
@@ -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
|
-
|
|
213
|
-
let directoryArgs = [];
|
|
186
|
+
const directoryArgs = parseArguments();
|
|
214
187
|
// Async initialization function to be called in runServer()
|
|
215
188
|
async function initializeDirectories() {
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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",
|