MCP 서버 파일 줄 편집
허용된 디렉터리 내에서 텍스트 파일에 대한 정확한 줄 기반 편집을 위한 도구를 제공하는 TypeScript 기반 MCP 서버입니다.
특징
주요 편집 도구
edit_file_lines
문자열 또는 정규식 패턴 매칭을 사용하여 파일을 줄 단위로 편집할 수 있습니다. 각 편집은 다음을 수행할 수 있습니다.
- 전체 줄 바꾸기
- 줄 서식을 유지하면서 특정 텍스트 일치 항목 바꾸기
- 복잡한 일치에 정규식 패턴 사용
- 여러 줄과 여러 편집을 처리합니다
- 드라이런 모드로 변경 사항 미리 보기
예제 파일( src/components/App.tsx
):
지엑스피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과 함께 사용하여 변경 사항을 적용합니다.
- 구조가 보존된 다중 줄 콘텐츠
{
"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
모두 사용할 수 없습니다. - 중복되는 정규식 패턴이 감지되어 방지됩니다.
- 모범 사례
- 변경 사항을 확인하려면 항상 먼저 연습을 실행하세요.
- 변경 사항을 승인하기 전에 diff 출력을 검토하세요.
- 편집 작업을 집중적이고 원자적으로 유지하세요
- 사용 사례에 적합한 패턴 매칭을 사용하세요
개발
종속성 설치:
서버를 빌드하세요:
자동 재빌드를 사용한 개발의 경우:
테스트
테스트 모음을 실행합니다.
추가 테스트 유틸리티:
테스트 도구 스크립트
샘플 파일에 대해 MCP 도구를 직접 테스트해 보세요.
이 스크립트:
- 테스트 고정물을 알려진 상태로 재설정합니다.
- MCP 서버에 연결합니다
- 각 도구를 순서대로 테스트합니다.
get_file_lines
edit_file_lines
(드라이런)approve_edit
- 각 작업의 출력을 보여줍니다
- 변경 사항이 올바르게 적용되었는지 확인합니다.
경기 일정 재설정 스크립트
테스트 픽스처를 원래 상태로 재설정합니다.
이 스크립트를 사용하여 다음을 수행할 수 있습니다.
- 테스트하기 전에 테스트 파일을 알려진 상태로 재설정합니다.
- 테스트 실패 후 정리
- 일관된 테스트 환경 보장
- 누락된 고정 장치 디렉토리 만들기
용법
서버를 시작할 때 하나 이상의 허용된 디렉토리를 지정해야 합니다.
node build/index.js <allowed-directory> [additional-directories...]
보안을 위해 모든 파일 작업은 이러한 디렉토리로 제한됩니다.
환경 변수
MCP_EDIT_STATE_TTL
: 편집 상태의 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 검사기가 도움이 될 수도 있지만, 현재 문자열 값이 아닌 입력은 처리할 수 없습니다.