# Seeing what the agent sends

> Watch the context meter while you connect one more tool, find the session in its record on disk, and log the requests on your own machine to see what the tool definitions add to each one.

Page: https://schubergphilis.github.io/ai-training/customizing-agents/seeing-what-the-agent-sends/
License: CC BY-SA 4.0, https://creativecommons.org/licenses/by-sa/4.0/

In this lesson we look at what Claude Code sends to the model, in three
places. The context meter shows how full the window is while you work.
The session record on disk holds every message, tool call and tool
result of a session. A request log, which you turn on for one session,
holds each request exactly as it was sent. The exercise connects one
more tool and follows it through all three.

The concepts course showed a toy harness's own log of
[what is in each request](https://schubergphilis.github.io/ai-training/concepts/one-prompt-many-requests/#what-is-in-each-request):
the instructions, the tool descriptions and the history, sent again on
every call. The lesson on
[what the tool list costs](https://schubergphilis.github.io/ai-training/customizing-agents/mcp-tool-cost/#what-the-tool-list-costs)
estimated what a server's tool definitions take from that request. Here
you look at the real requests of your own agent.

## Watch the context meter

In Claude Code, `/context` shows what fills the window right now, broken
down by category, with suggestions for what to trim (Explore the context window, Claude Code documentation).
One of the categories is the tools of Model Context Protocol (MCP)
servers, shown as MCP tools. With tool search on, which is the
default, only the tool names are in the window, and the model loads a
full schema when a task needs it. With `ENABLE_TOOL_SEARCH=false` every
schema loads at the start (Explore the context window, Claude Code documentation).

`/context` answers when you ask. A status line shows a number all the
time. Claude Code runs the status line command after each new reply from
the model, passes it the session data as JSON (JavaScript Object
Notation) on standard input, and shows what it prints
(Customize your status line, Claude Code documentation). The command runs on your machine and costs no
tokens. For this lesson the useful fields are these:

- `context_window.used_percentage`, the share of the window in use. It is
  counted from the input tokens of the last request, and it can be null
  early in a session.
- `context_window.total_input_tokens`, those input tokens, cache reads
  and cache writes included.
- `transcript_path`, the path of the session record on disk.

All three come from the statusline page (Customize your status line, Claude Code documentation).
`statusline.py` in `site/examples/customizing-agents/seeing-what-the-agent-sends/`
prints them on one line, and a dash for a field that is missing or null.
You point Claude Code at it with a `statusLine` entry in a settings file
(Customize your status line, Claude Code documentation):

```json
{
  "statusLine": {
    "type": "command",
    "command": "python3 /tmp/handbook-inspect/.claude/statusline.py"
  }
}
```

#### Example: Two status lines

Run `statusline_demo.py` in the fixture directory, and compare with the
output below. The first sample input is from before the first request,
when the share is still null. The second is from after a request.

```python
used_text = "-" if used is None else f"{round(used)}%"
total_text = "-" if total is None else f"{total:,} tokens"
record = "-" if not transcript else os.path.basename(transcript)
return f"context {used_text} | {total_text} | record {record}"
```

Output:

```text
context - | 0 tokens | record 2f6c0d1e-5a7b-4c9d-8e3f-1a2b3c4d5e6f.jsonl
context 12% | 23,480 tokens | record 2f6c0d1e-5a7b-4c9d-8e3f-1a2b3c4d5e6f.jsonl
```

The meter tells you that the window is filling, and `/context` tells you
which category fills it. Neither shows the text itself.

## Find the session in its record

Claude Code keeps data under `~/.claude` as you work. The transcript of a
session is `projects/<project>/<session>.jsonl`, and it holds every
message, every tool call and every tool result of that session
(Explore the .claude directory, Claude Code documentation). The project directory is named after the
project's path, with every character other than a letter or a digit
turned into a dash, so `/home/user/work/my-repo` becomes
`-home-user-work-my-repo`. On macOS a copy in `/tmp` is at
`/private/tmp`, and its directory starts with `-private-tmp-`.
The status line above gives you the file name, and `transcript_path`
gives the full path.

Resume and rewind read past sessions from these files, and they are the
place to look when a run went wrong yesterday and you want to see which
file the agent read before it did. They are also plain text and
unencrypted, so whoever can read your files can read them. A `.env`
file the agent opened, or a key that a command printed, ends up in the
record as well (Explore the .claude directory, Claude Code documentation). Claude Code deletes
records older than `cleanupPeriodDays`, which is 30 days unless you set
it. From version 2.1.288, `claude purge <path>` deletes a project's
transcripts, its memory and its lines in the prompt history, after it
shows you the plan (Explore the .claude directory, Claude Code documentation).

The record holds the session turn by turn. The system prompt and the
tool definitions that came with each request aren't in the page's list
of what it holds. For those you need the request itself.

#### Checkpoint: Which view answers the question?

Match each question to the view that answers it. The request log was
off in last week's and yesterday's sessions.

Statements:

- How full is the window in the session that is open now?
- Which file did last week's session read before it made the wrong change?
- How many tokens do the server's tool definitions take in each request, as sent?
- Did yesterday's session store the access key that a command printed?

Options:

- The context meter
- The session record
- The request log

## Log the requests on your machine

Claude Code can write every request it sends to a directory on your
machine. It is part of its OpenTelemetry monitoring, the standard it uses
to export usage data (Monitoring, Claude Code documentation). Telemetry is on only with
`CLAUDE_CODE_ENABLE_TELEMETRY=1`. `OTEL_LOG_RAW_API_BODIES=file:<dir>`
then writes the full body of each request to `<dir>/<uuid>.request.json`,
and from version 2.1.274 Claude Code adds one line per successful
response to `<dir>/index.jsonl`, with the session id, the `query_source`
that sent the request and the file names (Monitoring, Claude Code documentation).
`OTEL_METRICS_EXPORTER=none` and `OTEL_LOGS_EXPORTER=none` switch off both
exporters, so the events themselves don't go to a collector anywhere
(Monitoring, Claude Code documentation).
In our run in October 2026, the request files were written with both
exporters set to `none`. If your organization already collects Claude
Code telemetry, ask its administrators before you turn on the request
log, because their settings may send the events somewhere you don't
expect.

The monitoring page leaves request bodies out unless you ask for them.
The variable's description warns that a body carries the whole
conversation, so it reveals all that the switches for prompt text and
tool details would reveal (Monitoring, Claude Code documentation). A request file
holds the system prompt, every tool definition and every message so
far, with the full text of each file the agent read and any secret a
command printed. So keep the log on your own machine, in a directory
only your user can read and outside every repository. Turn it on for
the session you are investigating, and delete the directory when you
have your numbers. Don't send a request file to an online token counter
or paste it into a chat assistant.

Each request lists the tools in its `tools` field. In the Claude
application programming interface (API), a definition marked
`defer_loading` is sent to the API but stays out of the model's context
window until the model searches for that tool
(Tool search tool, Claude Platform documentation). So `/context` shows what is in the
window, the request log shows what was sent, and the two can differ. In
our run on 7 October 2026, a `claude -p` run with tool search on and no
MCP server, the deferred tools were Claude Code's own built-in tools.
They appeared by name in a message, and the `tools` field held one
placeholder marked `defer_loading`. The exercise starts Claude Code
with `ENABLE_TOOL_SEARCH=false`, which loads every MCP tool definition
upfront (Connect Claude Code to tools via MCP, Claude Code documentation), so each MCP tool definition in the log is
one the model reads.

A gateway can log requests too. You point `ANTHROPIC_BASE_URL` at it,
and the gateway can log every request that passes through
(Other LLM gateways, Claude Code documentation). Anthropic doesn't vouch for gateway products
made by others (Other LLM gateways, Claude Code documentation). A gateway you don't run
yourself also holds a copy of everything that passes through it, file
contents and secrets included. The log in a local directory never leaves
your machine.

Claude Code also sends requests of its own besides the turns of your
conversation, and the side requests are in the log too. The monitoring
page's cost counter sorts requests into main, subagent and auxiliary
ones. The `query_source` in `index.jsonl` is a different field, with
values such as `repl_main_thread` for the main conversation and
`compact` (Monitoring, Claude Code documentation).

`read_log.py` in the fixture directory reads a log directory and prints,
for each request, the estimated tokens of the system prompt, the tools
and the messages, at four characters per token. The `mcp tools` column
is the tool definitions of MCP servers and their share of the request.
Claude Code names an MCP tool `mcp__<server>__<tool>`
(Configure permissions, Claude Code documentation), and the script groups the tools by that
name. The `source` column is the `query_source` from `index.jsonl`. The
script skips a request without tools and leaves out a definition marked
`defer_loading`, and it prints how many of each it found.
`sample_log.py` writes a sample log of one side request without tools,
then one prompt that took three model calls, with three built-in tools
and a `notes` server with five tools, and runs `read_log.py` on it.

#### Example: Read the request log

Run `sample_log.py` in the fixture directory, and compare with the
output below.

```python
all_tools = body.get("tools") or []
if not all_tools:
    skipped += 1
    continue
tools = [tool for tool in all_tools if not tool.get("defer_loading")]
```

Output:

```text
estimated tokens, 4 characters per token
request  system   tools  messages   total  mcp tools   source
      1     404     926        20   1,350  604 (45%)   repl_main_thread
      2     404     926       107   1,437  604 (42%)   repl_main_thread
      3     404     926       235   1,565  604 (39%)   repl_main_thread
mcp__notes: 5 tools
skipped 1 request(s) without tools
left out 0 deferred definition(s)
```

The sample is small, so read it for its pattern. The system prompt and
the tools are the same in every request, and only the messages grow. The
`notes` server's five definitions are 604 of the 1,350 tokens of the
first request, before the agent has done anything. Two of the five tools
did the work. `add_note` and `delete_note` came along on every call, and
any instruction the agent read could ask for them.

#### Checkpoint: Why does the share fall?

The server's tool definitions are 45% of the first request and 39% of
the third. Why does the share fall?

- The client drops the definitions of the tools the model has already called.
- Prompt caching makes the definitions cheaper after the first request.
- The history grows with each call, and the definitions are sent unchanged.
- The system prompt gets shorter as the agent learns what the task needs.

#### Checkpoint: The numbers are in

The measurement is done: the server's tool definitions are a fifth of
every request. What do you do with the log?

- Attach the log directory to the team's issue as a zip, so others can check the numbers.
- Upload the files to an online token counter to get exact counts for the issue.
- Leave the directory, because Claude Code deletes its session files after 30 days.
- Put the table of numbers in the issue, delete the directory, and start new sessions without the variable.

#### Pitfall: The log in the project

A developer wants to measure their agent and points the request log at
`./request-log` inside the project, because that is where they work. At
the end of the day they run `git add .` and push. The forty request files
are now in the repository's history, with the full text of every file
the agent read that day, including the local `.env` file it opened to
check a port number. Deleting the files in the next commit doesn't remove
them from the history. The rule: a request log goes in a directory
outside every repository, readable only by you, and you delete it when
the measurement is done.

## Exercise

Connect one more tool in a copy and follow it through the meter, the
record and the log. Copy the `handbook` directory and the status line
script to places you can delete, and point the copy's settings at the
script. Run the shell steps from the course repository's top level.

```sh
cp -r site/examples/customizing-agents/mcp-first-server/handbook /tmp/handbook-inspect
mkdir -p /tmp/handbook-inspect/.claude
cp site/examples/customizing-agents/seeing-what-the-agent-sends/statusline.py /tmp/handbook-inspect/.claude/
printf '%s\n' '{"statusLine": {"type": "command", "command": "python3 /tmp/handbook-inspect/.claude/statusline.py"}}' > /tmp/handbook-inspect/.claude/settings.json
mkdir -m 700 ~/claude-request-log
```

1. `cd /tmp/handbook-inspect` and start `claude`. Trust the folder, check
   that the status line shows, and run `/context`. Write down the total
   and the MCP tools line, if there is one. Quit.
2. Add the file-system server to the copy, as in the lesson on your first
   server. It can read, write, edit and move files in the directory you
   give it, which is the copy (Filesystem MCP Server, GitHub, modelcontextprotocol/servers, src/filesystem). Start a logged
   session with every MCP tool definition loaded, so the log counts what
   the model reads:

   ```sh
   cd /tmp/handbook-inspect
   claude mcp add handbook -- npx -y @modelcontextprotocol/server-filesystem@2026.8.31 /tmp/handbook-inspect
   CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none OTEL_LOG_RAW_API_BODIES=file:$HOME/claude-request-log ENABLE_TOOL_SEARCH=false claude
   ```

   Run `/context` and compare the MCP tools line with step 1. Ask: "Use
   the handbook server to read on-call.md and tell me when the rota
   changes." Watch the status line after the reply.
3. In a second terminal, find the record. The status line shows its file
   name. Run `grep -l "rota changes every Monday" ~/.claude/projects/*/*.jsonl`
   and check that it is one of the files grep names. Records of the
   earlier handbook exercises can match too.
4. Quit the session, then run
   `python3 site/examples/customizing-agents/seeing-what-the-agent-sends/read_log.py ~/claude-request-log`
   from the course repository. Read the `mcp tools` column of the first
   row whose source is `repl_main_thread`, the main conversation
   (Monitoring, Claude Code documentation). If no row shows it, read the first row
   whose source isn't a title or another side request, and note its
   source. If the source column shows `-`, read the first row. If you
   do the stretch goal, do it now, before you clean up.
5. Clean up: `rm -rf ~/claude-request-log ~/claude-request-log-2`, then
   `cd /tmp/handbook-inspect && claude mcp remove handbook`. Run
   `claude purge` without a path and pick the copy from its list. The
   list may show it as `/private/tmp/handbook-inspect`. Check that the
   plan names the copy before you answer `y`
   (Explore the .claude directory, Claude Code documentation). Last, `rm -rf /tmp/handbook-inspect`.

A good result is three numbers from your own run: the MCP tools line of
`/context` before and after, and the `handbook` share in the first
main-conversation row of `read_log.py`. Write them in a short note with
one line on whether you would keep the server connected in a project
that reads its files once a week. Then ask yourself: which of the three
views would have shown you that a secret was read, and how long would it
have stayed on disk?

Stretch: Before step 5, deny the server's write tools by name in the copy's .claude/settings.json, run mkdir -m 700 ~/claude-request-log-2, run the logged claude command from step 2 with file:$HOME/claude-request-log-2, ask the same question, quit, run read_log.py on ~/claude-request-log-2, and compare the mcp tools column of its first repl_main_thread row with the one from step 4.

## Recap

1. `/context` breaks down what fills the window by category, and a status
   line can show the share in use, the input tokens and the record's path
   after every reply (Explore the context window, Claude Code documentation) (Customize your status line, Claude Code documentation).
2. The session record under `~/.claude/projects/` holds every message,
   tool call and tool result in plain text, secrets included, until
   `cleanupPeriodDays` or `claude purge` removes it
   (Explore the .claude directory, Claude Code documentation).
3. `OTEL_LOG_RAW_API_BODIES=file:<dir>` writes each request, with the
   system prompt, the tool definitions and the messages, to a local
   directory (Monitoring, Claude Code documentation). The log shows what each part
   costs in every request.
4. Turn the log on for one session, keep it in a directory outside every
   repository that only you can read, and delete it when you have your
   numbers. A gateway on `ANTHROPIC_BASE_URL` that you don't run holds a
   copy of the same data. Anthropic doesn't vouch for gateway products
   made by others (Other LLM gateways, Claude Code documentation).

#### Habit

The next time you share a transcript, a log or a request file from an agent session, search it first for keys and for file contents you would not paste into a chat.

## References

- Explore the context window, Claude Code documentation, https://code.claude.com/docs/en/context-window
- Customize your status line, Claude Code documentation, https://code.claude.com/docs/en/statusline
- Explore the .claude directory, Claude Code documentation, https://code.claude.com/docs/en/claude-directory
- Monitoring, Claude Code documentation, https://code.claude.com/docs/en/monitoring-usage
- Tool search tool, Claude Platform documentation, https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool
- Connect Claude Code to tools via MCP, Claude Code documentation, https://code.claude.com/docs/en/mcp
- Other LLM gateways, Claude Code documentation, https://code.claude.com/docs/en/llm-gateway
- Configure permissions, Claude Code documentation, https://code.claude.com/docs/en/permissions
- Filesystem MCP Server, GitHub, modelcontextprotocol/servers, src/filesystem, https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem
