Skip to content

Commit 63137b2

Browse files
committed
Merge branch 'feature/add-delete-text-file-operation' into develop
2 parents 6d917f5 + 6c5a5ff commit 63137b2

12 files changed

+2148
-4
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ typecheck:
2222
mypy src tests
2323

2424
# Run all checks required before pushing
25-
check: lint typecheck test
26-
fix: check format
25+
check: lint typecheck
26+
fix: format
2727
all: format check coverage

src/mcp_text_editor/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
"""MCP Text Editor Server package."""
22

33
import asyncio
4+
from typing import Any, Dict, List
45

56
from .server import main
7+
from .text_editor import TextEditor
8+
9+
# Create a global text editor instance
10+
_text_editor = TextEditor()
611

712

813
def run() -> None:
914
"""Run the MCP Text Editor Server."""
1015
asyncio.run(main())
16+
17+
18+
# Export functions
19+
async def get_text_file_contents(
20+
request: Dict[str, List[Dict[str, Any]]]
21+
) -> Dict[str, Any]:
22+
"""Get text file contents with line range specification."""
23+
return await _text_editor.read_multiple_ranges(
24+
ranges=request["files"],
25+
encoding="utf-8",
26+
)
27+
28+
29+
async def insert_text_file_contents(request: Dict[str, Any]) -> Dict[str, Any]:
30+
"""Insert text content before or after a specific line in a file."""
31+
return await _text_editor.insert_text_file_contents(
32+
file_path=request["path"],
33+
file_hash=request["file_hash"],
34+
after=request.get("after"),
35+
before=request.get("before"),
36+
contents=request["contents"],
37+
)

src/mcp_text_editor/models.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import Dict, List, Optional
44

5-
from pydantic import BaseModel, Field, model_validator
5+
from pydantic import BaseModel, Field, field_validator, model_validator
66

77

88
class GetTextFileContentsRequest(BaseModel):
@@ -120,3 +120,80 @@ class FileRanges(BaseModel):
120120
ranges: List[FileRange] = Field(
121121
..., description="List of line ranges to read from the file"
122122
)
123+
124+
125+
class InsertTextFileContentsRequest(BaseModel):
126+
"""Request model for inserting text into a file.
127+
128+
Example:
129+
{
130+
"path": "/path/to/file",
131+
"file_hash": "abc123...",
132+
"after": 5, # Insert after line 5
133+
"contents": "new content"
134+
}
135+
or
136+
{
137+
"path": "/path/to/file",
138+
"file_hash": "abc123...",
139+
"before": 5, # Insert before line 5
140+
"contents": "new content"
141+
}
142+
"""
143+
144+
path: str = Field(..., description="Path to the text file")
145+
file_hash: str = Field(..., description="Hash of original contents")
146+
after: Optional[int] = Field(
147+
None, description="Line number after which to insert content"
148+
)
149+
before: Optional[int] = Field(
150+
None, description="Line number before which to insert content"
151+
)
152+
contents: str = Field(..., description="Content to insert")
153+
154+
@model_validator(mode="after")
155+
def validate_position(self) -> "InsertTextFileContentsRequest":
156+
"""Validate that exactly one of after or before is specified."""
157+
if (self.after is None and self.before is None) or (
158+
self.after is not None and self.before is not None
159+
):
160+
raise ValueError("Exactly one of 'after' or 'before' must be specified")
161+
return self
162+
163+
@field_validator("after", "before")
164+
def validate_line_number(cls, v) -> Optional[int]:
165+
"""Validate that line numbers are positive."""
166+
if v is not None and v < 1:
167+
raise ValueError("Line numbers must be positive")
168+
return v
169+
170+
171+
class DeleteTextFileContentsRequest(BaseModel):
172+
"""Request model for deleting text from a file.
173+
174+
Example:
175+
{
176+
"path": "/path/to/file",
177+
"file_hash": "abc123...",
178+
"ranges": [
179+
{
180+
"start": 5,
181+
"end": 10,
182+
"range_hash": "def456..."
183+
}
184+
]
185+
}
186+
"""
187+
188+
path: str = Field(..., description="Path to the text file")
189+
file_hash: str = Field(..., description="Hash of original contents")
190+
ranges: List[FileRange] = Field(..., description="List of ranges to delete")
191+
192+
@field_validator("range_hash")
193+
def validate_range_hash(cls, v: str) -> str:
194+
"""Validate that range_hash is not empty."""
195+
if not v:
196+
raise ValueError("range_hash cannot be empty")
197+
return v
198+
199+
range_hash: str = Field(..., description="Hash of the content to be deleted")

0 commit comments

Comments
 (0)