Default Tools - Filesystem

July 22, 2026 · View on GitHub

description: Default Tools - Filesystem reference. 18 safety.fs-wrapped tools (list-roots · read · list · stat · grep · search · count · slice · sort · cut · find · write · append · edit · copy · move · delete · deleteDir) over readable roots and a writable working directory.

Default Tools - Filesystem

The 18 tools in default-tool-specs-builtin-fs.json are the safety.fs surface as ready-to-call tools - a shell-style filesystem pipeline covering list-roots, read, list, stat, grep, recursive content search, count, slice, sort, cut, find, write, append, targeted edit, copy, move, and delete (file and directory). They operate within two boundaries:

  • Readable roots - directories the tools may read from, recursively. The user's home directory is a readable root by default.
  • A working directory (spring.ai.playground.tool-studio.fs.base-path, default ${user.home}/spring-ai-playground/workspace) - the only writable location, and where relative paths resolve. When a tool runs inside a chat, writes are confined to a per-conversation subdirectory of it (<workspace>/<conversationId>/), created on first write and removed when you delete the conversation; listAllowedDirectories reports that exact path at runtime. This holds however the chat reaches the tool - a fixed selection travels through the built-in MCP server's loopback connection, which carries the conversation identity along with the call. A caller with no conversation - an external MCP client invoking the same exposed tools - writes to the working directory root instead.

So a relative path resolves under the working directory; an absolute path may be read anywhere under a readable root, but writes are confined to the working directory. Call listAllowedDirectories first to learn the exact absolute paths before reading or writing.

Three capability tiers. Read tools (listDir, readTextFile, grepFile, searchInFiles, ...) need only fileRead and rank L3. Write tools that create or amend a file (writeTextFile, appendTextFile, editTextFile, copyFile) need fileWrite and rank L4. Tools that remove or relocate data (moveFile, deleteFile, deleteDir) carry a destructive flag and rank L5 - deliberately above a plain overwrite, because the loss is not recoverable from the file itself. Every write and destructive tool is gated by human-in-the-loop approval, so its risk chip shows two levels with an arrow (L5 → L4, L4 → L3): the level before the arrow is the inherent posture that still drives sandbox permissions and the audit log, and the level after is the effective posture once a mandatory approval is credited (one band lower). A poisoned tool description forfeits that credit, and the runtime denies the call outright if approval is unavailable, so a credited mitigation can never silently no-op.

Because they ride on java.nio.file.Path / Files, separator handling (/ vs \), case folding, and symlink semantics are normalised at the JVM layer - these tools behave identically on macOS, Windows, and Linux. See Tool Studio: Cross-platform by design for the mechanics, and Tool Studio: Filesystem mode for the read-only / read-write sandbox split.

