Skip to main content
Glama

Insert Before Symbol

insert_before_symbol
Destructive

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
NameRequiredDescriptionDefault
name_pathYesName path of the symbol before which to insert content (definitions in the `find_symbol` tool apply).
relative_pathYesThe relative path to the file containing the symbol.
bodyYesThe body/content to be inserted before the line in which the referenced symbol is defined.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • 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
  • 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)
  • 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
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate destructiveHint=true and readOnlyHint=false, which the description aligns with by describing an insertion operation that modifies files. The description adds valuable context beyond annotations by specifying that insertion occurs 'before the beginning of the definition' and via 'the symbol's location', and mentions typical use cases, which helps the agent understand the tool's behavior in practical scenarios.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence, followed by a second sentence providing typical use cases. Both sentences earn their place by clarifying scope and practical applications without redundancy or unnecessary details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (file modification with symbol-based positioning), the description provides sufficient context alongside annotations (destructive, not read-only) and a complete input schema. With an output schema present, the description does not need to explain return values, making it complete for agent usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, providing clear documentation for all three parameters (name_path, relative_path, body). The description adds minimal semantic value beyond the schema, only implying that 'body' is content to insert and referencing 'find_symbol' for name_path definitions. This meets the baseline of 3 when schema coverage is high.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('inserts') and resource ('content before the beginning of the definition of the given symbol'), with specific examples of typical use cases (new class, function, method, field, variable assignment, or import statement). It distinguishes from sibling 'insert_after_symbol' by specifying 'before' rather than 'after'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use this tool ('to insert a new class, function, method, field or variable assignment; or a new import statement before the first symbol in the file'), but does not explicitly state when not to use it or name alternatives beyond the implied sibling 'insert_after_symbol'. It lacks explicit exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oraios/serena'

If you have feedback or need assistance with the MCP directory API, please join our Discord server