MCP HubMCP Hub
oakenai

mcp-edit-file-lines

by: oakenai

MCP Server to make line-based edits to a file.

19created 08/12/2024
Visit
file
editing

📌Overview

Purpose: To offer a robust TypeScript-based MCP server for making precise line-based edits to text files in designated directories.

Overview: The framework enables developers to perform line-based modifications on text files through multiple sophisticated tools. It provides the ability to replace entire lines or specific text within lines, using both string matches and regex pattern matching. Dry run capabilities allow for previewing changes before applying them, ensuring accuracy and safety.

Key Features:

  • Line-Based Editing (edit_file_lines): Allows for precise modifications using string or regex pattern matching, enabling replacements of full lines or specific text in a way that preserves original formatting.

  • Approval Process (approve_edit): Implements a two-step process where changes can be reviewed before final application, enhancing safety and control over modifications.

  • File Inspection (get_file_lines): Enables users to view specific lines within files along with surrounding context, helping in verification before edits are made.

  • Search Functionality (search_file): Facilitates finding text or regex patterns within files, returning relevant line numbers and contextual content, which aids in targeting specific points for editing.


Edit File Lines MCP Server

A TypeScript-based MCP server providing tools to make precise line-based edits to text files within allowed directories.

Features

Main Editing Tool

edit_file_lines

Make line-based edits to a file using string or regex pattern matching. Each edit can:

  • Replace entire lines
  • Replace specific text matches while preserving line formatting
  • Use regex patterns for complex matches
  • Handle multiple lines and edits
  • Preview changes with dry run mode

Example file (src/components/App.tsx):

// Basic component with props
const Button = ({ color = "blue", size = "md" }) => {
  return <button className={`btn-${color} size-${size}`}>Click me</button>;
};

// Component with multiple props and nested structure
export const Card = ({
  title,
  subtitle = "Default subtitle",
  theme = "light",
  size = "lg",
}) => {
  const cardClass = `card-${theme} size-${size}`;
  
  return (
    <div className={cardClass}>
      <h2>{title}</h2>
      <p>{subtitle}</p>
    </div>
  );
};

// Constants and configurations
const THEME = {
  light: { bg: "#ffffff", text: "#000000" },
  dark: { bg: "#000000", text: "#ffffff" },
};

const CONFIG = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3,
};

Example Use Cases

  1. Simple String Replacement
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 2,
    "endLine": 2,
    "content": "primary",
    "strMatch": "blue"
  }],
  "dryRun": true
}

Output:

Index: src/components/App.tsx
===================================================================
--- src/components/App.tsx        original
+++ src/components/App.tsx        modified
@@ -1,6 +1,6 @@
 // Basic component with props
-const Button = ({ color = "blue", size = "md" }) => {
+const Button = ({ color = "primary", size = "md" }) => {
   return Click me;
 };
 
 // Component with multiple props and nested structure

State ID: fcbf740a
Use this ID with approve_edit to apply the changes.

  1. Multi-line Content with Preserved Structure
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 16,
    "endLine": 19,
    "content": "    <div className={cardClass}>\n      <h2 className=\"title\">{title}</h2>\n      <p className=\"subtitle\">{subtitle}</p>\n    </div>",
    "regexMatch": "<div[^>]*>[\\s\\S]*?</div>"
  }],
  "dryRun": true
}

Output:

Index: src/components/App.tsx
===================================================================
--- src/components/App.tsx        original
+++ src/components/App.tsx        modified
@@ -13,10 +13,10 @@
   const cardClass = `card-${theme} size-${size}`;
   
   return (
     <div className={cardClass}>
-      <h2>{title}</h2>
-      <p>{subtitle}</p>
+      <h2 className="title">{title}</h2>
+      <p className="subtitle">{subtitle}</p>
     </div>
   );
 };

State ID: f2ce973f
Use this ID with approve_edit to apply the changes.

  1. Complex JSX Structure Modification
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 7,
    "endLine": 12,
    "content": "export const Card = ({\n  title,\n  subtitle = \"New default\",\n  theme = \"modern\",\n  size = \"responsive\"\n}) => {",
    "regexMatch": "export const Card[\\s\\S]*?\\) => \\{"
  }],
  "dryRun": true
}

Output:

Index: src/components/App.tsx
===================================================================
--- src/components/App.tsx        original
+++ src/components/App.tsx        modified
@@ -5,11 +5,11 @@
 // Component with multiple props and nested structure
 export const Card = ({
   title,
-  subtitle = "Default subtitle",
-  theme = "light",
-  size = "lg",
+  subtitle = "New default",
+  theme = "modern",
+  size = "responsive"
 }) => {
   const cardClass = `card-${theme} size-${size}`;
   
   return (

State ID: f1f1d27b
Use this ID with approve_edit to apply the changes.

  1. Configuration Update with Whitespace Preservation
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 29,
    "endLine": 32,
    "content": "const CONFIG = {\n  baseUrl: \"https://api.newexample.com\",\n  timeout: 10000,\n  maxRetries: 5",
    "regexMatch": "const CONFIG[\\s\\S]*?retries: \\d+"
  }],
  "dryRun": true
}

