How Claude Code Works

Interactive explainer

Claude Code is a loop, not a chatbot.

A language model cannot open a file, run a test or edit a line. It can only produce text. Everything else is done by a program on your machine that reads what the model asked for, does it, and hands the result back — over and over, until the model stops asking. That program is Claude Code.

Everything below is a simulation of that exchange, built to be poked at. Start with the loop, then take it apart.

01

The agent loop

One prompt from you becomes many round trips to the model. Press play and watch a whole task run: who is speaking, what actually executes, and what gets sent back up on the next request.

What you see in the terminal

turn 0
  1. Press Play to start a session.

What gets sent next time

request 0
system tool docs, your CLAUDE.md, environment
tools 9 JSON schemas the model may call
messages 0 blocks — the entire history, every time
    context used 0 / 200k

    The model remembers nothing between requests. This list is the memory.

    Idle.

    • The model never touches your machine. It emits a tool_use block — a name and some JSON — and waits.
    • The CLI is the one with hands. It runs the tool, catches the output, and appends it to the conversation as a tool_result.
    • Every request carries the whole transcript. The API is stateless; continuity is something the CLI re-uploads each turn.
    • The loop ends when the model stops asking — a reply with no tool call, stop_reason: "end_turn", and your prompt comes back.

    02

    Anatomy of one round trip

    Strip the terminal away and a turn is one HTTP request and one response. Hover or tap any highlighted part to see what it is for.

    Request · your machine → the API
    POST /v1/messages
    {
      "model": "claude-…",
      "system": "You are Claude Code… <CLAUDE.md>",
      "tools": [ { "name": "Read", … }, { "name": "Bash", … } ],
      "messages": [
        { "role": "user",      "content": "the auth test is failing" },
        { "role": "assistant", "content": [ { "type": "tool_use", … } ] },
        { "role": "user",      "content": [ { "type": "tool_result", … } ] }
      ]
    }
    Response · the API → your machine
    {
      "role": "assistant",
      "content": [
        { "type": "text", "text": "Let me read the middleware." },
        { "type": "tool_use",
          "id": "toolu_01…",
          "name": "Read",
          "input": { "file_path": "src/auth.js" } }
      ],
      "stop_reason": "tool_use"
    }

    03

    The tools it is given

    The model can only call what it was handed in the tools array. There is no hidden channel — a tool that is not in that array cannot be called, whatever the model would like to do. Here are the core nine. Pick one.

    04

    The gate in front of every tool

    Between the model asking and the machine acting sits a check that the model has no say in. Change the mode, change the rules, then fire a call at it.

    Permission mode

    .claude/settings.json

    {
      "permissions": {
        "allow": [ "Bash(npm test:*)", "Read(src/**)" ],
        "deny":  [ "Bash(rm:*)", "Read(./.env)" ]
      }
    }

    Fire a tool call at the gate

    1PreToolUse hookyour script may veto
    2deny rulesan outright no wins
    3modeplan / acceptEdits / bypass
    4allow rulespre-approved patterns
    5ask youthe default for anything left

    No call fired yet.

    • A denial is just another tool result. The model is told "the user declined" and has to find another way — it cannot retry past the gate.
    • Rules are patterns, not trust. Bash(npm test:*) approves a prefix, so scope them as narrowly as you can stand.
    • Hooks run before the model's choice matters. Exit non-zero in a PreToolUse hook and the call never happens.

    05

    Context is the budget

    Every file read and every command's output stays in the transcript and is re-sent on every later request. That is why a long session slows down, and why the two escape hatches below exist.

    dashed line: where compaction fires

    system + tools your turns tool output compacted summary free

    Compaction

    When the window fills, the older part of the transcript is replaced by a summary the model writes of itself. The session keeps going; the detail does not. Anything you need to survive that boundary belongs in a file, not in the scrollback.

    Subagents

    A subagent is a second loop with its own empty context. It burns whatever it likes reading forty files, and only its final report comes back to the main thread. Searching wide without paying for it in the main transcript is the whole point.

    06

    Six places you can get a word in

    Each of these is a file in your project that changes what happens inside the loop above. Tap a card for the shape of it.

    That is the whole machine.

    A prompt, a list of tools, a loop that runs them, a gate in front of the dangerous ones, and a transcript that has to fit. Everything else — skills, hooks, MCP servers, subagents — is a way of changing what goes into one of those five boxes.