edit_file
Modify text files by applying line-based changes to specified content, with options to preview edits before saving.
Instructions
Make line-based edits to a text file.
Args:
path: Path to the file
edits: List of {oldText, newText} dictionaries
encoding: Text encoding (default: utf-8)
dry_run: If True, return diff but don't modify file
ctx: MCP context
Returns:
Git-style diff showing changes
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| edits | Yes | ||
| encoding | No | utf-8 | |
| dry_run | No |
Implementation Reference
- mcp_filesystem/server.py:298-323 (registration)Registration and handler for the 'edit_file' MCP tool. This thin wrapper validates the call and delegates to the FileOperations.edit_file method.@mcp.tool() async def edit_file( path: str, edits: List[Dict[str, str]], ctx: Context, encoding: str = "utf-8", dry_run: bool = False, ) -> str: """Make line-based edits to a text file. Args: path: Path to the file edits: List of {oldText, newText} dictionaries encoding: Text encoding (default: utf-8) dry_run: If True, return diff but don't modify file ctx: MCP context Returns: Git-style diff showing changes """ try: components = get_components() return await components["operations"].edit_file(path, edits, encoding, dry_run) except Exception as e: return f"Error editing file: {str(e)}"
- mcp_filesystem/operations.py:494-573 (handler)Core handler logic for edit_file tool. Performs string-based find-and-replace edits on file content, generates git-style unified diff output, supports dry-run mode, and handles file I/O with path validation.async def edit_file( self, path: Union[str, Path], edits: List[Dict[str, str]], encoding: str = "utf-8", dry_run: bool = False, ) -> str: """Edit a text file by replacing line sequences. Args: path: Path to the file edits: List of {oldText, newText} dictionaries encoding: Text encoding (default: utf-8) dry_run: If True, return diff but don't modify file Returns: Git-style diff showing changes Raises: ValueError: If path is outside allowed directories FileNotFoundError: If file does not exist """ abs_path, allowed = await self.validator.validate_path(path) if not allowed: raise ValueError(f"Path outside allowed directories: {path}") try: # Read the file content current_content = await anyio.to_thread.run_sync( abs_path.read_text, encoding ) new_content = current_content # Create a diff-style output diff_lines = [f"--- {path}", f"+++ {path}"] # Apply each edit for edit in edits: if "oldText" not in edit or "newText" not in edit: continue old_text = edit["oldText"] new_text = edit["newText"] if old_text in new_content: # Add to diff context = new_content.split(old_text, 1) line_number = context[0].count("\n") + 1 old_lines = old_text.count("\n") + 1 new_lines = new_text.count("\n") + 1 diff_lines.append( f"@@ -{line_number},{old_lines} +{line_number},{new_lines} @@" ) for line in old_text.splitlines(): diff_lines.append(f"-{line}") for line in new_text.splitlines(): diff_lines.append(f"+{line}") # Replace the text new_content = new_content.replace(old_text, new_text, 1) # If this is not a dry run, write the changes if not dry_run and new_content != current_content: await anyio.to_thread.run_sync( abs_path.write_text, new_content, encoding ) if new_content == current_content: return "No changes made" return "\n".join(diff_lines) except FileNotFoundError: raise FileNotFoundError(f"File not found: {path}") except PermissionError: raise ValueError(f"Permission denied: {path}") except UnicodeDecodeError: raise ValueError(f"Cannot decode file as {encoding}: {path}")
- mcp_filesystem/server.py:299-305 (schema)Input schema defined by function signature: path (str), edits (List[Dict[str,str]] with oldText/newText), encoding (str), dry_run (bool). Output: str (diff).async def edit_file( path: str, edits: List[Dict[str, str]], ctx: Context, encoding: str = "utf-8", dry_run: bool = False, ) -> str: