Skip to content

ant_ai.tools.builtins.filesystem_tool

FilesystemTool pydantic-model

Bases: Tool

Tools for reading and writing files within the agent workspace.

Example
from ant_ai.agent import Agent
from ant_ai.tools.builtins.filesystem_tool import FilesystemTool

fs = FilesystemTool(workspace_root="/workspace")
agent = Agent(tools=[fs], ...)

The agent can now call:

- FilesystemTool_read_file(path="notes.txt")
- FilesystemTool_write_file(path="output.txt", content="hello")
- FilesystemTool_list_dir(path="src/")
- FilesystemTool_search(pattern="TODO", path="src/")
Notes

All paths are resolved relative to workspace_root and sandboxed within it. Attempts to escape the workspace (e.g. "../etc/passwd", "/absolute/path") are caught by _resolve and returned to the caller as an error string instead of raising — so the agent receives a recoverable error message rather than an exception.

Show JSON schema:
{
  "description": "Tools for reading and writing files within the agent workspace.\n\nExample:\n    ```python\n    from ant_ai.agent import Agent\n    from ant_ai.tools.builtins.filesystem_tool import FilesystemTool\n\n    fs = FilesystemTool(workspace_root=\"/workspace\")\n    agent = Agent(tools=[fs], ...)\n    ```\n\n    The agent can now call:\n\n        - FilesystemTool_read_file(path=\"notes.txt\")\n        - FilesystemTool_write_file(path=\"output.txt\", content=\"hello\")\n        - FilesystemTool_list_dir(path=\"src/\")\n        - FilesystemTool_search(pattern=\"TODO\", path=\"src/\")\n\nNotes:\n    All paths are resolved relative to `workspace_root` and sandboxed within it.\n    Attempts to escape the workspace (e.g. \"../etc/passwd\", \"/absolute/path\")\n    are caught by `_resolve` and returned to the caller as an error string\n    instead of raising \u2014 so the agent receives a recoverable error message\n    rather than an exception.",
  "properties": {
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Tool name.",
      "title": "Name"
    },
    "description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Tool description. Is used by the LLM to decide whether to call or not the specific tool.",
      "title": "Description"
    },
    "parameters": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "The parameters needed by the tool. This is a self-constructed field.",
      "title": "Parameters"
    },
    "workspace_root": {
      "default": ".",
      "format": "path",
      "title": "Workspace Root",
      "type": "string"
    }
  },
  "title": "FilesystemTool",
  "type": "object"
}

Fields:

  • name (str | None)
  • description (str | None)
  • parameters (dict[str, Any] | None)
  • __namespace_methods__ (list[str])
  • workspace_root (Path)

Validators:

  • _set_defaults
Source code in src/ant_ai/tools/builtins/filesystem_tool.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class FilesystemTool(Tool):
    """
    Tools for reading and writing files within the agent workspace.

    Example:
        ```python
        from ant_ai.agent import Agent
        from ant_ai.tools.builtins.filesystem_tool import FilesystemTool

        fs = FilesystemTool(workspace_root="/workspace")
        agent = Agent(tools=[fs], ...)
        ```

        The agent can now call:

            - FilesystemTool_read_file(path="notes.txt")
            - FilesystemTool_write_file(path="output.txt", content="hello")
            - FilesystemTool_list_dir(path="src/")
            - FilesystemTool_search(pattern="TODO", path="src/")

    Notes:
        All paths are resolved relative to `workspace_root` and sandboxed within it.
        Attempts to escape the workspace (e.g. "../etc/passwd", "/absolute/path")
        are caught by `_resolve` and returned to the caller as an error string
        instead of raising — so the agent receives a recoverable error message
        rather than an exception.
    """

    workspace_root: Path = Path()

    def __init__(self, workspace_root: str = "/workspace", **data):
        super().__init__(**data)
        self.workspace_root = Path(workspace_root).resolve()

    def read_file(self, path: str) -> str:
        """Read the full contents of a file. Path is relative to /workspace."""
        try:
            return self._resolve(path).read_text(encoding="utf-8")
        except ValueError as e:
            return f"Error: {e}"
        except FileNotFoundError:
            return f"Error: file not found: {path}"
        except Exception as e:
            return f"Error: {e}"

    def write_file(self, path: str, content: str) -> str:
        """Write content to a file, overwriting if it exists. Creates parent directories as needed. Path is relative to /workspace."""
        try:
            target: Path = self._resolve(path)
            target.parent.mkdir(parents=True, exist_ok=True)
            target.write_text(content, encoding="utf-8")
            return f"Written {len(content)} bytes to {path}"
        except ValueError as e:
            return f"Error: {e}"
        except Exception as e:
            return f"Error: {e}"

    def list_dir(self, path: str = "") -> list[str]:
        """List files and directories at the given path. Path is relative to /workspace. Defaults to workspace root."""
        try:
            target: Path = self._resolve(path) if path else self.workspace_root
            return [entry.name for entry in sorted(target.iterdir())]
        except ValueError as e:
            return [f"Error: {e}"]
        except FileNotFoundError:
            return [f"Error: directory not found: {path}"]
        except Exception as e:
            return [f"Error: {e}"]

    def search(self, pattern: str, path: str = "") -> str:
        """Search for a regex pattern recursively within path. Path is relative to /workspace. Returns matching lines in the format 'file:lineno: content'."""
        try:
            root: Path = self._resolve(path) if path else self.workspace_root
            regex: Pattern[str] = re.compile(pattern)
            results: list[str] = []
            for file in sorted(root.rglob("*")):
                if not file.is_file():
                    continue
                try:
                    for lineno, line in enumerate(
                        file.read_text(encoding="utf-8", errors="replace").splitlines(),
                        start=1,
                    ):
                        if regex.search(line):
                            rel = file.relative_to(self.workspace_root)
                            results.append(f"{rel}:{lineno}: {line}")
                except Exception:
                    continue
            return "\n".join(results) if results else "No matches found"
        except ValueError as e:
            return f"Error: {e}"
        except re.error as e:
            return f"Error: invalid regex pattern: {e}"
        except Exception as e:
            return f"Error: {e}"

    def _resolve(self, path: str) -> Path:
        """Resolve path relative to base, ensuring it stays within base."""
        resolved = (self.workspace_root / path).resolve()
        if not resolved.is_relative_to(self.workspace_root):
            raise ValueError(f"Path '{path}' escapes the workspace boundary")
        return resolved

