Skip to main content
Glama
count-files.ts5.65 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import fs from 'fs-extra'; import * as path from 'path'; import * as os from 'os'; interface FileDetails { path: string; type: 'file' | 'directory'; size: number; // in bytes modified_date: Date; // Use Date object for easier comparison } // Helper function for recursive listing and filtering (reused from list-files.ts for consistency) async function listFilesRecursiveForCount(dir: string, params: { includeHidden?: boolean; pattern?: string; recursive?: boolean; max_depth?: number; min_size_kb?: number; max_size_kb?: number; modified_since_days?: number; count_files?: boolean; // Specific to count-files, passed as include_files count_folders?: boolean; // Specific to count-files, passed as include_folders }, currentDepth = 0): Promise<FileDetails[]> { let results: FileDetails[] = []; let items: fs.Dirent[] = []; try { items = await fs.readdir(dir, { withFileTypes: true }); } catch (err) { console.warn(`Could not read directory ${dir}: ${err}`); return []; } for (const item of items) { const itemPath = path.join(dir, item.name); if (!params.includeHidden && item.name.startsWith('.')) continue; if (params.pattern && !new RegExp(params.pattern).test(item.name)) continue; try { const stats = await fs.stat(itemPath); const modifiedDate = stats.mtime; if (params.modified_since_days !== undefined) { const cutoffDate = new Date(Date.now() - params.modified_since_days * 86400000); if (modifiedDate < cutoffDate) continue; } if (item.isDirectory()) { if (params.count_folders) { // Use count_folders directly results.push({ path: itemPath, type: 'directory', size: stats.size, modified_date: modifiedDate, }); } if (params.recursive && (params.max_depth === undefined || currentDepth < params.max_depth)) { results = results.concat(await listFilesRecursiveForCount(itemPath, params, currentDepth + 1)); } } else if (item.isFile()) { if (params.min_size_kb !== undefined && stats.size < params.min_size_kb * 1024) continue; if (params.max_size_kb !== undefined && stats.size > params.max_size_kb * 1024) continue; if (params.count_files) { // Use count_files directly results.push({ path: itemPath, type: 'file', size: stats.size, modified_date: modifiedDate, }); } } } catch (err) { console.warn(`Could not stat item ${itemPath}: ${err}`); } } return results; } export default function registerCountFilesTool(server: McpServer) { server.registerTool( 'count-files', { description: 'Counts files and/or directories in a path with advanced filtering options.', inputSchema: { folderPath: z.string().optional().describe("The directory to analyze. Defaults to the user's desktop."), count_files: z.boolean().optional().default(true).describe("Whether to include files in the count."), count_folders: z.boolean().optional().default(false).describe("Whether to include folders in the count."), recursive: z.boolean().optional().default(false).describe("Count items in subdirectories recursively."), pattern: z.string().optional().describe("A regex pattern to filter items by name."), max_depth: z.number().int().min(0).optional().describe("Maximum recursion depth."), min_size_kb: z.number().int().min(0).optional().describe("Minimum file size in kilobytes."), max_size_kb: z.number().int().min(0).optional().describe("Maximum file size in kilobytes."), modified_since_days: z.number().int().min(0).optional().describe("Filter items modified within the last N days."), } }, async (params: { folderPath?: string; count_files?: boolean; count_folders?: boolean; recursive?: boolean; pattern?: string; max_depth?: number; min_size_kb?: number; max_size_kb?: number; modified_since_days?: number; }) => { try { const targetPath = params.folderPath || path.join(os.homedir(), 'Desktop'); if (!(await fs.pathExists(targetPath))) { return { content: [{ type: 'text', text: `Error: Path does not exist: ${targetPath}` }], isError: true }; } if (!params.count_files && !params.count_folders) { return { content: [{ type: 'text', text: "Error: You must choose to count files, folders, or both." }], isError: true }; } // Pass all relevant params to the recursive function const items = await listFilesRecursiveForCount(targetPath, params); const fileCount = items.filter(i => i.type === 'file').length; const folderCount = items.filter(i => i.type === 'directory').length; let resultText = `Analysis of '${targetPath}':\\n`; if (params.count_files) resultText += `- Files found: ${fileCount}\\n`; if (params.count_folders) resultText += `- Folders found: ${folderCount}\\n`; return { content: [{ type: 'text', text: resultText.trim() }] }; } catch (error: any) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text', text: `An unexpected error occurred: ${errorMessage}` }], isError: true }; } } ); }

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/Yussefgafer/MyMCP'

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