insert_before_symbol
Inserts content before a specific symbol definition in code files, enabling addition of classes, functions, imports, or variables at precise locations.
Instructions
Inserts the given content before the beginning of the definition of the given symbol (via the symbol's location). A typical use case is to insert a new class, function, method, field or variable assignment; or a new import statement before the first symbol in the file.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name_path | Yes | Name path of the symbol before which to insert content (definitions in the `find_symbol` tool apply). | |
| relative_path | Yes | The relative path to the file containing the symbol. | |
| body | Yes | The body/content to be inserted before the line in which the referenced symbol is defined. |
Implementation Reference
- src/serena/tools/symbol_tools.py:263-286 (handler)The handler function for the 'insert_before_symbol' MCP tool. It creates a CodeEditor instance and calls its insert_before_symbol method to perform the insertion.class InsertBeforeSymbolTool(Tool, ToolMarkerSymbolicEdit): """ Inserts content before the beginning of the definition of a given symbol. """ def apply( self, name_path: str, relative_path: str, body: str, ) -> str: """ Inserts the given content before the beginning of the definition of the given symbol (via the symbol's location). A typical use case is to insert a new class, function, method, field or variable assignment; or a new import statement before the first symbol in the file. :param name_path: name path of the symbol before which to insert content (definitions in the `find_symbol` tool apply) :param relative_path: the relative path to the file containing the symbol :param body: the body/content to be inserted before the line in which the referenced symbol is defined """ code_editor = self.create_code_editor() code_editor.insert_before_symbol(name_path, relative_file_path=relative_path, body=body) return SUCCESS_RESULT
- src/serena/code_editor.py:168-195 (helper)The core implementation logic for inserting content before a symbol. Finds the symbol position, adjusts the body with appropriate newlines, and inserts the text at the start of the symbol's line.def insert_before_symbol(self, name_path: str, relative_file_path: str, body: str) -> None: """ Inserts content before the symbol with the given name in the given file. """ symbol = self._find_unique_symbol(name_path, relative_file_path) symbol_start_pos = symbol.get_body_start_position_or_raise() # insert position is the start of line where the symbol is defined line = symbol_start_pos.line col = 0 original_trailing_empty_lines = self._count_trailing_newlines(body) - 1 # ensure eol is present at end body = body.rstrip() + "\n" # add suitable number of trailing empty lines after the body (at least 0/1 depending on the symbol type, # otherwise as many as the caller wanted to insert) min_trailing_empty_lines = 0 if symbol.is_neighbouring_definition_separated_by_empty_line(): min_trailing_empty_lines = 1 num_trailing_newlines = max(min_trailing_empty_lines, original_trailing_empty_lines) body += "\n" * num_trailing_newlines # apply edit with self.edited_file_context(relative_file_path) as edited_file: edited_file.insert_text_at_position(PositionInFile(line=line, col=col), body)
- src/serena/tools/tools_base.py:355-427 (registration)The ToolRegistry singleton automatically discovers and registers all subclasses of Tool, including InsertBeforeSymbolTool, deriving the tool name from the class name.@singleton class ToolRegistry: def __init__(self) -> None: self._tool_dict: dict[str, RegisteredTool] = {} for cls in iter_subclasses(Tool): if not any(cls.__module__.startswith(pkg) for pkg in tool_packages): continue is_optional = issubclass(cls, ToolMarkerOptional) name = cls.get_name_from_cls() if name in self._tool_dict: raise ValueError(f"Duplicate tool name found: {name}. Tool classes must have unique names.") self._tool_dict[name] = RegisteredTool(tool_class=cls, is_optional=is_optional, tool_name=name) def get_tool_class_by_name(self, tool_name: str) -> type[Tool]: return self._tool_dict[tool_name].tool_class def get_all_tool_classes(self) -> list[type[Tool]]: return list(t.tool_class for t in self._tool_dict.values()) def get_tool_classes_default_enabled(self) -> list[type[Tool]]: """ :return: the list of tool classes that are enabled by default (i.e. non-optional tools). """ return [t.tool_class for t in self._tool_dict.values() if not t.is_optional] def get_tool_classes_optional(self) -> list[type[Tool]]: """ :return: the list of tool classes that are optional (i.e. disabled by default). """ return [t.tool_class for t in self._tool_dict.values() if t.is_optional] def get_tool_names_default_enabled(self) -> list[str]: """ :return: the list of tool names that are enabled by default (i.e. non-optional tools). """ return [t.tool_name for t in self._tool_dict.values() if not t.is_optional] def get_tool_names_optional(self) -> list[str]: """ :return: the list of tool names that are optional (i.e. disabled by default). """ return [t.tool_name for t in self._tool_dict.values() if t.is_optional] def get_tool_names(self) -> list[str]: """ :return: the list of all tool names. """ return list(self._tool_dict.keys()) def print_tool_overview( self, tools: Iterable[type[Tool] | Tool] | None = None, include_optional: bool = False, only_optional: bool = False ) -> None: """ Print a summary of the tools. If no tools are passed, a summary of the selection of tools (all, default or only optional) is printed. """ if tools is None: if only_optional: tools = self.get_tool_classes_optional() elif include_optional: tools = self.get_all_tool_classes() else: tools = self.get_tool_classes_default_enabled() tool_dict: dict[str, type[Tool] | Tool] = {} for tool_class in tools: tool_dict[tool_class.get_name_from_cls()] = tool_class for tool_name in sorted(tool_dict.keys()): tool_class = tool_dict[tool_name] print(f" * `{tool_name}`: {tool_class.get_tool_description().strip()}") def is_valid_tool_name(self, tool_name: str) -> bool: return tool_name in self._tool_dict
- The apply method signature and docstring define the input schema (name_path: str, relative_path: str, body: str) and output (str) for the tool, used by MCP for validation and description.def apply( self, name_path: str, relative_path: str, body: str, ) -> str: """ Inserts the given content before the beginning of the definition of the given symbol (via the symbol's location). A typical use case is to insert a new class, function, method, field or variable assignment; or a new import statement before the first symbol in the file. :param name_path: name path of the symbol before which to insert content (definitions in the `find_symbol` tool apply) :param relative_path: the relative path to the file containing the symbol :param body: the body/content to be inserted before the line in which the referenced symbol is defined """ code_editor = self.create_code_editor() code_editor.insert_before_symbol(name_path, relative_file_path=relative_path, body=body) return SUCCESS_RESULT