Skip to main content
Glama
safurrier

MCP Filesystem Server

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
NameRequiredDescriptionDefault
pathYes
editsYes
encodingNoutf-8
dry_runNo

Implementation Reference

  • 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)}"
  • 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}")
  • 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:

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/safurrier/mcp-filesystem'

If you have feedback or need assistance with the MCP directory API, please join our Discord server