Skip to content

Backups and Recovery

The MCP Filesystem Ultra backup system protects your code against accidental loss. Every destructive operation (edit, delete) automatically creates a persistent backup that you can easily recover.

  • Automatic backups - Created automatically, no action required
  • Intelligent validation - Warns you before risky changes
  • Quick recovery - One command to restore lost code
  • Complete audit trail - History of all changes

Backups are created automatically before:

  1. File edits (edit_file)
  2. Deletions (delete_file)
  3. Batch operations (batch_operations with create_backup: true)

By default, backups are stored in:

  • Windows: %TEMP%\mcp-batch-backups\ (typically C:\Users\<you>\AppData\Local\Temp\mcp-batch-backups\)
  • Linux/macOS: $TMPDIR/mcp-batch-backups/ (typically /tmp/mcp-batch-backups)

The server resolves temp/mcp-batch-backups relative to the OS temp directory. To use a different location, pass --backup-dir <path> (and the path must be in --allowed-paths).

Each backup has its own directory with a unique ID:

20241203-153045-abc123\
├── metadata.json # Backup information
└── files\ # Backed up files
└── your_file.go

Customize backup behavior in claude_desktop_config.json:

{
"mcpServers": {
"filesystem-ultra": {
"args": [
"--backup-dir", "C:\\Backups\\MCP",
"--backup-max-age", "14",
"--backup-max-count", "200",
"C:\\your\\project\\",
"C:\\Backups\\MCP"
]
}
}
}

Important: The backup directory MUST be in allowed paths.


Before editing a file, the system analyzes the impact of the change:

  • Percentage of the file that will change
  • Number of occurrences to replace
  • Specific risk factors
LevelConditionsBehavior
LOW< 20% change, < 50 occurrencesProceeds silently
MEDIUM≥ 20% change, OR ≥ 50 occurrencesAuto-backup + proceed + risk notice
HIGH≥ 75% change, OR ≥ 100 occurrencesAuto-backup + proceed + prominent warning
CRITICAL≥ 90% changeAuto-backup + proceed + VERIFY instruction — never blocked by risk level alone

Thresholds are defaults and can be changed with --risk-threshold-medium and --risk-threshold-high. Occurrence thresholds are --risk-occurrences-medium (default 50) and --risk-occurrences-high (default 100). CRITICAL is fixed at ≥ 90% file change.

Since v4.5.10, two additional safety guards can still block an edit_file independently of the risk level:

  • Accidental-rewrite guard — fires when new_text is > 2× the size of old_text AND > 50% of the file is outside the matched block AND new_text is > 500 B / > 50% of file size. Override with allow_rewrite: true (not force:true).
  • --auto-occ=block (v4.5.17) — rejects the edit if the session’s stored content_hash baseline differs from the on-disk hash (another process wrote the file between read and edit).

See File Operations — Blocking conditions beyond CRITICAL.

When editing a significant portion of a file:

edit_file({
path: "main.go",
old_text: "// old implementation block...",
new_text: "// new implementation block..."
})

Response (edit succeeds immediately):

Replaced 1 occurrence(s). Characters changed: ~400
Backup: 20260302-143022-abc123
Risk notice [medium]
40.0% of file changed (1 occurrence(s), ~400 chars)
Auto-backup: 20260302-143022-abc123
Restore with: backup({ action:"restore", backup_id })

Example: CRITICAL Risk (Auto-Proceeds with Warning)

Section titled “Example: CRITICAL Risk (Auto-Proceeds with Warning)”

When attempting to rewrite nearly the entire file:

edit_file({
path: "main.go",
old_text: "func",
new_text: "function"
// This replaces most of the file
})

Response:

Replaced 1 occurrence(s). Characters changed: ~200
Backup: 20260302-143022-def456
⚠️ RISK LEVEL: CRITICAL
Almost complete file rewrite (95.3%)
Auto-backup created: 20260302-143022-def456
VERIFY: read_file(mode:"tail") to confirm file is complete.

For CRITICAL changes, the response includes a VERIFY instruction. The server does not block the edit; the rewrite guard (allow_rewrite) and OCC guard (--auto-occ=block) are the only things that can still block it.

For MEDIUM/HIGH/CRITICAL risk changes: no special workflow is needed. The edit proceeds automatically and creates a backup. If something looks wrong, use the backup ID from the response:

backup({ action: "undo_last", file_path: "main.go" })

