create_text_file
Create or overwrite text files by specifying a relative path and content. This tool writes files and returns success or failure messages.
Instructions
Write a new file or overwrite an existing file. Returns a message indicating success or failure.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| relative_path | Yes | The relative path to the file to create. | |
| content | Yes | The (appropriately encoded) content to write to the file. |
Implementation Reference
- src/serena/tools/file_tools.py:53-82 (handler)The handler implementation of the 'create_text_file' tool. The 'apply' method validates the relative path, creates necessary directories, writes the content to the file, and returns a success message indicating if it was a new file or overwrite.class CreateTextFileTool(Tool, ToolMarkerCanEdit): """ Creates/overwrites a file in the project directory. """ def apply(self, relative_path: str, content: str) -> str: """ Write a new file or overwrite an existing file. :param relative_path: the relative path to the file to create :param content: the (appropriately encoded) content to write to the file :return: a message indicating success or failure """ project_root = self.get_project_root() abs_path = (Path(project_root) / relative_path).resolve() will_overwrite_existing = abs_path.exists() if will_overwrite_existing: self.project.validate_relative_path(relative_path, require_not_ignored=True) else: assert abs_path.is_relative_to( self.get_project_root() ), f"Cannot create file outside of the project directory, got {relative_path=}" abs_path.parent.mkdir(parents=True, exist_ok=True) abs_path.write_text(content, encoding=self.project.project_config.encoding) answer = f"File created: {relative_path}." if will_overwrite_existing: answer += " Overwrote existing file." return answer
- src/serena/tools/tools_base.py:358-367 (registration)The ToolRegistry automatically discovers all subclasses of Tool in serena.tools modules (including CreateTextFileTool) and registers them with snake_case names derived from the class name (e.g., 'create_text_file').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)
- Extracts function metadata (including JSON schema from type hints and docstring) from the tool's 'apply' method for use in MCP tool definitions.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/tools/tools_base.py:119-125 (registration)Derives the tool name 'create_text_file' from the class name 'CreateTextFileTool' by removing 'Tool' suffix and converting CamelCase to snake_case.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