UNPKG

mcp-framework

Version:

Framework for building Model Context Protocol (MCP) servers in Typescript

84 lines (83 loc) 3.55 kB
import { promises as fs } from 'fs'; import { join } from 'path'; import { logger } from '../core/Logger.js'; /** * Recursively discovers files in a directory tree * @param rootPath - The root directory to start searching from * @param options - Configuration options for file discovery * @returns Array of file paths relative to the root path */ export async function discoverFilesRecursively(rootPath, options = {}) { const { extensions = ['.js'], excludePatterns = [], includeDirectories = false } = options; const discoveredFiles = []; async function traverseDirectory(currentPath, relativePath = '') { try { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = join(currentPath, entry.name); const relativeFilePath = relativePath ? join(relativePath, entry.name) : entry.name; if (entry.isDirectory()) { if (includeDirectories) { discoveredFiles.push(relativeFilePath); } // Recursively traverse subdirectories await traverseDirectory(fullPath, relativeFilePath); } else if (entry.isFile()) { // Check if file has valid extension const hasValidExtension = extensions.some((ext) => entry.name.endsWith(ext)); if (hasValidExtension) { // Check if file should be excluded const isExcluded = excludePatterns.some((pattern) => { if (pattern.includes('*')) { const regex = new RegExp(pattern.replace('*', '.*')); return regex.test(entry.name); } return entry.name === pattern; }); if (!isExcluded) { discoveredFiles.push(relativeFilePath); logger.debug(`Discovered file: ${relativeFilePath}`); } else { logger.debug(`Excluded file: ${relativeFilePath}`); } } } } } catch (error) { logger.warn(`Error reading directory ${currentPath}: ${error.message}`); } } try { const stats = await fs.stat(rootPath); if (!stats.isDirectory()) { logger.warn(`Path is not a directory: ${rootPath}`); return []; } await traverseDirectory(rootPath); logger.debug(`Discovered ${discoveredFiles.length} files in ${rootPath}`); return discoveredFiles; } catch (error) { logger.debug(`Directory not found: ${rootPath} - ${error.message}`); return []; } } /** * Checks if a directory exists and contains any valid files recursively * @param rootPath - The root directory to check * @param options - Configuration options for file discovery * @returns True if the directory contains valid files */ export async function hasValidFiles(rootPath, options = {}) { try { const files = await discoverFilesRecursively(rootPath, options); return files.length > 0; } catch (error) { logger.debug(`Error checking for valid files in ${rootPath}: ${error.message}`); return false; } }