For changes that trip the accidental-rewrite guard (very large new_text replacing a small anchor): override with allow_rewrite: true after reviewing.

edit_file({
path: "main.go",
old_text: "// old header",
new_text: "// new header...\n...many lines...",
allow_rewrite: true
})

For external-change protection, run the server with --auto-occ=block or chain edits using expected_hash from the previous response.


backup({
action: "list",
limit: 20, // Maximum to show
filter_operation: "edit", // edit, delete, batch, all
filter_path: "main.go", // Filter by file
newer_than_hours: 24 // Only last 24 hours
})

Response:

Available Backups (3)
20241203-153045-abc123
Time: 2024-12-03 15:30:45 (2 hours ago)
Operation: edit_file
Files: 1 (12.1KB)
Context: Edit: 12 occurrences, 35.2% change
20241203-140230-def456
Time: 2024-12-03 14:02:30 (3 hours ago)
Operation: batch_operations
Files: 47 (2.3MB)
Context: Batch rename: 47 files
Use backup({ action:"restore", backup_id }) to restore files
backup({
action: "info",
backup_id: "20241203-153045-abc123"
})

Response:

Backup Details: 20241203-153045-abc123
Timestamp: 2024-12-03 15:30:45 (2 hours ago)
Operation: edit_file
Context: Edit: 12 occurrences, 35.2% change
Total Size: 12.1KB
Files: 1
Files in backup:
- C:\project\main.go (12.1KB)
Backup Location: C:\Users\...\mcp-batch-backups\20241203-153045-abc123

Before restoring, see what changed:

backup({
action: "compare",
backup_id: "20241203-153045-abc123",
file_path: "C:\\project\\main.go"
})

Response:

