@@ -416,11 +416,117 @@ async def run_tool(self, arguments: Dict[str, Any]) -> Sequence[TextContent]:
416
416
raise RuntimeError (f"Error processing request: { str (e )} " ) from e
417
417
418
418
419
+ class DeleteTextFileContentsHandler :
420
+ """Handler for deleting content from a text file."""
421
+
422
+ name = "delete_text_file_contents"
423
+ description = "Delete specified content ranges from a text file. The file must exist. File paths must be absolute."
424
+
425
+ def __init__ (self ):
426
+ self .editor = TextEditor ()
427
+
428
+ def get_tool_description (self ) -> Tool :
429
+ """Get the tool description."""
430
+ return Tool (
431
+ name = self .name ,
432
+ description = self .description ,
433
+ inputSchema = {
434
+ "type" : "object" ,
435
+ "properties" : {
436
+ "file_path" : {
437
+ "type" : "string" ,
438
+ "description" : "Path to the text file. File path must be absolute." ,
439
+ },
440
+ "file_hash" : {
441
+ "type" : "string" ,
442
+ "description" : "Hash of the file contents for concurrency control" ,
443
+ },
444
+ "ranges" : {
445
+ "type" : "array" ,
446
+ "description" : "List of line ranges to delete" ,
447
+ "items" : {
448
+ "type" : "object" ,
449
+ "properties" : {
450
+ "start" : {
451
+ "type" : "integer" ,
452
+ "description" : "Starting line number (1-based)" ,
453
+ },
454
+ "end" : {
455
+ "type" : ["integer" , "null" ],
456
+ "description" : "Ending line number (null for end of file)" ,
457
+ },
458
+ "range_hash" : {
459
+ "type" : "string" ,
460
+ "description" : "Hash of the content being deleted" ,
461
+ },
462
+ },
463
+ "required" : ["start" , "range_hash" ],
464
+ },
465
+ },
466
+ "encoding" : {
467
+ "type" : "string" ,
468
+ "description" : "Text encoding (default: 'utf-8')" ,
469
+ "default" : "utf-8" ,
470
+ },
471
+ },
472
+ "required" : ["file_path" , "file_hash" , "ranges" ],
473
+ },
474
+ )
475
+
476
+ async def run_tool (self , arguments : Dict [str , Any ]) -> Sequence [TextContent ]:
477
+ """Execute the tool with given arguments."""
478
+ try :
479
+ # Input validation
480
+ if "file_path" not in arguments :
481
+ raise RuntimeError ("Missing required argument: file_path" )
482
+ if "file_hash" not in arguments :
483
+ raise RuntimeError ("Missing required argument: file_hash" )
484
+ if "ranges" not in arguments :
485
+ raise RuntimeError ("Missing required argument: ranges" )
486
+
487
+ file_path = arguments ["file_path" ]
488
+ if not os .path .isabs (file_path ):
489
+ raise RuntimeError (f"File path must be absolute: { file_path } " )
490
+
491
+ # Check if file exists
492
+ if not os .path .exists (file_path ):
493
+ raise RuntimeError (f"File does not exist: { file_path } " )
494
+
495
+ encoding = arguments .get ("encoding" , "utf-8" )
496
+
497
+ # Create patches for deletion (replacing content with empty string)
498
+ patches = [
499
+ {
500
+ "start" : r ["start" ],
501
+ "end" : r ["end" ],
502
+ "contents" : "" ,
503
+ "range_hash" : r ["range_hash" ],
504
+ }
505
+ for r in arguments ["ranges" ]
506
+ ]
507
+
508
+ # Use the existing edit_file_contents method
509
+ result = await self .editor .edit_file_contents (
510
+ file_path ,
511
+ expected_hash = arguments ["file_hash" ],
512
+ patches = patches ,
513
+ encoding = encoding ,
514
+ )
515
+
516
+ return [TextContent (type = "text" , text = json .dumps (result , indent = 2 ))]
517
+
518
+ except Exception as e :
519
+ logger .error (f"Error processing request: { str (e )} " )
520
+ logger .error (traceback .format_exc ())
521
+ raise RuntimeError (f"Error processing request: { str (e )} " ) from e
522
+
523
+
419
524
# Initialize tool handlers
420
525
get_contents_handler = GetTextFileContentsHandler ()
421
526
edit_contents_handler = EditTextFileContentsHandler ()
422
527
create_file_handler = CreateTextFileHandler ()
423
528
append_file_handler = AppendTextFileContentsHandler ()
529
+ delete_contents_handler = DeleteTextFileContentsHandler ()
424
530
425
531
426
532
@app .list_tools ()
@@ -431,6 +537,7 @@ async def list_tools() -> List[Tool]:
431
537
edit_contents_handler .get_tool_description (),
432
538
create_file_handler .get_tool_description (),
433
539
append_file_handler .get_tool_description (),
540
+ delete_contents_handler .get_tool_description (),
434
541
]
435
542
436
543
@@ -447,6 +554,8 @@ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
447
554
return await create_file_handler .run_tool (arguments )
448
555
elif name == append_file_handler .name :
449
556
return await append_file_handler .run_tool (arguments )
557
+ elif name == delete_contents_handler .name :
558
+ return await delete_contents_handler .run_tool (arguments )
450
559
else :
451
560
raise ValueError (f"Unknown tool: { name } " )
452
561
except ValueError :
0 commit comments