Skip to main content
Glama

vulcan-file-ops

approved-folders.test.ts10.3 kB
import { describe, test, expect, beforeEach, afterEach } from "@jest/globals"; import fs from "fs/promises"; import path from "path"; import { tmpdir } from "os"; import { promisify } from "util"; import { exec as execCallback } from "child_process"; const exec = promisify(execCallback); describe("Approved Folders Feature", () => { let testDir1: string; let testDir2: string; let testFile1: string; let testFile2: string; beforeEach(async () => { // Create test directories testDir1 = path.join(tmpdir(), `approved-folders-test1-${Date.now()}`); testDir2 = path.join(tmpdir(), `approved-folders-test2-${Date.now()}`); await fs.mkdir(testDir1, { recursive: true }); await fs.mkdir(testDir2, { recursive: true }); // Create test files in each directory testFile1 = path.join(testDir1, "test1.txt"); testFile2 = path.join(testDir2, "test2.txt"); await fs.writeFile(testFile1, "Content from test1"); await fs.writeFile(testFile2, "Content from test2"); }); afterEach(async () => { // Clean up test directories try { await fs.rm(testDir1, { recursive: true, force: true }); await fs.rm(testDir2, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); test("should validate approved folders argument parsing", () => { // This test validates that the argument parsing logic is correct // The actual parsing happens in server/index.ts const mockArgs = [ "--approved-folders", `${testDir1},${testDir2}`, "--ignored-folders", "node_modules", ]; // Simulate parsing logic const approvedFolders: string[] = []; const ignoredFolders: string[] = []; for (let i = 0; i < mockArgs.length; i++) { if (mockArgs[i] === "--approved-folders" && i + 1 < mockArgs.length) { approvedFolders.push( ...mockArgs[i + 1].split(",").map((f) => f.trim()) ); i++; } else if ( mockArgs[i] === "--ignored-folders" && i + 1 < mockArgs.length ) { ignoredFolders.push(...mockArgs[i + 1].split(",").map((f) => f.trim())); i++; } } expect(approvedFolders).toHaveLength(2); expect(approvedFolders[0]).toBe(testDir1); expect(approvedFolders[1]).toBe(testDir2); expect(ignoredFolders).toHaveLength(1); expect(ignoredFolders[0]).toBe("node_modules"); }); test("should handle empty approved folders", () => { const mockArgs = ["--ignored-folders", "node_modules"]; const approvedFolders: string[] = []; for (let i = 0; i < mockArgs.length; i++) { if (mockArgs[i] === "--approved-folders" && i + 1 < mockArgs.length) { approvedFolders.push( ...mockArgs[i + 1].split(",").map((f) => f.trim()) ); i++; } } expect(approvedFolders).toHaveLength(0); }); test("should handle multiple comma-separated directories", () => { const mockArgs = [ "--approved-folders", `/path/to/dir1,/path/to/dir2,/path/to/dir3`, ]; const approvedFolders: string[] = []; for (let i = 0; i < mockArgs.length; i++) { if (mockArgs[i] === "--approved-folders" && i + 1 < mockArgs.length) { approvedFolders.push( ...mockArgs[i + 1].split(",").map((f) => f.trim()) ); i++; } } expect(approvedFolders).toHaveLength(3); expect(approvedFolders[0]).toBe("/path/to/dir1"); expect(approvedFolders[1]).toBe("/path/to/dir2"); expect(approvedFolders[2]).toBe("/path/to/dir3"); }); test("should trim whitespace from directory paths", () => { const mockArgs = [ "--approved-folders", `/path/to/dir1 , /path/to/dir2 , /path/to/dir3`, ]; const approvedFolders: string[] = []; for (let i = 0; i < mockArgs.length; i++) { if (mockArgs[i] === "--approved-folders" && i + 1 < mockArgs.length) { approvedFolders.push( ...mockArgs[i + 1].split(",").map((f) => f.trim()) ); i++; } } expect(approvedFolders).toHaveLength(3); expect(approvedFolders[0]).toBe("/path/to/dir1"); expect(approvedFolders[1]).toBe("/path/to/dir2"); expect(approvedFolders[2]).toBe("/path/to/dir3"); }); test("should filter out empty strings from approved folders", () => { const mockArgs = ["--approved-folders", `/path/to/dir1,,/path/to/dir2`]; const approvedFolders: string[] = []; for (let i = 0; i < mockArgs.length; i++) { if (mockArgs[i] === "--approved-folders" && i + 1 < mockArgs.length) { const parsed = mockArgs[i + 1] .split(",") .map((f) => f.trim()) .filter((f) => f.length > 0); approvedFolders.push(...parsed); i++; } } expect(approvedFolders).toHaveLength(2); expect(approvedFolders[0]).toBe("/path/to/dir1"); expect(approvedFolders[1]).toBe("/path/to/dir2"); }); test("should validate that server description generator handles empty directories", () => { // Test the description generation logic const generateDescription = (currentDirs: string[]): string => { const baseDescription = "A configurable Model Context Protocol server for secure filesystem operations that absolutely rocks. " + "Enables AI assistants to dynamically access and manage file system resources with runtime directory registration and selective tool activation."; if (currentDirs.length === 0) { return ( baseDescription + "\n\nNO DIRECTORIES CURRENTLY ACCESSIBLE. Use register_directory tool to grant access, or restart server with --approved-folders argument." ); } const dirList = currentDirs.map((dir) => ` - ${dir}`).join("\n"); return `${baseDescription}\n\nIMMEDIATELY ACCESSIBLE DIRECTORIES (pre-approved, no registration needed):\n${dirList}\n\nIMPORTANT: These directories are already accessible to all filesystem tools. Do NOT use register_directory for these paths.\n\nTo add additional directories at runtime, use the register_directory tool or MCP Roots protocol.`; }; const emptyDescription = generateDescription([]); expect(emptyDescription).toContain("NO DIRECTORIES CURRENTLY ACCESSIBLE"); expect(emptyDescription).toContain("--approved-folders"); }); test("should validate that server description generator lists approved directories", () => { const generateDescription = (currentDirs: string[]): string => { const baseDescription = "A configurable Model Context Protocol server for secure filesystem operations that absolutely rocks. " + "Enables AI assistants to dynamically access and manage file system resources with runtime directory registration and selective tool activation."; if (currentDirs.length === 0) { return ( baseDescription + "\n\nNO DIRECTORIES CURRENTLY ACCESSIBLE. Use register_directory tool to grant access, or restart server with --approved-folders argument." ); } const dirList = currentDirs.map((dir) => ` - ${dir}`).join("\n"); return `${baseDescription}\n\nIMMEDIATELY ACCESSIBLE DIRECTORIES (pre-approved, no registration needed):\n${dirList}\n\nIMPORTANT: These directories are already accessible to all filesystem tools. Do NOT use register_directory for these paths.\n\nTo add additional directories at runtime, use the register_directory tool or MCP Roots protocol.`; }; const description = generateDescription([testDir1, testDir2]); expect(description).toContain("IMMEDIATELY ACCESSIBLE DIRECTORIES"); expect(description).toContain("no registration needed"); expect(description).toContain(` - ${testDir1}`); expect(description).toContain(` - ${testDir2}`); expect(description).toContain( "Do NOT use register_directory for these paths" ); expect(description).toContain("register_directory tool"); expect(description).toContain("MCP Roots protocol"); }); test("should validate directory existence checking logic", async () => { // Test that valid directories pass validation const stats1 = await fs.stat(testDir1); expect(stats1.isDirectory()).toBe(true); const stats2 = await fs.stat(testDir2); expect(stats2.isDirectory()).toBe(true); // Test that files fail validation const fileStats = await fs.stat(testFile1); expect(fileStats.isDirectory()).toBe(false); }); test("should validate that non-existent paths are rejected", async () => { const nonExistentPath = path.join(tmpdir(), `non-existent-${Date.now()}`); // Should throw error when trying to stat non-existent path await expect(fs.stat(nonExistentPath)).rejects.toThrow(); }); test("should validate path normalization for approved folders", async () => { // Test that paths are normalized correctly const normalized1 = path.resolve(testDir1); const normalized2 = path.resolve(testDir2); expect(path.isAbsolute(normalized1)).toBe(true); expect(path.isAbsolute(normalized2)).toBe(true); // Verify normalized paths are accessible const stats1 = await fs.stat(normalized1); const stats2 = await fs.stat(normalized2); expect(stats1.isDirectory()).toBe(true); expect(stats2.isDirectory()).toBe(true); }); test("should validate merging with legacy directories", () => { // Test the logic for merging approved folders with legacy positional args const approvedFolders = ["/path/to/approved1", "/path/to/approved2"]; const legacyDirs = ["/path/to/legacy1", "/path/to/approved1"]; // One duplicate const allowedDirectories = [...approvedFolders]; // Merge logic: add legacy dirs that aren't already in allowedDirectories for (const dir of legacyDirs) { if (!allowedDirectories.includes(dir)) { allowedDirectories.push(dir); } } expect(allowedDirectories).toHaveLength(3); expect(allowedDirectories).toContain("/path/to/approved1"); expect(allowedDirectories).toContain("/path/to/approved2"); expect(allowedDirectories).toContain("/path/to/legacy1"); // Verify no duplicates const uniqueDirs = new Set(allowedDirectories); expect(uniqueDirs.size).toBe(allowedDirectories.length); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/n0zer0d4y/vulcan-file-ops'

If you have feedback or need assistance with the MCP directory API, please join our Discord server