Edit File Lines MCP Server について
Edit File Lines MCP Server は、許可されたディレクトリ内のテキストファイルを行単位で正確に編集する TypeScript 製の MCP サーバーです。編集内容は差分として事前確認でき、ステート ID で承認してから適用されるため、ファイルを変更するコーディングエージェントに適しています。
主な機能
- 01文字列または正規表現による行単位の置換
- 02差分を表示するドライラン
- 03期限付きステート ID による二段階の承認
- 04指定行と前後の文脈の確認
- 05許可ディレクトリへのアクセス制限
活用シーン
- 01コーディングエージェントによるソースコードの部分的な修正
- 02ファイル変更を適用する前の差分確認
- 03承認済みディレクトリ内の設定やコンポーネントの更新
MCP 対応
本プロジェクトは MCP サーバーであり、edit_file_lines、approve_edit、get_file_lines、search_file などのツールを MCP クライアントに公開します。操作は起動時に指定したディレクトリ内に限定されます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Edit File Lines 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,
};
使用例
- 文字列の単純な置換
{
"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 で使用します。
- 構造を維持した複数行のコンテンツ
{
"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 で使用します。
- 複雑な 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 で使用します。
- 空白を保持した設定の更新
{
"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 で使用します。
- 柔軟な空白のマッチング
{
"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 のドライランで行った変更を適用します。このツールでは、安全性のために2段階の編集プロセスを実行します。ワークフローの例を以下に示します。
- まず、ドライランで編集します。
{
"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 で使用します。
- 次に、状態 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>;
};
- 変更を確認します。
{
"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)
}
使用例:
- テキストの単純な検索:
{
"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,
- 大文字と小文字を区別した単語全体の検索:
{
"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 = ({
- 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>
一般的なワークフロー:
- 検索してから編集する:
// 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';"
}]
}
- すべての使用箇所を検索する:
{
"path": "src/components/App.tsx",
"pattern": "\\buseMemo\\b",
"type": "regex",
"contextLines": 2,
"maxMatches": 50
}
- 特定のプロパティパターンを検索する:
{
"path": "src/components/App.tsx",
"pattern": "className=['\"]([^'\"]+)['\"]",
"type": "regex",
"contextLines": 1
}
重要な注意事項
-
空白の処理
- このツールは、文字列と正規表現の両方のマッチングで空白を適切に処理します
- 置換後も元のインデントが維持されます
- トークン間の連続する空白は、マッチング時に正規化されます
-
パターンマッチング
- 文字列の一致(
strMatch)では空白が正規化されます - 正規表現パターン(
regexMatch)では先読みと後読みがサポートされています - 同じ編集で
strMatchとregexMatchの両方は使用できません - 重複する正規表現パターンは検出され、使用できないようになっています
- 文字列の一致(
-
ベストプラクティス
- 変更を確認するため、必ず最初にドライランを実行してください
- 変更を承認する前に差分の出力を確認してください
- 編集操作の対象を絞り、アトミックに行ってください
- 使用目的に適したパターンマッチングを使用してください
開発
依存関係をインストールします。
npm install
サーバーをビルドします。
npm run build
自動再ビルドを有効にして開発する場合:
npm run watch
テスト
テストスイートを実行します。
npm run test
追加のテストユーティリティ:
ツールテストスクリプト
サンプルファイルを使って、MCP ツールを直接テストします。
npm run test:tools
このスクリプトでは、以下を行います。
- テスト用フィクスチャを既知の状態にリセットします
- MCP サーバーに接続します
- 各ツールを順番にテストします。
get_file_linesedit_file_lines(ドライラン)approve_edit
- 各操作の出力を表示します
- 変更が正しく適用されたことを確認します
フィクスチャリセットスクリプト
テスト用フィクスチャを元の状態にリセットします。
npm run reset:fixtures
このスクリプトは、以下の用途に使用します。
- テスト前にテストファイルを既知の状態にリセットする
- 失敗したテスト後にクリーンアップする
- 一貫性のあるテスト環境を確保する
- 不足しているフィクスチャディレクトリを作成する
使い方
サーバーの起動時に、許可するディレクトリを1つ以上指定する必要があります。
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)
}
}
}
}
エラー処理
このツールは、一般的な問題に対して明確なエラーメッセージを表示します。
- 一致する項目が見つからない場合
Error: No string match found for "oldValue" on line 5
- 無効な正規表現
Error: Invalid regex pattern "([": Unterminated group
- 同じ行に対する複数の編集
Error: Line 5 is affected by multiple edits
セキュリティに関する考慮事項
- すべてのファイル操作は、明示的に許可されたディレクトリ内に制限されます
- 許可されたディレクトリの外へ移動できないよう、シンボリックリンクを検証します
- 親ディレクトリへの移動を防止します
- 一貫したセキュリティチェックのため、パスを正規化します
- 無効な行番号と文字位置は拒否されます
- 行末の正規化により、プラットフォーム間で一貫した動作を確保します
- セキュリティのため、編集状態は60秒後に失効します
- 編集の承認には、ファイルパスと編集内容の完全一致が必要です
デバッグ
ツールテストスクリプトを使って、サンプルファイルに対してMCPツールを直接テストしてください。MCP Inspectorも役立つ場合がありますが、現在、文字列以外の値の入力には対応していません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。