Skip to content

Efficient Code Editing

This guide shows how to use MCP Filesystem Ultra tools efficiently to minimize token usage when editing code. Reading and writing only the parts you need usually saves a large fraction of tokens compared to full-file round-trips.

AI assistants sometimes read entire files and rewrite them completely, even when the change is only a few lines. For a 5,000-line file, that means sending thousands of lines of context on every turn.

When you need to edit a specific function or section:

search_files(path="engine.go", pattern="func ReadFile")

Result: file path, line number, and matching line.

read_file(path="engine.go", start_line=45, end_line=67)

Result: exactly the lines you need.

edit_file(path="engine.go", old_text="return nil", new_text="return content")

For several changes in the same file, use multi_edit so the file is written once and the diff is returned as a single result.

For large files, prefer a targeted workflow:

  1. Use search_files() to locate the relevant lines.
  2. Use read_file() with start_line/end_line to read only those lines.
  3. Edit with edit_file() or multi_edit() using the exact text from the read.

Example:

  • File size: 5,000 lines
  • Read-only approach: read_file() returns the whole file.
  • Targeted approach: search_files() + read_file(start_line/end_line) + edit_file() touches only the needed lines.

The savings grow with file size; the exact amount depends on your model and token pricing.

AntipatternProblemBetter Way
read_file() on a large file when you only need a sectionSends unnecessary contextUse read_file() with start_line/end_line
Edit without locating the target firstRisk of wrong replacement or stale contextUse search_files() first to verify location
Many sequential edit_file() calls on the same fileMultiple disk writes and re-readsUse multi_edit() for several changes in one atomic write
Rewriting an entire file for a small changeSends the whole file back and forthUse edit_file() for surgical changes
ToolPurposeUse When
search_filesFind code locationYou need to locate where something is
read_file with start_line/end_lineRead lines N–MYou know the line numbers (from search)
read_fileRead entire fileFile is small, or you genuinely need the whole file
edit_fileReplace text in fileYou have exact old_text and new_text
multi_editMultiple edits in one fileYou have several changes in the same file
write_fileCreate/overwrite entire fileFile does not exist or needs a complete rewrite
search_files with count_only:trueCount matches without readingYou need to verify how many occurrences exist
edit_file with occurrenceReplace a specific matchYou need to change only the 1st, 2nd, or last occurrence

Scenario: Change ProcessData() in a large file.

  1. read_file("main.go") — reads the entire file.
  2. Analyze and rewrite.
  3. write_file("main.go", entire_content) — writes the entire file back.
  1. search_files("main.go", "func ProcessData") — returns the line range.
  2. read_file("main.go", start_line=156, end_line=189) — reads only the function.
  3. edit_file("main.go", old_snippet, new_snippet) — replaces the changed lines.

If you need to change several places inside that function, use multi_edit instead.

For edits that depend on a previous read, use expected_hash to detect external changes:

  1. read_file() returns a content_hash.
  2. Feed that hash to the next edit_file(expected_hash: ...).
  3. If the file changed on disk in between, the edit fails with a clear external_change warning.

This prevents accidental overwrites when another process (or another agent) touched the file.

For multi-file operations, batch_operations with a pipeline_json can chain search, read, edit, and verify steps in one call.

{
"name": "rename-function",
"create_backup": true,
"steps": [
{ "id": "find", "action": "search", "params": { "path": "src/", "pattern": "oldFunc" } },
{ "id": "edit", "action": "edit", "input_from": "find", "params": { "old_text": "oldFunc", "new_text": "newFunc" } },
{ "id": "verify", "action": "count_occurrences", "input_from": "find", "params": { "pattern": "newFunc" } }
]
}

This is one round-trip instead of three separate calls, and it includes automatic backup and rollback.

Preview changes without modifying files:

{
"name": "preview-migration",
"dry_run": true,
"verbose": true,
"steps": [
{ "id": "find", "action": "search", "params": { "path": ".", "pattern": "deprecated_api", "file_types": [".go"] } },
{ "id": "preview", "action": "edit", "input_from": "find", "params": { "old_text": "deprecated_api", "new_text": "new_api" } }
]
}

Shows which files would be affected and how many replacements, without touching disk.

  1. Search first — use search_files to find line numbers.
  2. Read ranges — use read_file with start_line/end_line when you only need part of a file.
  3. Edit surgically — prefer edit_file over write_file for small changes.
  4. Batch same-file edits — use multi_edit for multiple changes in one file.
  5. Chain multi-file work — use batch_operations with pipeline_json when possible.
  6. Verify external changes — use expected_hash to chain reads and edits safely.

Following these patterns usually saves a large fraction of tokens compared to full-file round-trips, and it reduces the chance of accidental overwrites.