The 18 filesystem tools { #the-filesystem-tools }

listAllowedDirectories 🆓
:material-folder-key-outline:
file · pipeline L3
Reports the filesystem boundaries these tools operate within: the readable roots (anything under them can be read) and the working directory (the only writable location; a per-conversation subdirectory when run from a chat). Call this first. Uses safety.fs.readRoots() / workspace().
**Params**   -
**Env**       -
Click for full reference · params · sandbox · JS source
readTextFile 🆓
:material-file-document-outline:
file · pipeline L3
Reads a UTF-8 text file from disk and returns its contents as a single string.
**Params**   `path`
**Env**       -
Click for full reference · params · sandbox · JS source
listDir 🆓
:material-folder-outline:
file · pipeline L3
Lists the immediate entries (files and subdirectories) of a directory. Entries in the working directory come back as relative names; entries elsewhere under a readable root come back as absolute paths. Uses safety.fs.list().
**Params**   `dir`
**Env**       -
Click for full reference · params · sandbox · JS source
statFile 🆓
:material-information-outline:
file · pipeline L3
Returns size, last-modified timestamp, and a directory flag for a path (relative under the working directory, or absolute under a readable root). Uses safety.fs.stat().
**Params**   `path`
**Env**       -
Click for full reference · params · sandbox · JS source
lineCount 🆓
:material-counter:
file · pipeline L3
Counts the lines in a UTF-8 text file. Uses safety.fs.lineCount().
**Params**   `path`
**Env**       -
Click for full reference · params · sandbox · JS source
sliceFile 🆓
:material-content-cut:
file · pipeline L3
Returns a slice of lines from a UTF-8 text file (head / tail / range). `start` is 0-based inclusive, `end` is 0-based exclusive (Python-style slice). Negative values count from the end of the file. Uses safety.fs.slice().
**Params**   `path` · `start` · `end`
**Env**       -
Click for full reference · params · sandbox · JS source
sortFile 🆓
:material-sort:
file · pipeline L3
Sorts the lines of a UTF-8 text file and returns the sorted lines as an array. Options: reverse / numeric / caseInsensitive / unique. Uses safety.fs.sort().
**Params**   `path` · `reverse` · `numeric` · `caseInsensitive` · `unique`
**Env**       -
Click for full reference · params · sandbox · JS source
grepFile 🆓
:material-file-find-outline:
file · pipeline L3
Searches a UTF-8 text file for lines matching a JavaScript regex. Returns an array of matching lines (optionally numbered). Uses safety.fs.grep().
**Params**   `pattern` · `path` · `caseInsensitive` · `numbered` · `limit`
**Env**       -
Click for full reference · params · sandbox · JS source
findFiles 🆓
:material-folder-search-outline:
file · pipeline L3
Recursively finds files matching a glob inside a directory. Glob supports `*` and `?`. Optional max recursion depth and type filter ('file' or 'dir'). Uses safety.fs.find().
**Params**   `dir` · `glob` · `maxDepth` · `type`
**Env**       -
Click for full reference · params · sandbox · JS source
cutFileFields 🆓
:material-table-column:
file · pipeline L3
Extracts selected fields from each line of a delimited file (CSV/TSV/etc.). Uses safety.fs.cut(). 1-based field numbers, comma-separated alternatives via the array.
**Params**   `path` · `fields` · `delimiter` · `regex`
**Env**       -
Click for full reference · params · sandbox · JS source
writeTextFile 🆓
:material-file-edit-outline:
file · pipeline L4
Writes a UTF-8 text file inside the working directory (creating parent directories as needed). Overwrites any existing file. Requires `fileWrite` permission on the sandbox.
**Params**   `path` · `content`
**Env**       -
Click for full reference · params · sandbox · JS source
searchInFiles 🆓
:material-text-search:
file · pipeline L3
Recursively searches file **contents** across a directory tree for lines matching a regex, returning each hit as `{ file, line, text }`. Where `grepFile` scans one file, this opens every file under `dir` whose name matches `glob`. Uses safety.fs.searchInFiles().
**Params**   `pattern` · `dir` · `glob` · `caseInsensitive` · `maxDepth` · `limit`
**Env**       -
Click for full reference · params · sandbox · JS source
appendTextFile 🆓
:material-file-plus-outline:
file · pipeline L4
Appends UTF-8 text to the **end** of a file, creating it if absent. Unlike `writeTextFile` (which overwrites), this adds after the existing bytes - ideal for logs and incremental output. Uses safety.fs.appendText().
**Params**   `path` · `content`
**Env**       -
Click for full reference · params · sandbox · JS source
editTextFile 🆓
:material-file-replace-outline:
file · pipeline L4
Makes a **targeted** edit by replacing an exact substring, without rewriting the whole file. By default `oldString` must occur exactly once (else rejected as ambiguous); `replaceAll` swaps every occurrence. Uses safety.fs.editText().
**Params**   `path` · `oldString` · `newString` · `replaceAll`
**Env**       -
Click for full reference · params · sandbox · JS source
copyFile 🆓
:material-content-copy:
file · pipeline L4
Copies a file to a new location. The source may be any **readable** file (a readable root, or an upload); the destination must be inside the working directory. The source is left intact. Uses safety.fs.copy().
**Params**   `from` · `to`
**Env**       -
Click for full reference · params · sandbox · JS source
moveFile 🆓
:material-file-move-outline:
file · pipeline L5
Moves or renames a file within the working directory. **Destructive**: the source no longer exists at its old path afterwards, and an existing destination is overwritten. Uses safety.fs.move().
**Params**   `from` · `to`
**Env**       -
Click for full reference · params · sandbox · JS source
deleteFile 🆓
:material-file-remove-outline:
file · pipeline L5
Permanently deletes a single file from the working directory. **Destructive** and irreversible. Directories are rejected - use `deleteDir`. Uses safety.fs.delete().
**Params**   `path`
**Env**       -
Click for full reference · params · sandbox · JS source
deleteDir 🆓
:material-folder-remove-outline:
file · pipeline L5
Permanently deletes a directory **and all of its contents** (recursive) from the working directory. **Destructive** and irreversible. The workspace root itself and plain files are rejected. Uses safety.fs.deleteDir().
**Params**   `path`
**Env**       -
Click for full reference · params · sandbox · JS source

Composition patterns (shell-style filesystem chains)

These tools mirror the standard Unix-shell pipeline shape, but every step is a JSON-returning function so the agent can reason between calls:

  • Read → filter → trim → save - listDir(dir)grepFile(pattern, path)sliceFile(path, start, end)writeTextFile(outPath, content). The canonical "summarise recent errors from a log directory" flow.
  • Find → cut → ETL - findFiles(dir, glob='*.csv') → loop with cutFileFields(path, fields=[1,3]) to project a directory of CSVs into one structured dataset.
  • Sort dedupe → count - sortFile(path, numeric=true, unique=true)lineCount(path) to deduplicate a numeric stream in place and report the resulting size.
  • Stat-first guard - statFile(path) → branch on size / lastModified → only run the rest of the pipeline if the file changed since the last run.
  • Search → edit - searchInFiles(dir, glob, pattern) finds every hit across the tree, then editTextFile(path, oldString, newString) applies a targeted, unique-match change to each file - the code-refactor loop.
  • Copy → transform → clean up - copyFile(upload, 'work.csv') pulls an upload into the writable workspace; process it, then deleteFile('work.csv') or deleteDir('tmp') removes the scratch copy. Every write and destructive step pauses for approval first.

Tutorial 8: Default Tool Recipes walks the Read → filter → trim → save chain end-to-end as summariseRecentLogs.

Keys & secrets

One configuration value, no real secrets.

VariableWhat it doesDefaultWhere to set
SPRING_AI_PLAYGROUND_TOOL_STUDIO_FS_BASE_PATHThe safety.fs working directory - the only writable location, and where relative paths resolve. Reads also reach the readable roots (the home directory by default).${user.home}/spring-ai-playground/workspaceLauncher Environment Variables card, or export SPRING_AI_PLAYGROUND_TOOL_STUDIO_FS_BASE_PATH=/path before launch

The File Toolkit preset opts every read tool (now including searchInFiles) into fileRead automatically. The write tools (writeTextFile, appendTextFile, editTextFile, copyFile) require fileWrite (L4), and the destructive tools (moveFile, deleteFile, deleteDir) additionally carry the destructive flag (L5); you enable these per-tool in the Sandbox & Capabilities pane - see Tool Studio: Filesystem mode. In Agentic Chat, the Workspace organizer preset wires the full set - survey, restructure, clean up - with every mutating call gated by human-in-the-loop approval.

Tool Studio: Filesystem mode - fileRead / fileWrite semantics and base-path enforcement.