Skip to main content
Glama

find

Locate files in your filesystem using glob patterns like **/*.ts, returning matches with metadata for efficient file management.

Instructions

Find files by glob pattern (e.g. **/*.ts). Returns matching files with metadata. For content search, use grep. For bulk edits, pass the same glob to search_and_replace.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathNoBase directory (default: root). Absolute path required if multiple roots.
patternYesGlob pattern (e.g. "**/*.ts", "src/*.js")
maxResultsNoMax results (1-10000). Default: 100
includeIgnoredNoInclude ignored items (node_modules, etc).
includeHiddenNoInclude hidden items (starting with .)
sortByNoSort by path, name, size, or modifiedpath
maxDepthNoMaximum directory depth to scan
cursorNoPagination cursor from a previous response

Implementation Reference

  • Core logic for the 'find' tool that searches files based on a pattern.
    async function handleSearchFiles(
      args: z.infer<typeof SearchFilesInputSchema>,
      signal?: AbortSignal,
      onProgress?: (progress: { total?: number; current: number }) => void
    ): Promise<ToolResponse<z.infer<typeof SearchFilesOutputSchema>>> {
      const basePath = resolvePathOrRoot(args.path);
      const excludePatterns = args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS;
      const cursorOffset =
        args.cursor !== undefined ? decodeOffsetCursor(args.cursor) : 0;
      const pageSize = args.maxResults;
      const fetchMax = cursorOffset + pageSize;
      const searchOptions: Parameters<typeof searchFiles>[3] = {
        maxResults: fetchMax,
        includeHidden: args.includeHidden,
        sortBy: args.sortBy,
        respectGitignore: !args.includeIgnored,
        ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),
        ...(onProgress ? { onProgress } : {}),
        ...(signal ? { signal } : {}),
      };
      const result = await searchFiles(
        basePath,
        args.pattern,
        excludePatterns,
        searchOptions
      );
      const allResults = result.results;
      const displayResults =
        cursorOffset > 0 ? allResults.slice(cursorOffset) : allResults;
      const nextCursor =
        result.summary.truncated && displayResults.length > 0
          ? encodeOffsetCursor(cursorOffset + displayResults.length)
          : undefined;
      const relativeResults: z.infer<typeof SearchFilesOutputSchema>['results'] =
        [];
      for (const entry of displayResults) {
        relativeResults.push({
          path: path.relative(result.basePath, entry.path),
          size: entry.size,
          modified: entry.modified?.toISOString(),
        });
      }
      const structured: z.infer<typeof SearchFilesOutputSchema> = {
        ok: true,
        root: basePath,
        results: relativeResults,
        totalMatches: result.summary.matched,
        filesScanned: result.summary.filesScanned,
        ...(result.summary.truncated
          ? { truncated: result.summary.truncated }
          : {}),
        ...(result.summary.skippedInaccessible
          ? { skippedInaccessible: result.summary.skippedInaccessible }
          : {}),
        ...(result.summary.stoppedReason
          ? { stoppedReason: result.summary.stoppedReason }
          : {}),
        ...(nextCursor !== undefined ? { nextCursor } : {}),
      };
    
      let truncatedReason: string | undefined;
      if (result.summary.truncated) {
        if (result.summary.stoppedReason === 'timeout') {
          truncatedReason = 'timeout';
        } else if (result.summary.stoppedReason === 'maxFiles') {
          truncatedReason = `max files (${result.summary.filesScanned})`;
        } else {
          truncatedReason = `max results (${result.summary.matched})`;
        }
      }
    
      const summaryOptions: Parameters<typeof formatOperationSummary>[0] = {
        truncated: result.summary.truncated,
        ...(truncatedReason ? { truncatedReason } : {}),
      };
    
      const textLines: string[] = [];
      if (relativeResults.length === 0) {
        textLines.push('No matches');
      } else {
        textLines.push(`Found ${relativeResults.length}:`);
        for (const entry of relativeResults) {
          textLines.push(`  ${entry.path}`);
        }
      }
    
      let text = joinLines(textLines) + formatOperationSummary(summaryOptions);
      if (nextCursor) {
        text += `\n[Next page available. Use cursor: "${nextCursor}"]`;
      }
      return buildToolResponse(text, structured);
    }
  • Registration function for the 'find' tool in the MCP server.
    export function registerSearchFilesTool(
      server: McpServer,
      options: ToolRegistrationOptions = {}
    ): void {
      const handler = (
        args: z.infer<typeof SearchFilesInputSchema>,
        extra: ToolExtra
      ): Promise<ToolResult<z.infer<typeof SearchFilesOutputSchema>>> =>
        executeToolWithDiagnostics({
          toolName: 'find',
          extra,
          outputSchema: SearchFilesOutputSchema,
          timedSignal: { timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS },
          context: { path: args.path ?? '.' },
          run: async (signal) => {
            const rawScopeLabel = args.path ? path.basename(args.path) : '.';
            const scopeLabel = rawScopeLabel || '.';
            const { pattern } = args;
            const truncatedPattern = truncateProgressPattern(pattern);
            const context = `${truncatedPattern} in ${scopeLabel}`;
            let progressCursor = 0;
            notifyProgress(extra, {
              current: 0,
              message: `🔎︎ find: ${truncatedPattern}`,
            });
    
            const baseReporter = createProgressReporter(extra);
            const progressWithMessage = ({
              current,
              total,
            }: {
              total?: number;
              current: number;
            }): void => {
              if (current > progressCursor) progressCursor = current;
              baseReporter({
                current,
                ...(total !== undefined ? { total } : {}),
                message: `🔎︎ find: ${truncatedPattern} [${current} files]`,
              });
            };
    
            try {
              const result = await handleSearchFiles(
                args,
                signal,
                progressWithMessage
              );
              const sc = result.structuredContent;
              const { totalMatches = 0, stoppedReason } = sc;
    
              let suffix: string;
              if (totalMatches === 0) {
                suffix = 'No matches';
              } else {
                suffix = `${totalMatches} ${totalMatches === 1 ? 'match' : 'matches'}`;
                if (stoppedReason === 'timeout') {
                  suffix += ' [timeout]';
                } else if (stoppedReason === 'maxResults') {
                  suffix += ' [max results]';
                } else if (stoppedReason === 'maxFiles') {
                  suffix += ' [max files]';
                }
              }
    
              const finalCurrent = Math.max(
                (sc.filesScanned ?? 0) + 1,
                progressCursor + 1
              );
              notifyProgress(extra, {
                current: finalCurrent,
                total: finalCurrent,
                message: `🔎︎ find: ${context} • ${suffix}`,
              });
              return result;
            } catch (error) {
              const finalCurrent = Math.max(progressCursor + 1, 1);
              notifyProgress(extra, {
                current: finalCurrent,
                total: finalCurrent,
                message: `🔎︎ find: ${context} • failed`,
              });
              throw error;
            }
          },
          onError: (error) =>
            buildToolErrorResponse(error, ErrorCode.E_UNKNOWN, args.path),
        });
    
      const { isInitialized } = options;
    
      const wrappedHandler = wrapToolHandler(handler, {
        guard: isInitialized,
      });
    
      const validatedHandler = withValidatedArgs(
        SearchFilesInputSchema,
        wrappedHandler
      );
    
      if (
        registerToolTaskIfAvailable(
          server,
          'find',
          SEARCH_FILES_TOOL,
          validatedHandler,
          options.iconInfo,
          isInitialized
        )
      )
        return;
      server.registerTool(
        'find',
        withDefaultIcons({ ...SEARCH_FILES_TOOL }, options.iconInfo),
        validatedHandler
      );
    }
  • Tool definition and schema association for the 'find' tool.
    export const SEARCH_FILES_TOOL: ToolContract = {
      name: 'find',
      title: 'Find Files',
      description:
        'Find files by glob pattern (e.g. `**/*.ts`). Returns matching files with metadata. ' +
        'For content search, use `grep`. For bulk edits, pass the same glob to `search_and_replace`.',
      inputSchema: SearchFilesInputSchema,
      outputSchema: SearchFilesOutputSchema,
      annotations: READ_ONLY_TOOL_ANNOTATIONS,
      nuances: ['Respects `.gitignore` unless `includeIgnored=true`.'],
      taskSupport: 'optional',
    } as const;

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/j0hanz/filesystem-mcp'

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