# How do you build a simple AI agent that can edit code?

Source: https://codelifter.com/forum/posts/how-do-you-build-a-simple-ai-agent-that-can-edit-code/
Site: CodeLifter Forum, category MCP and tools
Published: 2026-11-09. Updated: 2026-11-09. Replies: 1.

## Question by CodeLifter editors (staff), 2026-11-09

To build a simple AI agent that can edit code, give a model an editing tool through its provider's API, run every tool call it asks for in your own code, send the result back, and repeat until it stops asking. That loop plus one guarded file tool is the whole agent; most of the work is in the guards.

## What is the smallest thing that counts as an agent?

Anthropic's engineering post [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) puts it in one line: agents "are typically just LLMs using tools based on environmental feedback in a loop." A tool here is a function your program offers the model, described by a name, a description and an input schema. The model never touches your disk. It returns a structured request; your code decides whether to run it. The [AI agent thread](/forum/posts/what-is-an-ai-agent-and-how-is-a-coding-agent-different-from-a-chatbot/) explains how that differs from a chatbot.

OpenAI's [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) describes the same loop in five steps: send the request with tools, receive a tool call, execute it in your application, send the output back, and receive a final answer "(or more tool calls)". Anthropic's API works the same way: a response with `stop_reason: "tool_use"` contains `tool_use` blocks, and you reply with `tool_result` blocks.

| Part | Your job or the model's? | In the example below |
| --- | --- | --- |
| Tool definition | Yours | Anthropic's built-in text editor schema |
| Choosing a tool call | The model's | It asks to `view`, `create` or `str_replace` |
| Running the call | Yours | `run_tool()` |
| Guarding the call | Yours | `safe()`, the single-match check and the backup |
| Stopping | Both | The model stops asking, or the 10-turn cap ends it |

## Step by step: a file-editing agent in Python

This follows Anthropic's [text editor tool documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool), read on 16 October 2026, which defines a tool type `text_editor_20250728` named `str_replace_based_edit_tool` with the commands `view`, `create`, `str_replace` and `insert`. CodeLifter has not run this code; treat it as a sketch to check against the current docs.

1. Use Python 3.9 or later (the path check relies on `Path.is_relative_to`). Install the SDK with `pip install anthropic` and set `ANTHROPIC_API_KEY` in your shell, never in the file.
2. Make a throwaway folder called `demo` with one broken file in it, and put it under git.
3. Save this as `agent.py` next to it:

```python
import pathlib, shutil, anthropic

ROOT = pathlib.Path("demo").resolve()
TOOLS = [{"type": "text_editor_20250728", "name": "str_replace_based_edit_tool"}]
client = anthropic.Anthropic()

def safe(path):
    p = (ROOT / path).resolve()
    if not p.is_relative_to(ROOT):
        raise ValueError("path is outside the project folder")
    return p

def run_tool(inp):
    p, cmd = safe(inp["path"]), inp["command"]
    if cmd == "view":
        return p.read_text() if p.is_file() else "\n".join(x.name for x in p.iterdir())
    if cmd == "create":
        p.write_text(inp["file_text"])
        return "Created"
    if cmd == "str_replace":
        text = p.read_text()
        n = text.count(inp["old_str"])
        if n != 1:
            raise ValueError(f"Found {n} matches; give more context for exactly one")
        shutil.copy(p, f"{p}.backup")
        p.write_text(text.replace(inp["old_str"], inp["new_str"]))
        return "Edited"
    raise ValueError(f"Command {cmd} is not supported here")

messages = [{"role": "user", "content": "Fix the syntax error in primes.py"}]
for turn in range(10):
    reply = client.messages.create(model="claude-opus-5-5", max_tokens=2048,
                                   tools=TOOLS, messages=messages)
    messages.append({"role": "assistant", "content": reply.content})
    if reply.stop_reason != "tool_use":
        break
    results = []
    for block in reply.content:
        if block.type == "tool_use":
            try:
                out, err = run_tool(block.input), False
            except Exception as e:
                out, err = f"Error: {e}", True
            results.append({"type": "tool_result", "tool_use_id": block.id,
                            "content": out, "is_error": err})
    messages.append({"role": "user", "content": results})
```

4. Run `python agent.py`, then `git diff` to see exactly what changed.

The model name is the one in the docs' own examples; swap in whichever current model you have access to.

## Why are the guards the important part?

Every guard above comes from a vendor recommendation, not from taste:

- **Paths.** The text editor page tells you to "Validate file paths to prevent directory traversal". `safe()` resolves the path and refuses anything outside `demo`. Anthropic's agents post adds that the model made mistakes with relative paths after moving out of the root, so its team "changed the tool to always require absolute filepaths".
- **Exactly one match.** The docs show an error for a `str_replace` that matches several places: "Please provide more context to make a unique match." Returning that as an error lets the model try again instead of editing the wrong line.
- **Backups.** The page says to "Create backups before making changes". Git does this better, which is why step 2 matters.
- **Errors go back to the model.** Each failure is returned with `"is_error": true`, so the model reads it and adjusts.
- **A turn cap.** The agents post mentions "stopping conditions (such as a maximum number of iterations) to maintain control". Without one, a confused loop keeps spending tokens.

The same post warns that agents bring "higher costs, and the potential for compounding errors" and recommends "extensive testing in sandboxed environments". So run this in a container or a throwaway folder, never in your home directory.

## What would you add next?

**Human approval.** Before `run_tool` writes anything, print the file name, `old_str` and `new_str`, and ask `Apply? [y/N]`. If the answer is no, return an error result saying the edit was refused, so the model knows to stop or try something else. That one prompt turns the loop from autonomous to supervised.

**A test tool.** A `run_tests` tool is the usual next step: it gives the model what the agents post calls "ground truth" from the environment. Run it with a fixed command only, not arbitrary shell input. If you want the same tools in several apps, wrap them in an MCP server instead, as in the [simple MCP server thread](/forum/posts/how-do-you-build-a-simple-mcp-server/). The CodeLifter [API providers page](https://codelifter.com/api-providers/) lists fourteen model APIs and which ones accept the OpenAI request format, useful if you port the loop, and the [agents directory](https://codelifter.com/agents/) shows how finished coding agents handle the same problems.

## Short version

- An agent is a model, some tools and a loop that runs the tool calls the model asks for.
- You execute every call yourself, so you decide what is allowed.
- Validate paths, require a single match for edits, keep backups and cap the turns.
- Test in a throwaway folder under git and read `git diff` after each run.

## Reply 1 by CodeLifter editors (staff), 2026-11-09

**Do you need a framework like an agent SDK to build an AI agent?** No, and for a first one it can hide what is happening. Anthropic's [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) post recommends "finding the simplest solution possible, and only increasing complexity when needed", which "might mean not building agentic systems at all." The loop in this thread is about forty lines of plain Python. What a helper library saves you is the bookkeeping. Anthropic's [tool runner page](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) says the SDK helper "handles the agentic loop, error wrapping, and type safety", and lists it as beta in the Python, TypeScript, C# and Go SDKs. The same page names the trade-off: when you need "human-in-the-loop approval, custom logging, or conditional execution", it points you back to the manual loop. A file-editing agent is exactly the case where you may want to approve each write, so writing the loop yourself first is a reasonable choice. Once you understand it, a helper or a full agent SDK saves code without changing the shape.

---
Cite as: CodeLifter Forum, "How do you build a simple AI agent that can edit code?", https://codelifter.com/forum/posts/how-do-you-build-a-simple-ai-agent-that-can-edit-code/, accessed 2026-11-12.
