Skip to content

Safe Editing Protocol

For: Claude Code, Claude Desktop, and developers Last Updated: February 2026


Use this checklist for ANY file edit operation:

## Before Editing File: [FILENAME]
### STEP 1: Read & Verify (5 min)
- [ ] Open file with `read_file() with start_line/end_line` to see exact content
- [ ] Copy the EXACT text you want to change (including spaces/tabs)
- [ ] Note the line numbers
- [ ] Paste here: [EXACT_TEXT_HERE]
### STEP 2: Confirm Pattern (2 min)
- [ ] Run `search_files() with count_only:true` to find how many times it appears
- [ ] Expected count: [NUMBER]
- [ ] Actual count: [NUMBER]
- [ ] ✅ Counts match: YES / NO
### STEP 3: Validate Edit (2 min)
- [ ] Small file (< 1 MB): Use `edit_file()` or `edit_file()`
- [ ] Large file (> 1 MB): Use `batch_operations` with atomic=true
- [ ] Critical change: Use `batch_operations` even if small
- [ ] Multiple changes: ALWAYS use `batch_operations`
- [ ] ✅ Method chosen: [METHOD]
### STEP 4: Execute Edit (1 min)
- [ ] For recovery_edit:

path: [FILE_PATH] old_text: [COPY_FROM_READ_FILE_RANGE] new_text: [REPLACEMENT_TEXT] force: false (unless risk warning)

- [ ] For batch_operations:
```json
{
"operations": [
{
"type": "edit",
"path": "[FILE_PATH]",
"old_text": "[FROM_READ_FILE_RANGE]",
"new_text": "[REPLACEMENT]"
}
],
"atomic": true
}
  • ✅ Operation executed
  • File size didn’t change drastically
  • New text appears in file: Use read_file() with start_line/end_line to check
  • Old text is gone: Use search_files() with count_only:true to confirm 0 matches
  • ✅ All verifications passed: YES / NO

Total Time: ~12 minutes for safe edit

---
## Rules
### Rule 1: Do Not Use Fuzzy Matching for Critical Edits
**Avoid:** Copying text from memory and hoping it matches.
**Recommended:** Use `read_file() with start_line/end_line` to get exact text.
### Rule 2: Do Not Skip Line Ending Verification
**Avoid:** Assuming the file uses Unix line endings.
**Recommended:** Check `read_file() with start_line/end_line` output for `\r\n` or `\n`.
### Rule 3: Do Not Edit Without Backup
**Avoid:** Using `edit_file()` without `force=true` when at risk.
**Recommended:** Always let the server create backups automatically (v3.8.0+).
### Rule 4: Do Not Make Multiple Changes in One Edit
**Avoid:**

old_text: “line1\nline2\nline3\nline4\nline5” new_text: “line1_new\nline2_new”

**Recommended:**

operations: [ { type: “edit”, old_text: “line1”, new_text: “line1_new” }, { type: “edit”, old_text: “line2”, new_text: “line2_new” }, { type: “edit”, old_text: “line3”, new_text: "" } ]

### Rule 5: Do Not Skip Verification After Edit
**Avoid:** Moving on immediately after an edit completes.
**Recommended:** Verify with `search_files() with count_only:true` that the old text is gone.
---
## Decision Tree

START: “I need to edit a file” │ ├─ Is it a CRITICAL file? (config, code, database schema) │ ├─ YES → Use batch_operations with atomic=true │ └─ NO ─┐ │ └─ Go to next check │ ├─ Does the old_text span MULTIPLE LINES? │ ├─ YES (3+ lines) → Use batch_operations │ └─ NO (1-2 lines) ─┐ │ └─ Go to next check │ ├─ Are you making MULTIPLE CHANGES? │ ├─ YES (2+ edits) → Use batch_operations │ └─ NO (1 edit) ─┐ │ └─ Go to next check │ ├─ Is the FILE SIZE > 1 MB? │ ├─ YES → Use batch_operations │ └─ NO ─┐ │ └─ Go to next check │ └─ DECISION: Use edit_file() or edit_file()

---
## Tool Reference
### 1. read_file() with start_line/end_line - Get Exact Content
```python
response = await client.call_tool(
"filesystem-ultra:read_file",
{
"path": "/path/to/file.cs",
"start_line": 10,
"end_line": 20
}
)
# Output shows EXACT text with line numbers
# COPY THIS EXACTLY for old_text parameter

Why: Guarantees you have the exact text including whitespace


2. search_files() with count_only:true - Verify Pattern Exists

