Edit File Lines MCP Server

Realiza ediciones precisas basadas en líneas en archivos de texto dentro de directorios permitidos.

Documentación

Servidor MCP de Edición de Líneas de Archivos

Un servidor MCP basado en TypeScript que proporciona herramientas para realizar ediciones precisas basadas en líneas en archivos de texto dentro de directorios permitidos.

Características

Herramienta Principal de Edición

edit_file_lines

Realiza ediciones basadas en líneas en un archivo utilizando coincidencia de patrones de cadena o expresiones regulares. Cada edición puede:

  • Reemplazar líneas completas
  • Reemplazar coincidencias de texto específicas preservando el formato de la línea
  • Utilizar patrones de expresiones regulares para coincidencias complejas
  • Manejar múltiples líneas y múltiples ediciones
  • Previsualizar cambios con modo de ejecución en seco

Archivo de ejemplo (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,
};

Casos de Uso de Ejemplo

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

Salida:

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

ID de Estado: fcbf740a Utilice este ID con approve_edit para aplicar los cambios.

  1. Contenido Multilínea con Estructura Preservada
{
  "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
}

Salida:

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>
   );
 };

ID de Estado: f2ce973f Utilice este ID con approve_edit para aplicar los cambios.

  1. Modificación de Estructura JSX Compleja
{
  "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
}

Salida:

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 (

ID de Estado: f1f1d27b Utilice este ID con approve_edit para aplicar los cambios.

  1. Actualización de Configuración con Preservación de Espacios en Blanco
{
  "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
}

Salida:

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
 };

ID de Estado: 20e93c34 Utilice este ID con approve_edit para aplicar los cambios.

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

