Skip to main content
Glama
oakenai

Edit File Lines MCP Server

by oakenai

编辑文件行 MCP 服务器

基于 TypeScript 的 MCP 服务器,提供对允许目录内的文本文件进行精确的基于行的编辑的工具。

特征

主要编辑工具

edit_file_lines

使用字符串或正则表达式模式匹配对文件进行基于行的编辑。每次编辑可以:

  • 替换整行

  • 替换特定的文本匹配,同时保留行格式

  • 使用正则表达式模式进行复杂匹配

  • 处理多行和多个编辑

  • 使用试运行模式预览更改

示例文件( 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,
};

示例用例

  1. 简单字符串替换

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

输出:

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:fcbf740a 使用此 ID 和 approve_edit 来应用更改。

  1. 保留结构的多行内容

{
  "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
}

输出:

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:f2ce973f 使用此 ID 和 approve_edit 来应用更改。

  1. 复杂的 JSX 结构修改

{
  "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
}

输出:

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:f1f1d27b 使用此 ID 和 approve_edit 来应用更改。

  1. 保留空白的配置更新

{
  "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
}

输出:

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:20e93c34 使用此 ID 和 approve_edit 来应用更改。

  1. 灵活的空白匹配

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

输出:

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

其他工具

approve_edit

应用上一次edit_file_lines试运行中的更改。此工具提供了两步编辑流程以确保安全。以下是示例工作流程:

  1. 首先,进行一次试运行编辑:

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

输出:

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:fcbf740a 使用此 ID 和 approve_edit 来应用更改。

  1. 然后,使用状态 ID 批准更改:

{
  "stateId": "fcbf740a"
}

输出:

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. 验证更改:

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

输出:

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

请注意,出于安全考虑,州 ID 会在短时间内过期。尝试使用过期或无效的州 ID 将导致错误:

{
  "stateId": "invalid123"
}

输出:

Error: Invalid or expired state ID

get_file_lines

使用可选的上下文行检查文件中的特定行。此工具可用于在编辑前验证行内容。

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

输出:

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

在文件中搜索文本模式或正则表达式,以查找特定的行号及其上下文。此工具对于使用edit_file_lines定位要编辑的行尤其有用。

特征:

  • 带有可选区分大小写的简单文本搜索

  • 正则表达式支持

  • 全词匹配

  • 可配置的上下文行

  • 返回行号、内容以及带有行号的周围上下文

参数:

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

示例用例:

  1. 简单文本搜索:

{
  "path": "src/components/App.tsx",
  "pattern": "const",
  "contextLines": 2
}

输出:

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. 区分大小写的全词搜索:

{
  "path": "src/components/App.tsx",
  "pattern": "props",
  "caseSensitive": true,
  "wholeWord": true,
  "contextLines": 1
}

输出:

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. 查找 JSX 组件:

{
  "path": "src/components/App.tsx",
  "pattern": "<[A-Z]\\w+\\s",
  "type": "regex",
  "contextLines": 1
}

输出:

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>

常见工作流程:

  1. 查找然后编辑:

// 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. 查找所有用法:

{
  "path": "src/components/App.tsx",
  "pattern": "\\buseMemo\\b",
  "type": "regex",
  "contextLines": 2,
  "maxMatches": 50
}
  1. 查找特定的道具图案:

{
  "path": "src/components/App.tsx",
  "pattern": "className=['\"]([^'\"]+)['\"]",
  "type": "regex",
  "contextLines": 1
}

重要提示

  1. 空格处理

    • 该工具可以智能地处理字符串和正则表达式匹配中的空格

    • 替换时保留原始缩进

    • 标记之间的多个空格被规范化以便匹配

  2. 模式匹配

    • 字符串匹配( strMatch )是空白规范化的

    • 正则表达式模式( regexMatch )支持前瞻和后瞻

    • 不能在同一编辑中同时使用strMatchregexMatch

    • 检测并阻止重叠的正则表达式模式

  3. 最佳实践

    • 始终先使用试运行来验证更改

    • 在批准更改之前检查差异输出

    • 保持编辑操作的集中性和原子性

    • 针对您的用例使用适当的模式匹配

Related MCP server: MCP Server

发展

安装依赖项:

npm install

构建服务器:

npm run build

对于使用自动重建的开发:

npm run watch

测试

运行测试套件:

npm run test

附加测试实用程序:

测试工具脚本

直接针对示例文件测试 MCP 工具:

npm run test:tools

此脚本:

  • 将测试装置重置为已知状态

  • 连接到 MCP 服务器

  • 按顺序测试每个工具:

    • get_file_lines

    • edit_file_lines (试运行)

    • approve_edit

  • 显示每个操作的输出

  • 验证更改是否正确应用

重置赛程脚本

将测试装置重置为原始状态:

npm run reset:fixtures

使用此脚本可以:

  • 在测试之前将测试文件重置为已知状态

  • 测试失败后清理

  • 确保一致的测试环境

  • 创建缺失的夹具目录

用法

服务器启动时需要指定一个或多个允许的目录:

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

为了安全起见,所有文件操作都将限制在这些目录中。

环境变量

  • MCP_EDIT_STATE_TTL :编辑状态的生存时间(毫秒)(默认值:60000)。编辑状态在此持续时间后将过期,必须重新创建。

安装

要与 Claude Desktop 一起使用,请添加服务器配置:

在 MacOS 上: ~/Library/Application Support/Claude/claude_desktop_config.json在 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)
      }
    }
  }
}

错误处理

该工具针对常见问题提供了清晰的错误消息:

  1. 未找到匹配项

Error: No string match found for "oldValue" on line 5
  1. 无效的正则表达式

Error: Invalid regex pattern "([": Unterminated group
  1. 同一行上的多个编辑

Error: Line 5 is affected by multiple edits

安全注意事项

  • 所有文件操作都限制在明确允许的目录中

  • 验证符号链接以防止转义允许的目录

  • 阻止父目录遍历

  • 执行路径规范化以进行一致的安全检查

  • 无效的行号和字符位置将被拒绝

  • 行尾规范化确保跨平台行为一致

  • 出于安全考虑,编辑状态将在 60 秒后过期

  • 编辑批准需要文件路径和编辑完全匹配

调试

使用测试工具脚本直接针对示例文件测试 MCP 工具。MCP检查器可能会有所帮助,但目前不支持处理非字符串值的输入。

-
security - not tested
A
license - permissive license
-
quality - not tested

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/oakenai/mcp-edit-file-lines'

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