Section titled “2. search_files() with count_only:true - Verify Pattern Exists”
response = await client.call_tool(
"filesystem-ultra:search_files",
{
"path": "/path/to/file.cs",
"pattern": "exact_text_from_read_file",
"count_only": true
}
)
# Output shows how many matches and which lines
# Must be > 0 before attempting edit

Why: Confirms the text exists exactly as you expect


response = await client.call_tool(
"filesystem-ultra:edit_file",
{
"path": "/path/to/file.cs",
"old_text": "COPY_EXACTLY_FROM_read_file",
"new_text": "replacement text",
"force": False # Set to True only if risk warning appears
}
)
# Returns: Modified file, backup ID, lines affected
# After: Verify with search_files() with count_only:true that old text is gone

When to use: Single-line or 1-2 line replacements on small files When NOT to use: Multiple changes, large edits, critical files


4. batch_operations() - Multiple Safe Edits

Section titled “4. batch_operations() - Multiple Safe Edits”
response = await client.call_tool(
"filesystem-ultra:batch_operations",
{
"operations": [
{
"type": "edit",
"path": "/path/to/file.cs",
"old_text": "line1 to replace",
"new_text": "line1 replacement"
},
{
"type": "edit",
"path": "/path/to/file.cs",
"old_text": "line2 to replace",
"new_text": "line2 replacement"
}
],
"atomic": True # ALWAYS true for safety
}
)
# All operations apply together, or none if any fails
# After: Verify each change with search_files() with count_only:true

When to use: ALWAYS for critical edits, multiple changes, large files Advantage: Atomic (all-or-nothing), automatic backup


response = await client.call_tool(
"filesystem-ultra:search_files",
{
"path": "/path/to/file.cs",
"pattern": "partial_text_to_find",
"context_lines": 3
}
)
# Shows matches with surrounding code
# Use to understand the context before editing

Why: Understand code context before making changes


Before (without validation):

# Read file
response1 = client.call_tool("read_file", {"path": "file.cs"})
# Try to edit without checking if text exists
response2 = client.call_tool("edit_file", {
"path": "file.cs",
"old_text": "...some multiline text...",
"new_text": "replacement"
})
# ❌ FAILS: "old_text not found"

After (with validation):

# STEP 1: Read exact content
response1 = await client.call_tool(
"filesystem-ultra:read_file",
{"path": "file.cs", "start_line": 10, "end_line": 20}
)
# Output:
# Line 10: // Orders list
# Line 11: public List<C1Pedidos> Orders { get; set; } = new();
# Line 12: private List<C1Pedidos> filteredOrders = new();
# Line 13: private bool isFiltered = false;
# Line 14: private Dictionary<int, bool> listadoValidados = new Dictionary<int, bool>();
# STEP 2: Copy EXACTLY and verify it exists
exact_text = """ // Orders list
public List<C1Pedidos> Orders { get; set; } = new();
private List<C1Pedidos> filteredOrders = new();
private bool isFiltered = false;
private Dictionary<int, bool> listadoValidados = new Dictionary<int, bool>();"""
response2 = await client.call_tool(
"filesystem-ultra:search_files",
{"path": "file.cs", "pattern": exact_text, "count_only": true}
)
# Output: Found 1 match at line 10
# ✅ Confirmed: Text exists exactly as expected
# STEP 3: Use batch_operations (safer for multiline)
response3 = await client.call_tool(
"filesystem-ultra:batch_operations",
{
"operations": [
{
"type": "edit",
"path": "file.cs",
"old_text": exact_text,
"new_text": """ // Orders list
public List<C1Pedidos> Orders { get; set; } = new();
private List<C1Pedidos> filteredOrders = new();
private bool isFiltered = false;"""
}
],
"atomic": True
}
)
# ✅ SUCCESS: 1 replacement, file updated
# STEP 4: Verify the result
response4 = await client.call_tool(
"filesystem-ultra:search_files",
{"path": "file.cs", "pattern": "private Dictionary<int, bool>", "count_only": true}
)
# Output: Found 0 matches
# ✅ Confirmed: Old text is gone
response5 = await client.call_tool(
"filesystem-ultra:read_file",
{"path": "file.cs", "start_line": 10, "end_line": 20}
)
# Output shows updated content without the Dictionary line
# ✅ SUCCESS: Edit verified

