read_file
Retrieve file contents or specific line ranges from codebases to analyze source code, review implementations, or extract data for development tasks.
Instructions
Reads the given file or a chunk of it. Generally, symbolic operations like find_symbol or find_referencing_symbols should be preferred if you know which symbols you are looking for. Returns the full text of the file at the given relative path.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| relative_path | Yes | The relative path to the file to read. | |
| start_line | No | The 0-based index of the first line to be retrieved. | |
| end_line | No | The 0-based index of the last line to be retrieved (inclusive). If None, read until the end of the file. | |
| max_answer_chars | No | If the file (chunk) is longer than this number of characters, no content will be returned. Don't adjust unless there is really no other way to get the content required for the task. |
Implementation Reference
- src/serena/tools/file_tools.py:22-51 (handler)Handler implementation for the 'read_file' tool: the apply method of ReadFileTool reads the file content using project.read_file, extracts line range, and limits length if specified.class ReadFileTool(Tool): """ Reads a file within the project directory. """ def apply(self, relative_path: str, start_line: int = 0, end_line: int | None = None, max_answer_chars: int = -1) -> str: """ Reads the given file or a chunk of it. Generally, symbolic operations like find_symbol or find_referencing_symbols should be preferred if you know which symbols you are looking for. :param relative_path: the relative path to the file to read :param start_line: the 0-based index of the first line to be retrieved. :param end_line: the 0-based index of the last line to be retrieved (inclusive). If None, read until the end of the file. :param max_answer_chars: if the file (chunk) is longer than this number of characters, no content will be returned. Don't adjust unless there is really no other way to get the content required for the task. :return: the full text of the file at the given relative path """ self.project.validate_relative_path(relative_path, require_not_ignored=True) result = self.project.read_file(relative_path) result_lines = result.splitlines() if end_line is None: result_lines = result_lines[start_line:] else: result_lines = result_lines[start_line : end_line + 1] result = "\n".join(result_lines) return self._limit_length(result, max_answer_chars)
- src/serena/tools/tools_base.py:118-125 (registration)Tool.get_name_from_cls derives the tool name 'read_file' from the ReadFileTool class name.@classmethod def get_name_from_cls(cls) -> str: name = cls.__name__ if name.endswith("Tool"): name = name[:-4] # convert to snake_case name = "".join(["_" + c.lower() if c.isupper() else c for c in name]).lstrip("_") return name
- src/serena/tools/tools_base.py:355-367 (registration)ToolRegistry automatically discovers Tool subclasses like ReadFileTool in serena.tools packages and registers them with names derived from class names.@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)
- Derives schema metadata from the apply method signature and docstring using func_metadata for MCP tool definition.def get_apply_fn_metadata_from_cls(cls) -> FuncMetadata: """Get the metadata for the apply method from the class (static metadata). Needed for creating MCP tools in a separate process without running into serialization issues. """ # First try to get from __dict__ to handle dynamic docstring changes if "apply" in cls.__dict__: apply_fn = cls.__dict__["apply"] else: # Fall back to getattr for inherited methods apply_fn = getattr(cls, "apply", None) if apply_fn is None: raise AttributeError(f"apply method not defined in {cls}. Did you forget to implement it?") return func_metadata(apply_fn, skip_names=["self", "cls"])
- src/serena/project.py:149-157 (helper)Project.read_file helper method called by the tool handler to safely read file contents using FileUtils.def read_file(self, relative_path: str) -> str: """ Reads a file relative to the project root. :param relative_path: the path to the file relative to the project root :return: the content of the file """ abs_path = Path(self.project_root) / relative_path return FileUtils.read_file(str(abs_path), self.project_config.encoding)