create_text_file
Quickly create or overwrite text files on the Serena MCP server by specifying a relative path and content. Returns success or failure status for file operations.
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 |
|---|---|---|---|
| content | Yes | The (utf-8-encoded) content to write to the file. | |
| relative_path | Yes | The relative path to the file to create. |
Implementation Reference
- src/serena/tools/file_tools.py:52-82 (handler)The CreateTextFileTool class provides the core handler logic for the 'create_text_file' tool via its 'apply' method. It creates a new file or overwrites an existing one at the specified relative path within the project directory.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:359-370 (registration)The ToolRegistry singleton automatically discovers all subclasses of Tool in the 'serena.tools' package, derives their tool names (e.g., CreateTextFileTool becomes 'create_text_file'), and registers them for use by the agent.class ToolRegistry: def __init__(self) -> None: self._tool_dict: dict[str, RegisteredTool] = {} for cls in iter_subclasses(Tool): if not cls.__module__.startswith("serena.tools"): 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 parameters, types, and docstring) from the tool's 'apply' method to generate the MCP tool schema for input validation and LLM tool calling.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"])
- Derives the snake_case tool name from the PascalCase class name, e.g., 'CreateTextFileTool' -> 'create_text_file'.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