The text editor tool — Built-in tool cho file operations

5 — Tool UseTrung cấp20 phút

Tool use bình thường: bạn define schema + function.

Bạn sẽ học được
  • Dùng built-in str_replace_editor tool của Anthropic
  • Implement handlers cho 6 commands: view, str_replace, create, insert, undo_edit
  • So sánh built-in tool vs custom tool
  • Hiểu đây chính là foundation của Claude Code

6 commands

Claude decide command nào qua input param command.

CommandTask
viewXem file content hoặc list directory
str_replaceReplace text trong file
createTạo file mới
insertInsert dòng mới tại line number
undo_editUndo edit gần nhất

Schema declaration

Không cần viết full schema. Chỉ declare tool type + name:

Claude "expand" schema nội bộ → nhận input types đầy đủ.

def get_text_edit_schema(model: str) -> dict:
    """Return schema version matching model."""
    if model.startswith("claude-sonnet-5") or model.startswith("claude-opus-4"):
        return {
            "type": "text_editor_20260101",  # check docs cho version mới
            "name": "str_replace_based_edit_tool",
        }
    elif model.startswith("claude-3-7"):
        return {
            "type": "text_editor_20250124",
            "name": "str_replace_editor",
        }
    # ...

Implement handler

Đây là phần bạn write. Mỗi command = case xử lý:

import os
from pathlib import Path

# Track undo history
edit_history = []


def handle_text_editor(command: str, **kwargs) -> str:
    """Dispatch text editor commands."""
    if command == "view":
        return cmd_view(**kwargs)
    elif command == "str_replace":
        return cmd_str_replace(**kwargs)
    elif command == "create":
        return cmd_create(**kwargs)
    elif command == "insert":
        return cmd_insert(**kwargs)
    elif command == "undo_edit":
        return cmd_undo_edit(**kwargs)
    else:
        raise ValueError(f"Unknown command: {command}")


def cmd_view(path: str, view_range: list = None) -> str:
    """View file or directory."""
    p = Path(path)
    if not p.exists():
        raise FileNotFoundError(f"{path} does not exist")
    
    if p.is_dir():
        # List directory
        items = [f.name for f in p.iterdir()]
        return f"Directory {path}:\n" + "\n".join(items)
    
    # Read file
    content = p.read_text()
    lines = content.split("\n")
    
    if view_range:
        start, end = view_range
        lines = lines[start-1:end]
        numbered = [f"{i+start}: {line}" for i, line in enumerate(lines)]
    else:
        numbered = [f"{i+1}: {line}" for i, line in enumerate(lines)]
    
    return "\n".join(numbered)


def cmd_str_replace(path: str, old_str: str, new_str: str) -> str:
    """Replace old_str with new_str in file. old_str must be unique."""
    p = Path(path)
    content = p.read_text()
    
    count = content.count(old_str)
    if count == 0:
        raise ValueError(f"old_str not found in {path}")
    if count > 1:
        raise ValueError(f"old_str appears {count} times in {path}, must be unique")
    
    # Save for undo
    edit_history.append((path, content))
    
    new_content = content.replace(old_str, new_str, 1)
    p.write_text(new_content)
    
    return f"Replaced in {path}"


def cmd_create(path: str, file_text: str) -> str:
    """Create new file."""
    p = Path(path)
    if p.exists():
        raise FileExistsError(f"{path} already exists")
    
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(file_text)
    
    edit_history.append((path, None))  # creation marker
    return f"Created {path}"


def cmd_insert(path: str, insert_line: int, new_str: str) -> str:
    """Insert new_str at insert_line."""
    p = Path(path)
    content = p.read_text()
    lines = content.split("\n")
    
    edit_history.append((path, content))
    
    lines.insert(insert_line, new_str)
    p.write_text("\n".join(lines))
    
    return f"Inserted at line {insert_line} in {path}"


def cmd_undo_edit(path: str) -> str:
    """Undo last edit."""
    # Find last edit to this path
    for i in range(len(edit_history) - 1, -1, -1):
        saved_path, saved_content = edit_history[i]
        if saved_path == path:
            if saved_content is None:
                # Was created → delete
                Path(path).unlink()
                edit_history.pop(i)
                return f"Undid creation of {path}"
            else:
                # Restore
                Path(path).write_text(saved_content)
                edit_history.pop(i)
                return f"Restored {path}"
    
    raise ValueError(f"No edit history for {path}")

