Skip to content

File System Server

file_system_server (doc_processing_std_tools.file_system, MCP name file-system-tools) gives an agent 12 tools to explore, read, write, edit and search files. It needs no extra.

from doc_processing_std_tools import file_system_server

Tools

Tool Key parameters Returns
get_directory_structure path, max_depth=3, include_hidden=False A tree drawn with box characters, directories first
list_directory path, include_hidden=False JSON list of {name, type, size} (size for files only), directories first
read_file path, offset (1-based line), limit (lines), encoding="utf-8" The text, truncated at the output limit
write_file path, content, create_dirs=False, encoding="utf-8" Confirmation with the bytes written; overwrites an existing file
create_file path, content, create_dirs=False Confirmation; fails if the file already exists
edit_file path, old_string, new_string Confirmation; old_string must occur exactly once
apply_patch path, patch (unified diff) Confirmation with the hunk count
delete_file path Confirmation; files only, not directories
move_file source, destination Confirmation; moves files or directories
copy_file source, destination Confirmation; copies a file with its metadata
get_file_metadata path JSON: path, size_bytes, size_human, modified, created, is_file, is_dir, extension
search_in_files path, query, file_pattern="*", is_regex=False, max_results=50 JSON list of {file, line, content} matches

Timestamps in get_file_metadata are ISO 8601 in UTC. Hidden entries are names that start with a dot.

Environment variables

Variable Default Effect here
DPTTOOLS_MAX_FILE_SIZE_MB 50 read_file refuses larger files; search_in_files skips them
DPTTOOLS_OUTPUT_TRUNCATION_LIMIT 50000 read_file output is cut at this many characters, with a notice

Examples

Tool calls are written here as Python calls; the model sends the same arguments as JSON.

Read lines 100–149 of a log:

read_file(path="logs/run.log", offset=100, limit=50)

Search Python files for a regular expression:

search_in_files(path="src", query=r"def \w+_server", file_pattern="*.py", is_regex=True)

Change one line with apply_patch(path="config.ini", patch=...), where patch is:

--- a/config.ini
+++ b/config.ini
@@ -1,3 +1,3 @@
 [server]
-port = 8000
+port = 8080
 host = 127.0.0.1

Pitfalls

  • Paths are not confined to a directory. Relative .. paths and absolute paths are accepted; the container is the boundary. See Architecture.
  • apply_patch is strict. Each hunk's context and removed lines must match the file exactly at the stated line numbers, and the hunk body must contain exactly the number of lines its @@ header declares. A mismatch is rejected — as "malformed patch" when the diff itself is inconsistent, or as "does not match current file content" when the file has changed — and the file is left untouched. For a single replacement, edit_file is simpler.
  • edit_file needs a unique match. If old_string occurs zero times or more than once, the call fails and says how many times it was found. Include more surrounding text to make it unique.
  • Overlapping calls. Through the composed launcher, tools run on worker threads and calls can overlap. Every tool that changes a file (write_file, create_file, edit_file, apply_patch, delete_file, move_file, copy_file) holds one lock from its checks to the end of the change, so overlapping calls behave as if they ran one after the other: no lost edits, no interleaved overwrites, and a deleted or moved file is not written back. The lock is per server process.
  • search_in_files searches only text it can read. It skips files over the size limit and files it cannot open. A PDF is searched only through a companion text file named <name>.pdf.txt next to it; a PDF without one is skipped. The glob in file_pattern is applied recursively, and the search stops at max_results matching lines.
  • write_file and create_file do not create folders unless create_dirs=True.
  • Encodings. read_file replaces undecodable bytes instead of failing. edit_file, apply_patch and create_file always use UTF-8.
  • Large listings are not truncated. get_directory_structure and list_directory return the whole result; keep max_depth small on big trees.