read_file

read_file(path: str) -> str

Read the full contents of a file. Path is relative to /workspace.

Source code in src/ant_ai/tools/builtins/filesystem_tool.py
44
45
46
47
48
49
50
51
52
53
def read_file(self, path: str) -> str:
    """Read the full contents of a file. Path is relative to /workspace."""
    try:
        return self._resolve(path).read_text(encoding="utf-8")
    except ValueError as e:
        return f"Error: {e}"
    except FileNotFoundError:
        return f"Error: file not found: {path}"
    except Exception as e:
        return f"Error: {e}"

write_file

write_file(path: str, content: str) -> str

Write content to a file, overwriting if it exists. Creates parent directories as needed. Path is relative to /workspace.

Source code in src/ant_ai/tools/builtins/filesystem_tool.py
55
56
57
58
59
60
61
62
63
64
65
def write_file(self, path: str, content: str) -> str:
    """Write content to a file, overwriting if it exists. Creates parent directories as needed. Path is relative to /workspace."""
    try:
        target: Path = self._resolve(path)
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(content, encoding="utf-8")
        return f"Written {len(content)} bytes to {path}"
    except ValueError as e:
        return f"Error: {e}"
    except Exception as e:
        return f"Error: {e}"

list_dir

list_dir(path: str = '') -> list[str]

List files and directories at the given path. Path is relative to /workspace. Defaults to workspace root.

Source code in src/ant_ai/tools/builtins/filesystem_tool.py
67
68
69
70
71
72
73
74
75
76
77
def list_dir(self, path: str = "") -> list[str]:
    """List files and directories at the given path. Path is relative to /workspace. Defaults to workspace root."""
    try:
        target: Path = self._resolve(path) if path else self.workspace_root
        return [entry.name for entry in sorted(target.iterdir())]
    except ValueError as e:
        return [f"Error: {e}"]
    except FileNotFoundError:
        return [f"Error: directory not found: {path}"]
    except Exception as e:
        return [f"Error: {e}"]

search

search(pattern: str, path: str = '') -> str

Search for a regex pattern recursively within path. Path is relative to /workspace. Returns matching lines in the format 'file:lineno: content'.

Source code in src/ant_ai/tools/builtins/filesystem_tool.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def search(self, pattern: str, path: str = "") -> str:
    """Search for a regex pattern recursively within path. Path is relative to /workspace. Returns matching lines in the format 'file:lineno: content'."""
    try:
        root: Path = self._resolve(path) if path else self.workspace_root
        regex: Pattern[str] = re.compile(pattern)
        results: list[str] = []
        for file in sorted(root.rglob("*")):
            if not file.is_file():
                continue
            try:
                for lineno, line in enumerate(
                    file.read_text(encoding="utf-8", errors="replace").splitlines(),
                    start=1,
                ):
                    if regex.search(line):
                        rel = file.relative_to(self.workspace_root)
                        results.append(f"{rel}:{lineno}: {line}")
            except Exception:
                continue
        return "\n".join(results) if results else "No matches found"
    except ValueError as e:
        return f"Error: {e}"
    except re.error as e:
        return f"Error: invalid regex pattern: {e}"
    except Exception as e:
        return f"Error: {e}"