Integrate with conversation loop

Claude sequence:

  • view(path="main.py") → read file
  • Identify function
  • str_replace(path, old_str="def calculate(...)", new_str="def calculate(...)\n '''Docstring'''")
  • Confirm
def run_tool_with_editor(name, input_data):
    """Dispatch including text editor."""
    if name == "str_replace_based_edit_tool":
        return handle_text_editor(**input_data)
    # ... other tools
    return TOOL_FUNCTIONS[name](**input_data)


# Main
text_editor_schema = get_text_edit_schema(MODEL)

final, history = run_conversation(
    "Open main.py, find function 'calculate' and add docstring.",
    tools=[text_editor_schema]
)

Ví dụ thực chiến: Add unit tests

User: "Open ./main.py, add function to calculate pi to 5 digits. Create ./test.py with tests."

Claude sequence:

Final: Text response summary changes.

Đây chính là mini Claude Code bạn vừa xây.

  • view("main.py") — see existing code
  • str_replace("main.py", old, new) — add function
  • view("main.py") — verify
  • create("test.py", test_code) — new test file
  • Optional: view("test.py") — verify

Built-in tool vs custom tool

Khi nào dùng built-in: file operations (text_editor), web search, code execution (Module 6 covers). Anthropic trained Claude thành thạo các tools này.

Khi nào custom: domain-specific (CRM, your API, business logic).

Built-in (text_editor)Custom tool
SchemaAnthropic definedBạn write
FunctionBạn implementBạn implement
NamingFixed namesBạn chọn
UpdatesAnthropic improveBạn maintain

Security considerations

Text editor = Claude có thể modify files.

1. Sandbox directory

Never allow access ngoài sandbox trong production.

2. Read-only mode

Cho dev/debug, disable write operations:

def cmd_view(path: str, ...):
    resolved = Path(path).resolve()
    allowed_dir = Path("/sandbox").resolve()
    
    if not str(resolved).startswith(str(allowed_dir)):
        raise PermissionError(f"Access denied: {path}")
    
    # ...

2. Read-only mode

3. Backup trước edit

READ_ONLY = True

def cmd_str_replace(...):
    if READ_ONLY:
        raise PermissionError("Read-only mode")

3. Backup trước edit

def cmd_str_replace(path, ...):
    # Auto-backup
    shutil.copy(path, f"{path}.bak.{timestamp()}")
    ...

Anti-patterns

❌ Cho Claude full filesystem access

Claude có thể accidentally rm -rf hay edit wrong file.

Fix: Sandbox + whitelist.

❌ Không track edit history

Undo không work.

Fix: Save content trước khi modify.

❌ Dùng built-in tool khi task đơn giản

Cho task "read 1 file", built-in overkill — viết tool read_file đơn giản.

Fix: Built-in cho scenarios cần đầy đủ 6 commands.

❌ Không version model cho schema

Schema version depends model. Hardcode → break khi upgrade.

Fix: get_text_edit_schema(model) function.

Áp dụng ngay

Bài tập 1: Setup + test basic (20 phút)

Implement handler. Test:

Bài tập 2: Refactor task (30 phút)

run_conversation(
    "Create a file called hello.py with print('hello world')",
    tools=[text_editor_schema]
)

Bài tập 2: Refactor task (30 phút)

Claude sẽ: view → multiple str_replace → verify. Observe loop.

run_conversation(
    "Open ./sample.py. Find all function def, add docstring to each that lacks one.",
    tools=[text_editor_schema]
)

Tóm tắt

🎯 Text editor = built-in tool cho file ops. Schema từ Anthropic, function bạn viết.

🎯 6 commands: view, str_replace, create, insert, undo_edit.

🎯 Sandbox + backup là must-have cho security.

🎯 Đây là foundation của Claude Code — hiểu tool này = hiểu cách Claude Code work.

🎯 Built-in cho common tasks, custom cho domain-specific.

Nội dung này có hữu ích không?
Kiểm tra kiến thức

Củng cố những gì bạn vừa học

12 câu trắc nghiệm · đạt từ 70% · câu hỏi và đáp án xáo trộn mỗi lần.