Output:

Index: src/components/App.tsx
===================================================================
--- src/components/App.tsx        original
+++ src/components/App.tsx        modified
@@ -26,8 +26,8 @@
   dark: { bg: "#000000", text: "#ffffff" },
 };
 
 const CONFIG = {
-  apiUrl: "https://api.example.com",
-  timeout: 5000,
-  retries: 3,
+  baseUrl: "https://api.newexample.com",
+  timeout: 10000,
+  maxRetries: 5
 };

State ID: 20e93c34
Use this ID with approve_edit to apply the changes.

  1. Flexible Whitespace Matching
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 9,
    "endLine": 9,
    "content": "description",
    "strMatch": "subtitle   =   \"Default subtitle\""  // Extra spaces are handled
  }],
  "dryRun": true
}

Output:

Index: src/components/App.tsx
===================================================================
--- src/components/App.tsx        original
+++ src/components/App.tsx        modified
@@ -5,9 +5,9 @@
 // Component with multiple props and nested structure
 export const Card = ({
   title,
-  subtitle = "Default subtitle",
+  description
   theme = "light",
   size = "lg",
 }) => {
   const cardClass = `card-${theme} size-${size}`;

Additional Tools

approve_edit

Apply changes from a previous dry run of edit_file_lines. This two-step editing process helps ensure safety.

Example workflow:

  1. Make a dry run edit with edit_file_lines (see example above).

  2. Approve the changes using the state ID provided:

{
  "stateId": "fcbf740a"
}
  1. Verify the changes by inspecting the affected lines.

Note: State IDs expire after a short time for security.

get_file_lines

Inspect specific lines in a file with optional surrounding context to verify content before editing.

Example:

{
  "path": "src/components/App.tsx",
  "lineNumbers": [1, 2, 3],
  "context": 1
}

search_file

Search a file for text patterns or regular expressions to find specific line numbers and their surrounding context.

Features:

  • Simple text and regex search
  • Case sensitivity options
  • Whole word matching
  • Configurable context lines
  • Returns match positions and context

Arguments example:

{
  path: string;          // File path
  pattern: string;       // Search pattern (text or regex)
  type?: "text" | "regex"; // Default is "text"
  caseSensitive?: boolean; // Default is false
  contextLines?: number;   // Default is 2
  maxMatches?: number;     // Default is 100
  wholeWord?: boolean;     // Default is false
  multiline?: boolean;     // Default is false
}

Important Notes

  1. Whitespace Handling

    • Whitespace is intelligently handled in matches and replacements
    • Original indentation is preserved
    • Multiple spaces are normalized for matching
  2. Pattern Matching

    • String matches (strMatch) are whitespace-normalized
    • Regex patterns (regexMatch) support advanced constructs including look-ahead and look-behind
    • Cannot use both strMatch and regexMatch in the same edit
    • Overlapping regex patterns are prevented
  3. Best Practices

    • Always perform a dry run to verify changes
    • Review diff output before approval
    • Keep edits focused and atomic
    • Use appropriate pattern matching based on the use case

Development

Install dependencies:

npm install

Build the server:

npm run build

For development with auto-rebuild:

npm run watch

Testing

Run the test suite:

npm run test

Additional utilities:

  • Test Tools Script — test MCP tools directly against sample files:
npm run test:tools
  • Reset Fixtures Script — reset test fixtures to a known state:
npm run reset:fixtures

Usage

Start the server specifying allowed directories; all file ops are restricted to these for security:

node build/index.js <allowed-directory> [additional-directories...]

Environment Variables

  • MCP_EDIT_STATE_TTL: TTL in milliseconds for edit states (default 60000 ms). Edit states expire after this duration.

Installation

To use with Claude Desktop, configure the MCP server by adding:

MacOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%/Claude/claude_desktop_config.json

Example config:

{
  "mcpServers": {
    "edit-file-lines": {
      "command": "node",
      "args": [
        "/path/to/edit-file-lines/build/index.js",
        "<allowed-directory>"
      ],
      "env": {
        "MCP_EDIT_STATE_TTL": "300000"  // Optional TTL in milliseconds
      }
    }
  }
}

Error Handling

Common error messages:

  • Match Not Found
Error: No string match found for "oldValue" on line 5
  • Invalid Regex
Error: Invalid regex pattern "([": Unterminated group
  • Multiple Edits on Same Line
Error: Line 5 is affected by multiple edits

Security Considerations

  • Operations restricted to explicitly allowed directories
  • Symlink validation to prevent directory escapes
  • Parent directory traversal blocked
  • Path normalization for consistent security checks
  • Invalid line and character positions rejected
  • Line ending normalization for cross-platform consistency
  • Edit states expire after 60 seconds
  • Approvals require exact match of file path and edits

Debugging

Use the Test Tools script to test MCP tools on sample files. The MCP Inspector may also help but currently does not support non-string input.