edit_file
Apply multiple text replacements to specified files using a list of edits, generate a unified diff to preview changes, and atomically update the file. Ensures compliance with directory restrictions and UTF-8 encoding.
Instructions
Apply multiple text replacements to a file and return a unified diff.
Args: path (str): File path to edit (absolute or relative to allowed directories) edits (List[Dict[str, str]]): List of edit operations, each with 'oldText' and 'newText' keys
Returns: str: Unified diff showing changes made, or error message if failed
Note: - Path must be within allowed directory roots - File must be a UTF-8 text file - Edits are applied sequentially in the order provided - Each 'oldText' must match exactly (first occurrence is replaced) - Returns unified diff format showing before/after changes - File is atomically updated using temporary file - If no changes made, returns 'No changes made.'
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| edits | Yes | ||
| path | Yes |
Implementation Reference
- main.py:570-620 (handler)The handler function that implements the 'edit_file' tool. It applies a list of text edits (replacements of oldText with newText, first occurrence only, sequentially) to a text file within allowed directories, generates a unified diff of changes, and atomically updates the file using a temporary file.def edit_file(path: str, edits: List[Dict[str, str]]) -> str: """Apply multiple text replacements to a file and return a unified diff. Args: path (str): File path to edit (absolute or relative to allowed directories) edits (List[Dict[str, str]]): List of edit operations, each with 'oldText' and 'newText' keys Returns: str: Unified diff showing changes made, or error message if failed Note: - Path must be within allowed directory roots - File must be a UTF-8 text file - Edits are applied sequentially in the order provided - Each 'oldText' must match exactly (first occurrence is replaced) - Returns unified diff format showing before/after changes - File is atomically updated using temporary file - If no changes made, returns 'No changes made.' """ try: rp = _resolve(path) if not _is_text(rp): return ( f"Error editing file: '{rp}' is not a UTF-8 text file or is binary" ) original = rp.read_text(encoding="utf-8") modified = original for i, edit in enumerate(edits): old = edit.get("oldText", "") new = edit.get("newText", "") if old not in modified: return f"Error editing file: could not find text to replace in edit {i + 1}. Make sure the text matches exactly:\n{old}" modified = modified.replace(old, new, 1) if modified == original: return "No changes made." diff = "\n".join( difflib.unified_diff( original.splitlines(), modified.splitlines(), fromfile=str(rp), tofile=str(rp), lineterm="", ) ) tmp = rp.with_suffix(rp.suffix + ".tmp") tmp.write_text(modified, encoding="utf-8") tmp.replace(rp) return diff except Exception as e:
- main.py:569-569 (registration)The @mcp.tool decorator registers the edit_file function as an MCP tool in the FastMCP server (conditionally under if not READ_ONLY_MODE).@mcp.tool
- main.py:570-570 (schema)Type annotations and docstring (above) define the input schema: path (str), edits (list of dicts with 'oldText' and 'newText'), returns str (diff or error). Used by FastMCP for tool schema.def edit_file(path: str, edits: List[Dict[str, str]]) -> str: