@@ -521,12 +521,105 @@ async def run_tool(self, arguments: Dict[str, Any]) -> Sequence[TextContent]:
521
521
raise RuntimeError (f"Error processing request: { str (e )} " ) from e
522
522
523
523
524
+ class InsertTextFileContentsHandler :
525
+ """Handler for inserting content before or after a specific line in a text file."""
526
+
527
+ name = "insert_text_file_contents"
528
+ description = "Insert content before or after a specific line in a text file. Uses hash-based validation for concurrency control."
529
+
530
+ def __init__ (self ):
531
+ self .editor = TextEditor ()
532
+
533
+ def get_tool_description (self ) -> Tool :
534
+ """Get the tool description."""
535
+ return Tool (
536
+ name = self .name ,
537
+ description = self .description ,
538
+ inputSchema = {
539
+ "type" : "object" ,
540
+ "properties" : {
541
+ "path" : {
542
+ "type" : "string" ,
543
+ "description" : "Path to the text file. File path must be absolute." ,
544
+ },
545
+ "file_hash" : {
546
+ "type" : "string" ,
547
+ "description" : "Hash of the file contents for concurrency control" ,
548
+ },
549
+ "contents" : {
550
+ "type" : "string" ,
551
+ "description" : "Content to insert" ,
552
+ },
553
+ "before" : {
554
+ "type" : "integer" ,
555
+ "description" : "Line number before which to insert content (mutually exclusive with 'after')" ,
556
+ },
557
+ "after" : {
558
+ "type" : "integer" ,
559
+ "description" : "Line number after which to insert content (mutually exclusive with 'before')" ,
560
+ },
561
+ "encoding" : {
562
+ "type" : "string" ,
563
+ "description" : "Text encoding (default: 'utf-8')" ,
564
+ "default" : "utf-8" ,
565
+ },
566
+ },
567
+ "required" : ["path" , "file_hash" , "contents" ],
568
+ },
569
+ )
570
+
571
+ async def run_tool (self , arguments : Dict [str , Any ]) -> Sequence [TextContent ]:
572
+ """Execute the tool with given arguments."""
573
+ try :
574
+ if "path" not in arguments :
575
+ raise RuntimeError ("Missing required argument: path" )
576
+ if "file_hash" not in arguments :
577
+ raise RuntimeError ("Missing required argument: file_hash" )
578
+ if "contents" not in arguments :
579
+ raise RuntimeError ("Missing required argument: contents" )
580
+
581
+ file_path = arguments ["path" ]
582
+ if not os .path .isabs (file_path ):
583
+ raise RuntimeError (f"File path must be absolute: { file_path } " )
584
+
585
+ # Check if exactly one of before/after is specified
586
+ if ("before" in arguments ) == ("after" in arguments ):
587
+ raise RuntimeError (
588
+ "Exactly one of 'before' or 'after' must be specified"
589
+ )
590
+
591
+ line_number = (
592
+ arguments .get ("before" )
593
+ if "before" in arguments
594
+ else arguments .get ("after" )
595
+ )
596
+ is_before = "before" in arguments
597
+ encoding = arguments .get ("encoding" , "utf-8" )
598
+
599
+ result = await self .editor .insert_text_file_contents (
600
+ file_path = file_path ,
601
+ file_hash = arguments ["file_hash" ],
602
+ contents = arguments ["contents" ],
603
+ before = is_before ,
604
+ line_number = line_number ,
605
+ encoding = encoding ,
606
+ )
607
+
608
+ return [TextContent (type = "text" , text = json .dumps (result , indent = 2 ))]
609
+
610
+ except Exception as e :
611
+ logger .error (f"Error processing request: { str (e )} " )
612
+ logger .error (traceback .format_exc ())
613
+ raise RuntimeError (f"Error processing request: { str (e )} " ) from e
614
+
615
+
524
616
# Initialize tool handlers
525
617
get_contents_handler = GetTextFileContentsHandler ()
526
618
edit_contents_handler = EditTextFileContentsHandler ()
527
619
create_file_handler = CreateTextFileHandler ()
528
620
append_file_handler = AppendTextFileContentsHandler ()
529
621
delete_contents_handler = DeleteTextFileContentsHandler ()
622
+ insert_file_handler = InsertTextFileContentsHandler ()
530
623
531
624
532
625
@app .list_tools ()
@@ -538,6 +631,7 @@ async def list_tools() -> List[Tool]:
538
631
create_file_handler .get_tool_description (),
539
632
append_file_handler .get_tool_description (),
540
633
delete_contents_handler .get_tool_description (),
634
+ insert_file_handler .get_tool_description (),
541
635
]
542
636
543
637
@@ -556,6 +650,8 @@ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
556
650
return await append_file_handler .run_tool (arguments )
557
651
elif name == delete_contents_handler .name :
558
652
return await delete_contents_handler .run_tool (arguments )
653
+ elif name == insert_file_handler .name :
654
+ return await insert_file_handler .run_tool (arguments )
559
655
else :
560
656
raise ValueError (f"Unknown tool: { name } " )
561
657
except ValueError :
0 commit comments