❌ BAD:
old_text = "line1\nline2" # Unix
# But file has: "line1\r\nline2" # Windows
# Result: NO MATCH
✅ GOOD:
# Check read_file() with start_line/end_line output for line ending type
# Match exactly what's shown
❌ BAD:
old_text = "public List Orders { get; set; }"
# But file has: " public List Orders { get; set; }"
# (4 spaces at start)
# Result: NO MATCH
✅ GOOD:
old_text = " public List Orders { get; set; }"
# Copy from read_file() with start_line/end_line output with indentation
❌ BAD:
old_text = " property = value" # 4 spaces
# But file has: "\tproperty = value" # 1 tab
# Result: NO MATCH
✅ GOOD:
# Use read_file() with start_line/end_line which shows [SPACE] and [TAB] clearly
❌ BAD:
# Edit multiple lines and hope it works
# No verification afterwards
✅ GOOD:
# After each edit, use search_files() with count_only:true on old text
# Should return 0
# Use read_file() with start_line/end_line to view the actual result

Mistake 5: One Big Edit Instead of Smaller Ones

Section titled “Mistake 5: One Big Edit Instead of Smaller Ones”
❌ BAD:
old_text = """entire_method_body_here
lots_of_lines
might_fail"""
new_text = "simplified_replacement"
✅ GOOD:
# Split into multiple edits if possible
# Use batch_operations for all changes
# One operation per logical change

Diagnosis: The exact text doesn’t exist

Steps:

  1. Run read_file() with start_line/end_line around the area you think it is
  2. Copy the text EXACTLY (spaces, tabs, line endings)
  3. Use search_files() with count_only:true first to verify it exists
  4. If found, try again with exact text

Diagnosis: File was modified since you read it

Steps:

  1. Run read_file() with start_line/end_line again to get latest content
  2. Verify the text still exists
  3. Update your old_text if needed
  4. Retry edit

”OPERATION BLOCKED - Change impact is HIGH/CRITICAL”

Section titled “”OPERATION BLOCKED - Change impact is HIGH/CRITICAL””

Diagnosis: Edit is large/risky, need explicit approval

Steps:

  1. Review the warning message carefully
  2. Add "force": true if you’re sure
  3. Better: Use batch_operations with smaller edits
  4. Better: Create backup first manually if needed

”Expected recovery but file content changed unexpectedly”

Section titled “”Expected recovery but file content changed unexpectedly””

Diagnosis: Something went wrong with file sync

Steps:

  1. Don’t edit anymore
  2. Check backup files in .mcp-filesystem-backups/
  3. Use backup with action “restore” if needed
  4. Report this as a bug

SituationToolReason
First time editing a fileread_file() with start_line/end_lineGet exact content
About to editsearch_files() with count_only:trueConfirm text exists
Single line edit, small fileedit_file()Simple, fast
Multiple line editbatch_operationsAtomic, safer
Critical filebatch_operationsMore reliable
File > 1 MBbatch_operationsBetter performance
Don’t know where to editsearch_files()Find first
Want to see contextsearch_files()Context included
After any editsearch_files() with count_only:trueVerify success
Verify entire sectionread_file() with start_line/end_lineSee actual result

# 1. Stop immediately - don't make more edits
# 2. Check if backup exists
response = await client.call_tool(
"filesystem-ultra:backup",
{"action": "list", "file_path": "/path/to/file.cs"}
)
# 3. Restore from backup
response = await client.call_tool(
"filesystem-ultra:backup",
{
"action": "restore",
"backup_id": "backup_id_from_list",
"restore_to": "/path/to/file.cs"
}
)
# 4. Verify restoration
response = await client.call_tool(
"filesystem-ultra:read_file",
{"path": "/path/to/file.cs", "start_line": 1, "end_line": 20}
)
1. DON'T USE edit_file()
2. DON'T USE batch_operations without verification
3. DO USE: read_file() with start_line/end_line to see what's there
4. DO USE: search_files() with count_only:true to confirm text exists
5. DO USE: search_files() to understand context
6. THEN decide if edit is safe


The 5 steps:

  1. Read - Use read_file() with start_line/end_line to see exact content
  2. Verify - Use search_files() with count_only:true to confirm pattern exists
  3. Choose - Use edit_file() or batch_operations
  4. Execute - Run the edit with exact text
  5. Confirm - Use search_files() with count_only:true to verify success

The 5 rules:

  1. Do not use fuzzy matching for critical edits.
  2. Do not skip line ending verification.
  3. Do not edit without backup.
  4. Do not make multiple changes in one edit.
  5. Do not skip verification after editing.

Updated: February 2026 | Version: 3.13.2