Skip to main content
Glama

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