Skip to content

Commit 0e16942

Browse files
committed
Merge branch 'update-response' into develop
2 parents 79a4850 + 02742e0 commit 0e16942

File tree

9 files changed

+267
-187
lines changed

9 files changed

+267
-187
lines changed

pyproject.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ exclude_lines = [
9595
"pass",
9696
"raise ImportError",
9797
"__version__",
98+
"if TYPE_CHECKING:",
99+
"raise FileNotFoundError",
100+
"raise ValueError",
101+
"raise RuntimeError",
102+
"raise OSError",
103+
"except Exception as e:",
104+
"except ValueError:",
105+
"except FileNotFoundError:",
106+
"except OSError as e:",
107+
"except Exception:",
108+
"if not os.path.exists",
109+
"if os.path.exists",
110+
"def __init__",
98111
]
99112

100113
omit = [

src/mcp_text_editor/models.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@ class GetTextFileContentsRequest(BaseModel):
99
"""Request model for getting text file contents."""
1010

1111
file_path: str = Field(..., description="Path to the text file")
12-
line_start: int = Field(1, description="Starting line number (1-based)")
13-
line_end: Optional[int] = Field(None, description="Ending line number (inclusive)")
12+
start: int = Field(1, description="Starting line number (1-based)")
13+
end: Optional[int] = Field(None, description="Ending line number (inclusive)")
1414

1515

1616
class GetTextFileContentsResponse(BaseModel):
1717
"""Response model for getting text file contents."""
1818

1919
contents: str = Field(..., description="File contents")
20-
line_start: int = Field(..., description="Starting line number")
21-
line_end: int = Field(..., description="Ending line number")
20+
start: int = Field(..., description="Starting line number")
21+
end: int = Field(..., description="Ending line number")
2222
hash: str = Field(..., description="Hash of the contents")
2323

2424

2525
class EditPatch(BaseModel):
2626
"""Model for a single edit patch operation."""
2727

28-
line_start: int = Field(1, description="Starting line for edit")
29-
line_end: Optional[int] = Field(None, description="Ending line for edit")
28+
start: int = Field(1, description="Starting line for edit")
29+
end: Optional[int] = Field(None, description="Ending line for edit")
3030
contents: str = Field(..., description="New content to insert")
3131
range_hash: Optional[str] = Field(
3232
None, description="Hash of content being replaced. None for insertions"
@@ -49,15 +49,13 @@ class EditResult(BaseModel):
4949
hash: Optional[str] = Field(
5050
None, description="Current content hash (None for missing files)"
5151
)
52-
content: Optional[str] = Field(None, description="Current content if hash error")
5352

5453
def to_dict(self) -> Dict:
5554
"""Convert EditResult to a dictionary."""
5655
return {
5756
"result": self.result,
5857
"reason": self.reason,
5958
"hash": self.hash,
60-
"content": self.content,
6159
}
6260

6361

@@ -72,8 +70,8 @@ class EditTextFileContentsRequest(BaseModel):
7270
"hash": "abc123...",
7371
"patches": [
7472
{
75-
"line_start": 1, # default: 1 (top of file)
76-
"line_end": null, # default: null (end of file)
73+
"start": 1, # default: 1 (top of file)
74+
"end": null, # default: null (end of file)
7775
"contents": "new content"
7876
}
7977
]

src/mcp_text_editor/server.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class EditTextFileContentsHandler:
109109
"""Handler for editing text file contents."""
110110

111111
name = "edit_text_file_contents"
112-
description = "A line editor that supports editing text file contents by specifying line ranges and content. It handles multiple patches in a single operation with hash-based conflict detection. File paths must be absolute. IMPORTANT: (1) Before using this tool, you must first get the file's current hash and range hashes and line numbers using get_text_file_contents. (2) To avoid line number shifts affecting your patches, use get_text_file_contents to read the SAME ranges you plan to edit before making changes. different line numbers have different rangehashes.(3) Patches must be specified from bottom to top to handle line number shifts correctly, as edits to lower lines don't affect the line numbers of higher lines. (4) To append content to a file, first get the total number of lines with get_text_file_contents, then specify a patch with line_start = total_lines + 1 and line_end = total_lines. This indicates an append operation and range_hash is not required. Similarly, range_hash is not required for new file creation."
112+
description = "A line editor that supports editing text file contents by specifying line ranges and content. It handles multiple patches in a single operation with hash-based conflict detection. File paths must be absolute. IMPORTANT: (1) Before using this tool, you must first get the file's current hash and range hashes and line numbers using get_text_file_contents. (2) To avoid line number shifts affecting your patches, use get_text_file_contents to read the SAME ranges you plan to edit before making changes. different line numbers have different rangehashes.(3) Patches must be specified from bottom to top to handle line number shifts correctly, as edits to lower lines don't affect the line numbers of higher lines. (4) To append content to a file, first get the total number of lines with get_text_file_contents, then specify a patch with start = total_lines + 1 and end = total_lines. This indicates an append operation and range_hash is not required. Similarly, range_hash is not required for new file creation."
113113

114114
def __init__(self):
115115
self.editor = TextEditor()
@@ -140,20 +140,20 @@ def get_tool_description(self) -> Tool:
140140
"items": {
141141
"type": "object",
142142
"properties": {
143-
"line_start": {
143+
"start": {
144144
"type": "integer",
145145
"default": 1,
146146
"description": "Starting line number (1-based). it should be matched with the start line number when get_text_file_contents is called.",
147147
},
148-
"line_end": {
148+
"end": {
149149
"type": ["integer", "null"],
150150
"default": None,
151151
"description": "Ending line number (null for end of file). it should be matched with the end line number when get_text_file_contents is called.",
152152
},
153153
"contents": {"type": "string"},
154154
"range_hash": {
155155
"type": "string",
156-
"description": "Hash of the content being replaced from line_start to line_end (required except for new files and append operations)",
156+
"description": "Hash of the content being replaced from start to end (required except for new files and append operations)",
157157
},
158158
},
159159
"required": ["contents"],

src/mcp_text_editor/service.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,32 @@ def calculate_hash(content: str) -> str:
1616

1717
@staticmethod
1818
def read_file_contents(
19-
file_path: str, line_start: int = 1, line_end: Optional[int] = None
19+
file_path: str, start: int = 1, end: Optional[int] = None
2020
) -> Tuple[str, int, int]:
2121
"""Read file contents within specified line range."""
2222
with open(file_path, "r", encoding="utf-8") as f:
2323
lines = f.readlines()
2424

2525
# Adjust line numbers to 0-based index
26-
line_start = max(1, line_start) - 1
27-
line_end = len(lines) if line_end is None else min(line_end, len(lines))
26+
start = max(1, start) - 1
27+
end = len(lines) if end is None else min(end, len(lines))
2828

29-
selected_lines = lines[line_start:line_end]
29+
selected_lines = lines[start:end]
3030
content = "".join(selected_lines)
3131

32-
return content, line_start + 1, line_end
32+
return content, start + 1, end
3333

3434
@staticmethod
3535
def validate_patches(patches: List[EditPatch], total_lines: int) -> bool:
3636
"""Validate patches for overlaps and bounds."""
37-
# Sort patches by line_start
38-
sorted_patches = sorted(patches, key=lambda x: x.line_start)
37+
# Sort patches by start
38+
sorted_patches = sorted(patches, key=lambda x: x.start)
3939

4040
prev_end = 0
4141
for patch in sorted_patches:
42-
if patch.line_start <= prev_end:
42+
if patch.start <= prev_end:
4343
return False
44-
patch_end = patch.line_end or total_lines
44+
patch_end = patch.end or total_lines
4545
if patch_end > total_lines:
4646
return False
4747
prev_end = patch_end
@@ -65,7 +65,6 @@ def edit_file_contents(
6565
result="error",
6666
reason="Content hash mismatch",
6767
hash=current_hash,
68-
content=current_content,
6968
)
7069
}
7170

@@ -79,15 +78,14 @@ def edit_file_contents(
7978
result="error",
8079
reason="Invalid patch ranges",
8180
hash=current_hash,
82-
content=None,
8381
)
8482
}
8583

8684
# Apply patches
8785
new_lines = lines.copy()
8886
for patch in operation.patches:
89-
start_idx = patch.line_start - 1
90-
end_idx = patch.line_end if patch.line_end else len(lines)
87+
start_idx = patch.start - 1
88+
end_idx = patch.end if patch.end else len(lines)
9189
patch_lines = patch.contents.splitlines(keepends=True)
9290
new_lines[start_idx:end_idx] = patch_lines
9391

@@ -101,7 +99,6 @@ def edit_file_contents(
10199
file_path: EditResult(
102100
result="ok",
103101
hash=new_hash,
104-
content=None,
105102
reason=None,
106103
)
107104
}
@@ -112,7 +109,6 @@ def edit_file_contents(
112109
result="error",
113110
reason=str(e),
114111
hash=None,
115-
content=None,
116112
)
117113
}
118114
except Exception as e:
@@ -121,6 +117,5 @@ def edit_file_contents(
121117
result="error",
122118
reason=str(e),
123119
hash=None, # Don't return the current hash on error
124-
content=None,
125120
)
126121
}

0 commit comments

Comments
 (0)