Salida:

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}`;

Herramientas Adicionales

approve_edit

Aplica cambios de una ejecución en seco previa de edit_file_lines. Esta herramienta proporciona un proceso de edición en dos pasos por seguridad. Aquí hay un flujo de trabajo de ejemplo:

  1. Primero, realice una edición en seco:
{
  "p": "src/components/App.tsx",
  "e": [{
    "startLine": 2,
    "endLine": 2,
    "content": "primary",
    "strMatch": "blue"
  }],
  "dryRun": true
}

Salida:

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 <button className={`btn-${color} size-${size}`}>Click me</button>;
 };

ID de Estado: fcbf740a Utilice este ID con approve_edit para aplicar los cambios.

  1. Luego, apruebe los cambios utilizando el ID de estado:
{
  "stateId": "fcbf740a"
}

Salida:

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 <button className={`btn-${color} size-${size}`}>Click me</button>;
 };
  1. Verifique los cambios:
{
  "path": "src/components/App.tsx",
  "lineNumbers": [2],
  "context": 1
}

Salida:

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

Tenga en cuenta que los ID de estado expiran después de un corto período por seguridad. Intentar utilizar un ID de estado expirado o inválido resultará en un error:

{
  "stateId": "invalid123"
}

Salida:

Error: Invalid or expired state ID

get_file_lines

Inspeccione líneas específicas en un archivo con líneas de contexto opcionales. Esta herramienta es útil para verificar el contenido de las líneas antes de realizar ediciones.

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

Salida:

Line 1:
> 1: // Basic component with props
  2: const Button = ({ color = "blue", size = "md" }) => {

Line 2:
  1: // Basic component with props
> 2: const Button = ({ color = "blue", size = "md" }) => {
  3:   return Click me;

Line 3:
  2: const Button = ({ color = "blue", size = "md" }) => {
> 3:   return Click me;
  4: };

search_file

Busque en un archivo patrones de texto o expresiones regulares para encontrar números de línea específicos y su contexto circundante. Esta herramienta es particularmente útil para localizar las líneas exactas que desea editar con edit_file_lines.

Características:

  • Búsqueda de texto simple con sensibilidad a mayúsculas opcional
  • Soporte de expresiones regulares
  • Coincidencia de palabras completas
  • Líneas de contexto configurables
  • Devuelve números de línea, contenido y contexto circundante con números de línea

Argumentos:

{
  path: string;          // Path to the file to search
  pattern: string;       // Search pattern (text or regex)
  type?: "text" | "regex"; // Type of search (default: "text")
  caseSensitive?: boolean; // Case-sensitive search (default: false)
  contextLines?: number;   // Number of context lines (default: 2, max: 10)
  maxMatches?: number;     // Maximum matches to return (default: 100)
  wholeWord?: boolean;     // Match whole words only (default: false)
  multiline?: boolean;     // Enable multiline regex mode (default: false)
}

Casos de uso de ejemplo:

  1. Búsqueda de texto simple:
{
  "path": "src/components/App.tsx",
  "pattern": "const",
  "contextLines": 2
}

Salida:

Found 6 matches in 0.9ms:
File size: 0.7KB

Match 1: Line 2, Column 1
----------------------------------------
     1 | // Basic component with props
>    2 | const Button = ({ color = "blue", size = "md" }) => {
     3 |   return <button className={`btn-${color} size-${size}`}>Click me</button>;
     4 | };

Match 2: Line 7, Column 8
----------------------------------------
     5 | 
     6 | // Component with multiple props and nested structure
>    7 | export const Card = ({
     8 |   title,
     9 |   subtitle = "Default subtitle",

Match 3: Line 13, Column 3
----------------------------------------
    11 |   size = "lg",
    12 | }) => {
>   13 |   const cardClass = `card-${theme} size-${size}`;
    14 |   
    15 |   return (

Match 4: Line 23, Column 4
----------------------------------------
    21 | };
    22 | 
>   23 | // Constants and configurations
    24 | const THEME = {
    25 |   light: { bg: "#ffffff", text: "#000000" },

Match 5: Line 24, Column 1
----------------------------------------
    22 | 
    23 | // Constants and configurations
>   24 | const THEME = {
    25 |   light: { bg: "#ffffff", text: "#000000" },
    26 |   dark: { bg: "#000000", text: "#ffffff" },

Match 6: Line 29, Column 1
----------------------------------------
    27 | };
    28 | 
>   29 | const CONFIG = {
    30 |   apiUrl: "https://api.example.com",
    31 |   timeout: 5000,
  1. Búsqueda de palabras completas sensible a mayúsculas:
{
  "path": "src/components/App.tsx",
  "pattern": "props",
  "caseSensitive": true,
  "wholeWord": true,
  "contextLines": 1
}

Salida:

Found 2 matches in 0.7ms:
File size: 0.7KB

Match 1: Line 1, Column 25
----------------------------------------
>    1 | // Basic component with props
     2 | const Button = ({ color = "blue", size = "md" }) => {

Match 2: Line 6, Column 28
----------------------------------------
     5 | 
>    6 | // Component with multiple props and nested structure
     7 | export const Card = ({
  1. Encontrar componentes JSX:
{
  "path": "src/components/App.tsx",
  "pattern": "<[A-Z]\\w+\\s",
  "type": "regex",
  "contextLines": 1
}

Salida:

Found 2 matches in 0.6ms:
File size: 0.7KB

Match 1: Line 3, Column 10
----------------------------------------
     2 | const Button = ({ color = "blue", size = "md" }) => {
>    3 |   return <button className={`btn-${color} size-${size}`}>Click me</button>;
     4 | };

Match 2: Line 16, Column 5
----------------------------------------
    15 |   return (
>   16 |     <div className={cardClass}>
    17 |       <h2>{title}</h2>

Flujos de trabajo comunes:

  1. Encontrar y luego editar:
// First, search for the line
{
  "path": "src/config.ts",
  "pattern": "API_URL",
  "wholeWord": true
}

// Then use the returned line number in edit_file_lines
{
  "p": "src/config.ts",
  "e": [{
    "startLine": 23,  // Line number from search result
    "endLine": 23,
    "content": "export const API_URL = 'https://new-api.example.com';"
  }]
}
  1. Encontrar todos los usos:
{
  "path": "src/components/App.tsx",
  "pattern": "\\buseMemo\\b",
  "type": "regex",
  "contextLines": 2,
  "maxMatches": 50
}
  1. Encontrar patrones de props específicos:
{
  "path": "src/components/App.tsx",
  "pattern": "className=['\"]([^'\"]+)['\"]",
  "type": "regex",
  "contextLines": 1
}

Notas Importantes

  1. Manejo de Espacios en Blanco

    • La herramienta maneja inteligentemente los espacios en blanco tanto en coincidencias de cadenas como de expresiones regulares
    • La indentación original se preserva en los reemplazos
    • Los espacios múltiples entre tokens se normalizan para la coincidencia
  2. Coincidencia de Patrones

    • Las coincidencias de cadenas (strMatch) se normalizan en espacios en blanco
    • Los patrones de expresiones regulares (regexMatch) soportan look-ahead y look-behind
    • No se pueden usar ambos strMatch y regexMatch en la misma edición
    • Los patrones de expresiones regulares superpuestos se detectan y previenen
  3. Mejores Prácticas

    • Siempre use la ejecución en seco primero para verificar los cambios
    • Revise la salida de diff antes de aprobar los cambios
    • Mantenga las operaciones de edición enfocadas y atómicas
    • Utilice la coincidencia de patrones apropiada para su caso de uso

Desarrollo

Instalar dependencias:

npm install

Compilar el servidor:

npm run build

Para desarrollo con reconstrucción automática:

npm run watch

Pruebas

Ejecutar la suite de pruebas:

npm run test

Utilidades de prueba adicionales:

Script de Herramientas de Prueba

Pruebe las herramientas MCP directamente contra archivos de muestra:

npm run test:tools

Este script:

  • Restablece los fixtures de prueba a un estado conocido
  • Se conecta al servidor MCP
  • Prueba cada herramienta en secuencia:
    • get_file_lines
    • edit_file_lines (ejecución en seco)
    • approve_edit
  • Muestra la salida de cada operación
  • Verifica que los cambios se aplicaron correctamente

Script de Restablecimiento de Fixtures

Restablezca los fixtures de prueba a su estado original:

npm run reset:fixtures

Utilice este script para:

  • Restablecer archivos de prueba a un estado conocido antes de probar
  • Limpiar después de pruebas fallidas
  • Asegurar un entorno de prueba consistente
  • Crear directorios de fixtures faltantes

Uso

El servidor requiere uno o más directorios permitidos para ser especificados al iniciar:

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

Todas las operaciones de archivos estarán restringidas a estos directorios por seguridad.

Variables de Entorno

  • MCP_EDIT_STATE_TTL: Tiempo de vida en milisegundos para los estados de edición (predeterminado: 60000). Los estados de edición expirarán después de esta duración y deberán recrearse.

Instalación

Para usar con Claude Desktop, agregue la configuración del servidor:

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

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

Manejo de Errores

La herramienta proporciona mensajes de error claros para problemas comunes:

  1. Coincidencia No Encontrada
Error: No string match found for "oldValue" on line 5
  1. Expresión Regular Inválida
Error: Invalid regex pattern "([": Unterminated group
  1. Múltiples Ediciones en la Misma Línea
Error: Line 5 is affected by multiple edits

Consideraciones de Seguridad

  • Todas las operaciones de archivos están restringidas a directorios explícitamente permitidos
  • Los enlaces simbólicos se validan para prevenir escapar de los directorios permitidos
  • Se previene la navegación al directorio padre
  • La normalización de rutas se realiza para verificaciones de seguridad consistentes
  • Los números de línea y posiciones de caracteres inválidos se rechazan
  • La normalización de finales de línea asegura un comportamiento consistente entre plataformas
  • Los estados de edición expiran después de 60 segundos por seguridad
  • Las aprobaciones de edición requieren coincidencia exacta de la ruta del archivo y las ediciones

Depuración

Utilice el script de Herramientas de Prueba para probar las herramientas MCP directamente contra archivos de muestra. El Inspector MCP podría ayudar, pero actualmente no soporta manejar entradas que no sean valores de cadena.