Skip to content

FileSystemTools

A comprehensive file manipulation toolkit providing read, write, and edit operations for working with files in the local filesystem.

Features: - Read files with line range support and pagination - Write new files or overwrite existing ones - Precise string replacement editing - Line number formatting for easy reference - Long line truncation (2000 chars max) - Automatic parent directory creation - UTF-8 encoding support - Replace all or single occurrence editing - Optional allowedDirectory to restrict all file operations to one or more configured paths

Builder Configuration

allowedDirectory / allowedDirectories — Restrict File Operations to a Directory

By default FileSystemTools can access any path on the filesystem. Configure one or more allowed directories to restrict all read, write, and edit operations to paths within them. Access to any path outside all allowed directories returns an error.

Usage:

// Single directory (Path or String)
FileSystemTools tools = FileSystemTools.builder()
    .allowedDirectory(Path.of("/workspace/project"))
    .build();

// Multiple directories — chained
FileSystemTools tools = FileSystemTools.builder()
    .allowedDirectory("/workspace/project")
    .allowedDirectory("/tmp/uploads")
    .build();

// Multiple directories — varargs
FileSystemTools tools = FileSystemTools.builder()
    .allowedDirectories(Path.of("/workspace/project"), Path.of("/tmp/uploads"))
    .build();

// No restriction (default — backward compatible)
FileSystemTools tools = FileSystemTools.builder().build();

Security guarantees:

Three layers block bypass attempts for each candidate directory:

  1. Path traversal — raw .. components are rejected before normalization, blocking /allowed/../etc/passwd-style attacks.
  2. Normalized containment — the resolved path must start with the allowed directory (component-aware, so /allowed-evil does not match /allowed).
  3. Symlink resolution — real paths of existing path components are verified, blocking symlinks that point outside the allowed directory. Dangling symlinks are always denied.

Error returned on denied access:

Error: Access denied. Path is outside the allowed directories: /etc/passwd

Notes: - When no directory is configured, all paths are allowed (backward compatible). - allowedDirectory(null) is a no-op — it does not add an entry. - Non-existent allowed directories are supported; writes into them are permitted (symlink check is skipped until the directory exists).

Available Tools

1. Read - Read File Contents

Reads a file from the local filesystem with optional line range support for handling large files.

Parameters: - filePath (required) - The absolute path to the file to read - offset (optional) - The line number to start reading from (1-indexed) - limit (optional) - The number of lines to read (default: 2000)

Basic Usage:

FileSystemTools fileTools = FileSystemTools.builder().build();

// Read entire file (up to 2000 lines)
String content = fileTools.read(
    "/path/to/file.txt",
    null,                    // offset (start from beginning)
    null                    // limit (read up to 2000 lines)
);

// Read specific line range
String content = fileTools.read(
    "/path/to/large-file.log",
    100,                     // Start from line 100
    50                      // Read 50 lines
);

// Read from line 500 onwards
String content = fileTools.read(
    "/path/to/file.java",
    500,                     // Start from line 500
    2000                    // Read up to 2000 lines
);

Output Format:

File: /path/to/file.txt
Showing lines 1-10 of 150

     1→First line of content
     2→Second line of content
     3→Third line of content
     ...
    10→Tenth line of content

Key Features: - Line numbers: Results formatted with cat -n style line numbers (right-aligned, 6 chars, arrow separator) - Line truncation: Lines longer than 2000 characters are truncated with "... (line truncated)" suffix - Pagination: Read large files in chunks using offset and limit - Empty file detection: Returns "File is empty" message for empty files - Error handling: Clear error messages for non-existent files or directories

Important Notes: - File path must be absolute, not relative - Default limit is 2000 lines - recommended to read full file when possible - Line numbers are 1-indexed (first line is line 1) - Cannot read directories (use Bash tool with ls command) - Supports reading various file types (text, images, PDFs, Jupyter notebooks with appropriate handling)

2. Write - Create or Overwrite Files

Writes content to a file, creating new files or overwriting existing ones.

Parameters: - filePath (required) - The absolute path to the file to write (must be absolute) - content (optional) - The content to write to the file (defaults to empty string if null)

Basic Usage:

// Create a new file
String result = fileTools.write(
    "/path/to/new-file.txt",
    "This is the file content\nWith multiple lines"
);
// Returns: "Successfully created file: /path/to/new-file.txt (45 bytes)"

// Create an empty file
String result = fileTools.write(
    "/path/to/empty-file.txt",
    null                    // content (creates empty file)
);
// Returns: "Successfully created file: /path/to/empty-file.txt (0 bytes)"

// Overwrite an existing file
String result = fileTools.write(
    "/path/to/existing-file.txt",
    "New content replacing old content"
);
// Returns: "Successfully overwrote file: /path/to/existing-file.txt (33 bytes)"

