Skip to main content
Glama
filesystem.ts8.59 kB
import { exec } from "child_process"; import { promisify } from "util"; import * as fs from "fs/promises"; import * as path from "path"; const execAsync = promisify(exec); export const fileSystemTool = { name: "filesystem", description: "Comprehensive file system operations including directory browsing, file reading, searching, and basic file operations", parameters: { type: "object", properties: { action: { type: "string", enum: ["list_directory", "read_file", "search_files", "get_file_info", "find_large_files", "get_disk_usage"], description: "The file system action to perform" }, path: { type: "string", description: "File or directory path (required for most actions)" }, pattern: { type: "string", description: "Search pattern for file searching (supports wildcards)" }, recursive: { type: "boolean", description: "Whether to search recursively (default: false)", default: false }, max_depth: { type: "number", description: "Maximum depth for recursive operations (default: 3)", default: 3 }, size_threshold: { type: "number", description: "Size threshold in MB for finding large files (default: 100)", default: 100 } }, required: ["action"] }, async run(args: { action: string; path?: string; pattern?: string; recursive?: boolean; max_depth?: number; size_threshold?: number; }) { try { switch (args.action) { case "list_directory": return await this.listDirectory(args.path || "C:\\", args.recursive, args.max_depth); case "read_file": return await this.readFile(args.path!); case "search_files": return await this.searchFiles(args.pattern!, args.path, args.recursive); case "get_file_info": return await this.getFileInfo(args.path!); case "find_large_files": return await this.findLargeFiles(args.path || "C:\\", args.size_threshold!); case "get_disk_usage": return await this.getDiskUsage(); default: throw new Error(`Unknown action: ${args.action}`); } } catch (error: any) { return { content: [{ type: "text", text: `❌ File system operation failed: ${error.message}` }], isError: true }; } }, async listDirectory(dirPath: string, recursive = false, maxDepth = 3) { try { const items = await fs.readdir(dirPath, { withFileTypes: true }); let result = `# Directory Listing: ${dirPath}\n\n`; const directories: string[] = []; const files: string[] = []; for (const item of items) { const fullPath = path.join(dirPath, item.name); try { const stats = await fs.stat(fullPath); const size = item.isFile() ? this.formatFileSize(stats.size) : ""; const modified = stats.mtime.toISOString().split('T')[0]; if (item.isDirectory()) { directories.push(`📁 ${item.name}/ (${modified})`); } else { files.push(`📄 ${item.name} (${size}, ${modified})`); } } catch { if (item.isDirectory()) { directories.push(`📁 ${item.name}/ (access denied)`); } else { files.push(`📄 ${item.name} (access denied)`); } } } result += "## Directories:\n" + directories.join("\n") + "\n\n"; result += "## Files:\n" + files.join("\n"); if (recursive && maxDepth > 0) { result += "\n\n## Subdirectories (recursive):\n"; for (const item of items) { if (item.isDirectory()) { try { const subPath = path.join(dirPath, item.name); const subResult = await this.listDirectory(subPath, true, maxDepth - 1); result += `\n### ${item.name}/\n${subResult.content[0].text}\n`; } catch { result += `\n### ${item.name}/ (access denied)\n`; } } } } return { content: [{ type: "text", text: result }] }; } catch (error: any) { throw new Error(`Cannot list directory ${dirPath}: ${error.message}`); } }, async readFile(filePath: string) { try { const stats = await fs.stat(filePath); if (stats.isDirectory()) { throw new Error("Path is a directory, not a file"); } if (stats.size > 1024 * 1024) { // 1MB limit throw new Error("File too large (>1MB). Use file info to check size first."); } const content = await fs.readFile(filePath, 'utf-8'); return { content: [{ type: "text", text: `# File Content: ${filePath}\n\n\`\`\`\n${content}\n\`\`\`` }] }; } catch (error: any) { throw new Error(`Cannot read file ${filePath}: ${error.message}`); } }, async searchFiles(pattern: string, searchPath = "C:\\", recursive = false) { try { const command = recursive ? `Get-ChildItem -Path "${searchPath}" -Filter "${pattern}" -Recurse -ErrorAction SilentlyContinue | Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize` : `Get-ChildItem -Path "${searchPath}" -Filter "${pattern}" -ErrorAction SilentlyContinue | Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize`; const { stdout } = await execAsync(`powershell -Command "${command}"`); return { content: [{ type: "text", text: `# File Search Results\n\nPattern: ${pattern}\nPath: ${searchPath}\nRecursive: ${recursive}\n\n\`\`\`\n${stdout}\n\`\`\`` }] }; } catch (error: any) { throw new Error(`File search failed: ${error.message}`); } }, async getFileInfo(filePath: string) { try { const stats = await fs.stat(filePath); const result = `# File Information: ${filePath}\n\n` + `- **Type**: ${stats.isDirectory() ? 'Directory' : 'File'}\n` + `- **Size**: ${this.formatFileSize(stats.size)}\n` + `- **Created**: ${stats.birthtime.toISOString()}\n` + `- **Modified**: ${stats.mtime.toISOString()}\n` + `- **Accessed**: ${stats.atime.toISOString()}\n` + `- **Permissions**: ${stats.mode.toString(8)}`; return { content: [{ type: "text", text: result }] }; } catch (error: any) { throw new Error(`Cannot get file info for ${filePath}: ${error.message}`); } }, async findLargeFiles(searchPath: string, sizeThresholdMB: number) { try { const command = `Get-ChildItem -Path "${searchPath}" -Recurse -File -ErrorAction SilentlyContinue | Where-Object {$_.Length -gt ${sizeThresholdMB * 1024 * 1024}} | Sort-Object Length -Descending | Select-Object FullName, @{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}}, LastWriteTime | Format-Table -AutoSize`; const { stdout } = await execAsync(`powershell -Command "${command}"`); return { content: [{ type: "text", text: `# Large Files (>${sizeThresholdMB}MB)\n\nSearch Path: ${searchPath}\n\n\`\`\`\n${stdout}\n\`\`\`` }] }; } catch (error: any) { throw new Error(`Large file search failed: ${error.message}`); } }, async getDiskUsage() { try { const command = `Get-WmiObject -Class Win32_LogicalDisk | Select-Object DeviceID, @{Name="SizeGB";Expression={[math]::Round($_.Size/1GB,2)}}, @{Name="FreeSpaceGB";Expression={[math]::Round($_.FreeSpace/1GB,2)}}, @{Name="UsedSpaceGB";Expression={[math]::Round(($_.Size-$_.FreeSpace)/1GB,2)}}, @{Name="PercentFree";Expression={[math]::Round(($_.FreeSpace/$_.Size)*100,2)}} | Format-Table -AutoSize`; const { stdout } = await execAsync(`powershell -Command "${command}"`); return { content: [{ type: "text", text: `# Disk Usage Information\n\n\`\`\`\n${stdout}\n\`\`\`` }] }; } catch (error: any) { throw new Error(`Disk usage query failed: ${error.message}`); } }, formatFileSize(bytes: number): string { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } };

Latest Blog Posts

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/guangxiangdebizi/windows-system-mcp'

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