Skip to main content
Glama

ABSD DevOps MCP Server

by anthonybir
filesystem.test.ts10.8 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { SecurityValidator } from '../../src/security/validator.js'; import { createLogger } from '../../src/utils/logger.js'; import { tmpdir } from 'node:os'; import { mkdirSync, rmSync, existsSync, writeFileSync, readFileSync, realpathSync } from 'node:fs'; import { join } from 'node:path'; import type { Config } from '../../src/types/config.js'; // Import tools import { readFileTool } from '../../src/tools/filesystem/read.js'; import { writeFileTool } from '../../src/tools/filesystem/write.js'; import { listDirectoryTool } from '../../src/tools/filesystem/list.js'; import { createDirectoryTool } from '../../src/tools/filesystem/create.js'; import { getFileInfoTool } from '../../src/tools/filesystem/info.js'; describe('Filesystem Tools Integration', () => { let validator: SecurityValidator; let logger: ReturnType<typeof createLogger>; let testDir: string; let config: Config; beforeEach(() => { // Create temp test directory testDir = join(tmpdir(), `absd-mcp-integration-${Date.now()}`); mkdirSync(testDir, { recursive: true }); // Resolve to real path (important for macOS where /tmp is symlink) testDir = realpathSync(testDir); config = { allowedDirectories: [testDir], blockedCommands: [], fileReadLineLimit: 1000, fileWriteLineLimit: 50, sessionTimeout: 30000, logLevel: 'error', // Suppress logs during tests }; validator = new SecurityValidator(config); logger = createLogger(config); }); afterEach(() => { // Cleanup try { rmSync(testDir, { recursive: true, force: true }); } catch { // Ignore cleanup errors } }); describe('read_file and write_file integration', () => { it('should write and then read a file successfully', async () => { const testPath = join(testDir, 'test.txt'); const testContent = 'Hello, ABSD MCP!'; // Write file const writeResult = await writeFileTool( { path: testPath, content: testContent, mode: 'rewrite' }, validator, logger, config ); expect(writeResult.content[0].text).toContain('wrote'); // Read file const readResult = await readFileTool( { path: testPath, offset: 0 }, validator, logger, config ); expect(readResult.content[0].text).toBe(testContent); }); it('should append to existing file', async () => { const testPath = join(testDir, 'append-test.txt'); // Write initial content await writeFileTool( { path: testPath, content: 'Line 1\n', mode: 'rewrite' }, validator, logger, config ); // Append content await writeFileTool( { path: testPath, content: 'Line 2\n', mode: 'append' }, validator, logger, config ); // Read full file const readResult = await readFileTool( { path: testPath, offset: 0 }, validator, logger, config ); expect(readResult.content[0].text).toBe('Line 1\nLine 2\n'); }); it('should handle chunked reads with offset', async () => { const testPath = join(testDir, 'chunked.txt'); const lines = Array.from({ length: 10 }, (_, i) => `Line ${i + 1}`).join('\n'); await writeFileTool( { path: testPath, content: lines, mode: 'rewrite' }, validator, logger, config ); // Read with offset const readResult = await readFileTool( { path: testPath, offset: 5, length: 3 }, validator, logger, config ); expect(readResult.content[0].text).toContain('Line 6'); expect(readResult.content[0].text).toContain('Line 7'); expect(readResult.content[0].text).toContain('Line 8'); expect(readResult.content[0].text).not.toContain('Line 9'); }); it('should handle negative offset (tail behavior)', async () => { const testPath = join(testDir, 'tail.txt'); const lines = Array.from({ length: 10 }, (_, i) => `Line ${i + 1}`).join('\n'); await writeFileTool( { path: testPath, content: lines, mode: 'rewrite' }, validator, logger, config ); // Read last 3 lines const readResult = await readFileTool( { path: testPath, offset: -3 }, validator, logger, config ); expect(readResult.content[0].text).toContain('Line 8'); expect(readResult.content[0].text).toContain('Line 9'); expect(readResult.content[0].text).toContain('Line 10'); }); }); describe('create_directory and list_directory integration', () => { it('should create directory and list its contents', async () => { const newDir = join(testDir, 'new-folder'); // Create directory const createResult = await createDirectoryTool( { path: newDir, recursive: true }, validator, logger ); expect(createResult.content[0].text).toContain('created'); expect(existsSync(newDir)).toBe(true); // Create some files in it writeFileSync(join(newDir, 'file1.txt'), 'content1'); writeFileSync(join(newDir, 'file2.txt'), 'content2'); mkdirSync(join(newDir, 'subfolder')); // List directory const listResult = await listDirectoryTool( { path: newDir, recursive: false }, validator, logger ); const listing = listResult.content[0].text; expect(listing).toContain('[FILE] file1.txt'); expect(listing).toContain('[FILE] file2.txt'); expect(listing).toContain('[DIR] subfolder'); }); it('should create nested directories recursively', async () => { const nestedPath = join(testDir, 'level1', 'level2', 'level3'); const createResult = await createDirectoryTool( { path: nestedPath, recursive: true }, validator, logger ); expect(createResult.content[0].text).toContain('created'); expect(existsSync(nestedPath)).toBe(true); }); it('should list directories recursively', async () => { const rootDir = join(testDir, 'recursive-test'); mkdirSync(rootDir); mkdirSync(join(rootDir, 'subdir1')); mkdirSync(join(rootDir, 'subdir2')); writeFileSync(join(rootDir, 'file1.txt'), 'content'); writeFileSync(join(rootDir, 'subdir1', 'file2.txt'), 'content'); const listResult = await listDirectoryTool( { path: rootDir, recursive: true, maxDepth: 2 }, validator, logger ); const listing = listResult.content[0].text; expect(listing).toContain('[FILE] file1.txt'); expect(listing).toContain('[DIR] subdir1'); expect(listing).toContain('[FILE] subdir1/file2.txt'); }); }); describe('get_file_info integration', () => { it('should return file metadata', async () => { const testPath = join(testDir, 'info-test.txt'); const content = 'Line 1\nLine 2\nLine 3'; writeFileSync(testPath, content); const infoResult = await getFileInfoTool( { path: testPath }, validator, logger ); const info = infoResult.content[0].text; expect(info).toContain('Type: File'); expect(info).toContain('Size:'); expect(info).toContain('Created:'); expect(info).toContain('Modified:'); expect(info).toContain('Lines: 3'); expect(info).toContain('Last Line (0-indexed): 2'); }); it('should return directory metadata', async () => { const dirPath = join(testDir, 'info-dir'); mkdirSync(dirPath); const infoResult = await getFileInfoTool( { path: dirPath }, validator, logger ); const info = infoResult.content[0].text; expect(info).toContain('Type: Directory'); }); }); describe('error handling', () => { it('should reject paths outside allowed directories', async () => { const outsidePath = '/etc/passwd'; const readResult = await readFileTool( { path: outsidePath, offset: 0 }, validator, logger, config ); expect(readResult.content[0].text).toContain('Error'); expect(readResult.content[0].text).toContain('fuera de directorios permitidos'); }); it('should handle non-existent file reads gracefully', async () => { const nonExistent = join(testDir, 'does-not-exist.txt'); const readResult = await readFileTool( { path: nonExistent, offset: 0 }, validator, logger, config ); expect(readResult.content[0].text).toContain('Error'); expect(readResult.content[0].text).toContain('not found'); }); it('should handle listing non-existent directory', async () => { const nonExistent = join(testDir, 'no-such-dir'); const listResult = await listDirectoryTool( { path: nonExistent, recursive: false }, validator, logger ); expect(listResult.content[0].text).toContain('Error'); }); }); describe('end-to-end workflow', () => { it('should support complete file operation workflow', async () => { const projectDir = join(testDir, 'my-project'); const srcDir = join(projectDir, 'src'); const indexFile = join(srcDir, 'index.ts'); // 1. Create project structure await createDirectoryTool( { path: srcDir, recursive: true }, validator, logger ); // 2. Write source file await writeFileTool( { path: indexFile, content: 'export function hello() {\n return "Hello";\n}', mode: 'rewrite', }, validator, logger, config ); // 3. Get file info const infoResult = await getFileInfoTool( { path: indexFile }, validator, logger ); expect(infoResult.content[0].text).toContain('Lines: 3'); // 4. Read file back const readResult = await readFileTool( { path: indexFile, offset: 0 }, validator, logger, config ); expect(readResult.content[0].text).toContain('export function hello'); // 5. Append to file await writeFileTool( { path: indexFile, content: '\nexport function goodbye() {\n return "Goodbye";\n}', mode: 'append', }, validator, logger, config ); // 6. List src directory directly const listResult = await listDirectoryTool( { path: srcDir, recursive: false }, validator, logger ); expect(listResult.content[0].text).toContain('index.ts'); }); }); });

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/anthonybir/ABSD_MCP'

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