=== Comparison for C:\project\main.go ===
Backup lines: 245
Current lines: 268
Difference: +23 lines
First differences:
Line 12:
- BACKUP: func oldName() {
+ CURRENT: func newName() {
Line 45:
- BACKUP: // TODO: implement
+ CURRENT: // DONE: implemented

First, use preview mode to see what will be restored:

backup({
action: "restore",
backup_id: "20241203-153045-abc123",
file_path: "C:\\project\\main.go",
preview: true
})

Response:

Preview Mode - Changes to be restored:
=== Comparison for C:\project\main.go ===
[shows the diff]

If the preview looks correct, proceed with restoration:

backup({
action: "restore",
backup_id: "20241203-153045-abc123",
file_path: "C:\\project\\main.go"
})

Response:

Restore completed successfully
Restored 1 file(s):
- C:\project\main.go
A backup of the current state was created before restoring

Note: A backup of the current state is created before restoring, giving you double protection.

Omit file_path to restore everything:

backup({
action: "restore",
backup_id: "20241203-140230-def456" // Backup with 47 files
})

Backups take disk space. Regularly clean up old ones.

First, see what would be deleted:

backup({
action: "cleanup",
older_than_days: 7,
dry_run: true
})

Response:

Dry Run Mode - Preview of cleanup operation
Would delete: 45 backup(s)
Would free: 120.5MB
Run with dry_run: false to actually delete backups

If you agree, execute the cleanup:

backup({
action: "cleanup",
older_than_days: 7,
dry_run: false
})

Response:

Cleanup completed
Deleted: 45 backup(s)
Freed: 120.5MB

The system automatically cleans up when:

  • backup_max_count is exceeded (default: 100)
  • Oldest backups are deleted first

Instead of restoring a file in one jump, walk the backup chain one step at a time. Every edit_file / multi_edit links its new backup to the previous one for the same file via PreviousBackupID. Use undo_last with a file_path to walk the chain backwards.

backup({
action: "undo_last",
file_path: "main.go",
preview: true
})

Returns a diff of what would be undone (without executing).

backup({
action: "undo_last",
file_path: "main.go"
})

Each undo_last undoes exactly one backup in the chain. Repeat until you reach the version you want. Without file_path, it undoes the most recent backup globally.

backup({
action: "undo_chain",
file_path: "main.go"
})

Returns the complete ordered list of backups for the file (oldest first), so the agent can decide which step to roll back to.


Soft-deletes (the default behaviour of delete_file without permanent:true) are now integrated with the backup system via the SD-ID flow. When --backup-dir is configured, the response of a soft-delete returns:

  • The SD-ID (e.g. sd-...)
  • The trash path (e.g. <backup-dir>/filesdelete/<sd-id>/<basename>)
  • A one-line restore command
backup({ action: "list_trash" })
backup({ action: "list_trash", filter_path: "main.go" })
backup({ action: "list_trash", older_than_days: 30, limit: 50 })
backup({ action: "restore_trash", sd_id: "sd-..." })
// Moves the file back to its original path
// Refuses if the original path is currently occupied (no silent overwrite)
// Dry-run first
backup({ action: "purge_trash", older_than_days: 30, dry_run: true })
// Real purge
backup({ action: "purge_trash", older_than_days: 30 })
backup({ action: "purge_trash", sd_id: "sd-..." })

The Dashboard Trash tab (v4.5.12+) shows the same entries through a web UI with View / Download / Restore / Purge per-row actions.


Situation: You need to change “func” to “function” throughout the file.

// 1. Analyze the impact
analyze_operation({
operation: "edit",
path: "main.go",
old_text: "func",
new_text: "function"
})
// 2. If safe, proceed
edit_file({
path: "main.go",
old_text: "func",
new_text: "function",
force: true // If risky
})
// 3. If something went wrong, restore
backup({
action: "restore",
backup_id: "20241203-153045-abc123",
file_path: "main.go"
})

Situation: You accidentally overwrote an important file.

// 1. List recent backups
backup({
action: "list",
newer_than_hours: 2,
filter_path: "important.go"
})
// 2. Find the correct backup
backup({
action: "info",
backup_id: "20241203-140230-def456"
})
// 3. Compare to be sure
backup({
action: "compare",
backup_id: "20241203-140230-def456",
file_path: "important.go"
})
// 4. Restore
backup({
action: "restore",
backup_id: "20241203-140230-def456",
file_path: "important.go"
})

Situation: You need to rename 50 files.

// 1. Batch with automatic backup
batch_operations({
operations: [
{type: "edit", path: "file1.go", old_text: "old", new_text: "new"},
// ... 49 more
],
atomic: true,
create_backup: true,
force: true // If analysis requires it
})
// 2. If something fails, rollback is automatic
// Or you can restore manually if needed

Situation: You want to see what changes were made today.

// List all backups from today
backup({
action: "list",
newer_than_hours: 24,
limit: 100
})
// Review each one
backup({
action: "info",
backup_id: "20241203-XXXXXX-XXXXXX"
})
// Compare with current state
backup({
action: "compare",
backup_id: "20241203-XXXXXX-XXXXXX",
file_path: "file.go"
})

Adjust risk validation sensitivity:

{
"args": [
"--risk-threshold-medium", "40.0",
"--risk-threshold-high", "60.0",
"--risk-occurrences-medium", "75",
"--risk-occurrences-high", "150"
]
}

Defaults (v4.5.26, per CLAUDE.md):

  • MEDIUM: ≥ 30% change or ≥ 50 occurrences
  • HIGH: ≥ 50% change or > 100 occurrences
  • CRITICAL: ≥ 90% change (no longer hard-blocked; auto-proceeds with backup)

Control how many backups to keep:

{
"args": [
"--backup-max-age", "14",
"--backup-max-count", "200"
]
}

Defaults:

  • Max age: 7 days
  • Max count: 100 backups

  • SHA256 hash of each file
  • Integrity verification on restore
  • JSON metadata for audit
  • Rollback if backup creation fails
  • Backup of current state before restoring
  • Descriptive error messages
  • Minimal overhead: approximately 5-10ms per small file
  • Intelligent cache: Metadata in memory (refresh every 5 min)
  • Non-blocking: Operations do not block the system

Before restoring, use preview mode:

backup({ action:"restore", backup_id: "...", file_path: "...", preview: true })

Establish a weekly routine:

backup({ action:"cleanup", older_than_days: 7, dry_run: false })

For massive edits, always use analyze_operation first.

Ensure it is in allowed paths for full access.


Not after success, but yes when:

  • You exceed backup_max_count
  • You run backup({ action:"cleanup" })

Yes, they are in the normal filesystem. You can copy, move them, etc.

What happens if I edit a file without backup?

Section titled “What happens if I edit a file without backup?”

The system always creates a backup before editing. It is automatic.

Not directly, but you can use --backup-max-age=0 to delete them immediately.

Yes, any file type is backed up.

The system will warn you, but it is better to clean up regularly with backup({ action:"cleanup" }).


Last updated: 2026-07-11 Version: 4.5.29