mcpField
E
Edit File Lines MCP Server

Edit File Lines MCP Server

本地服务
github.com/oakenai开发工具3497 周前更新

关于 Edit File Lines MCP Server

Edit File Lines MCP Server 是一个基于 TypeScript 的 MCP 服务,用于对允许目录内的文本文件进行精确的按行编辑。编辑前可预览差异并通过状态 ID 确认应用,适合需要安全修改文件的编码 Agent 场景。

主要功能

  1. 01按行号替换或修改文件内容
  2. 02支持字符串与正则匹配
  3. 03试运行预览差异后再应用
  4. 04通过状态 ID 批准编辑,状态会过期
  5. 05搜索与查看指定行及上下文

适用场景

  1. 01让编码 Agent 对源代码进行局部修改
  2. 02在应用前检查文件修改的差异
  3. 03在指定目录内批量修改配置或组件代码

MCP 相关

该项目是一个 MCP 服务,向 MCP 客户端提供 edit_file_lines、approve_edit、get_file_lines 和 search_file 等工具,操作范围限定在启动时指定的目录内。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

编辑文件行 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. 查找特定的 prop 模式:
{
  "path": "src/components/App.tsx",
  "pattern": "className=['\"]([^'\"]+)['\"]",
  "type": "regex",
  "contextLines": 1
}
重要说明
  1. 空白处理

    • 该工具会智能处理字符串匹配和正则表达式匹配中的空白
    • 替换时保留原始缩进
    • 匹配时会将 token 之间的多个空格规范化
  2. 模式匹配

    • 字符串匹配(strMatch)会进行空白规范化
    • 正则表达式模式(regexMatch)支持向前查找和向后查找
    • 同一次编辑中不能同时使用 strMatch 和 regexMatch
    • 会检测并阻止重叠的正则表达式模式
  3. 最佳实践

    • 始终先进行试运行以验证更改
    • 批准更改前检查差异输出
    • 保持编辑操作专注且原子化
    • 根据使用场景选择合适的模式匹配方式

开发

安装依赖项:

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 Inspector 或许有帮助,但目前不支持传入非字符串值。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。