// Create file with parent directories
String result = fileTools.write(
    "/path/to/new/directory/file.txt",
    "Content"
);
// Automatically creates /path/to/new/directory/ if it doesn't exist

Important Notes: - MUST read first: If overwriting an existing file, you MUST use the Read tool first - Prefer Edit: ALWAYS prefer editing existing files instead of writing new ones - No emojis: Avoid writing emojis unless explicitly requested by the user - No proactive docs: Never create documentation files (.md, README) unless explicitly requested - Parent directories: Automatically creates parent directories if they don't exist - Complete replacement*: Overwrites the entire file content (does not append)

When to Use: - Creating new files explicitly requested by the user - Generating configuration files, scripts, or source files - Writing output from data transformations

When NOT to Use: - Modifying existing files (use Edit instead) - Making small changes to code (use Edit instead)

3. Edit - Precise String Replacement

Performs exact string replacements in files with safety checks to prevent unintended changes.

Parameters: - filePath (required) - The absolute path to the file to modify - old_string (required) - The exact text to replace - new_string (required) - The text to replace it with (must be different from old_string) - replace_all (optional) - Replace all occurrences (default: false)

Basic Usage:

// Single replacement (default)
String result = fileTools.edit(
    "/path/to/file.java",
    "public void oldMethod() {",       // old_string
    "public void newMethod() {",       // new_string
    null                               // replace_all (false)
);

// Replace all occurrences (useful for variable renaming)
String result = fileTools.edit(
    "/path/to/file.java",
    "oldVariableName",                 // old_string
    "newVariableName",                 // new_string
    true                              // replace_all
);

// Multi-line replacement
String result = fileTools.edit(
    "/path/to/config.yml",
    "database:\n  host: localhost\n  port: 5432",
    "database:\n  host: prod-server\n  port: 3306",
    null
);

Output Format:

The file /path/to/file.java has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
    15→    private static final String NAME = "newValue";
    16→
    17→    public void newMethod() {
    18→        // method implementation
    19→    }

Safety Features:

  1. Uniqueness Check: If old_string appears multiple times and replace_all=false, the edit fails with an error:

    Error: old_string appears 5 times in the file. Either provide a larger string
    with more surrounding context to make it unique or use replace_all=true to
    change all instances.
    

  2. Existence Check: Returns error if old_string is not found in the file

  3. Different Strings: Validates that old_string and new_string are different

  4. Must Read First: Should read the file first to see exact content and indentation

Indentation Handling:

When copying text from Read tool output, preserve exact indentation AFTER the line number prefix:

Read output:
    42→    public void method() {

Correct old_string:
"    public void method() {"

Incorrect old_string (includes line number):
"42→    public void method() {"

Best Practices:

  1. Read before editing: Always use Read tool first to see exact content

    String content = fileTools.read("/path/to/file.java", null, null);
    // Now edit based on what you see
    

  2. Include context for uniqueness: If replacement string appears multiple times, include surrounding lines

    // Instead of just "foo"
    fileTools.edit(filePath, "bar\nfoo\nbaz", "bar\nnewValue\nbaz", null);
    

  3. Use replace_all for renaming: When renaming variables or constants throughout a file

    fileTools.edit(filePath, "OLD_CONSTANT", "NEW_CONSTANT", true);
    

  4. Preserve indentation: Copy exact whitespace from Read output (spaces/tabs)

  5. Multi-line edits: Use \n for newlines in both old_string and new_string

Example Workflow:

FileSystemTools fileTools = FileSystemTools.builder().build();

// 1. Read the file to see current content
String content = fileTools.read(
    "/src/main/java/Example.java",
    null,
    null
);

// 2. Identify exact string to replace (preserving indentation)
String oldCode = """
    public void calculate() {
        return value * 2;
    }""";

String newCode = """
    public void calculate() {
        return value * 3;
    }""";

// 3. Perform the edit
String result = fileTools.edit(
    "/src/main/java/Example.java",
    oldCode,
    newCode,
    null
);

// Output shows snippet with context around the edit

Common Use Cases:

Task Configuration Example
Update single method replace_all=false Modify one method implementation
Rename variable replace_all=true Change oldName to newName everywhere
Update configuration replace_all=false Change one config value
Fix typos replace_all=true Fix misspelled word throughout file
Refactor imports replace_all=false Update specific import statement

Error Messages:

Error Meaning Solution
"File does not exist" File path is invalid Check file path is correct
"old_string not found" Text doesn't exist in file Verify exact string including whitespace
"appears N times" Multiple matches found Add more context or use replace_all=true
"must be different" old_string equals new_string Ensure you're actually changing the content