TL;DR Export
CLAUDE_CODE_DISABLE_FILE_MENTIONS=1in your shell or in theenvblock of~/.claude/settings.json. From then on@src/file.tsattaches nothing: Claude gets the path with a note and reads, with its own tools, as much as the question needs. Asking about one function in a 22 KB file cost 16,418 new tokens by default and 5,637 with the variable. Leave it off when you want the whole file in context.
Every @ in Claude Code is resolved before the model sees your message: Claude Code reads the file from disk and attaches it whole to the turn, with the CLAUDE.md of its tree thrown in, as the hidden cost of @ measures. It costs no turn, but how much goes in is decided by you when you type the @, not by Claude when it reads the question. Since v2.1.299, CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 swaps the attachment for a one-line note with the path, and Claude decides what to read and how much.
What Claude receives with the variable
The note, as it sits in the v2.1.310 binary:
The user @-mentioned src/payments.ts. File contents are not attached automatically in this session: if these are files or directories in your working directory, read them with your file tools before responding.
Nothing else: not the file, and not the listing if it was a directory. In the session's .jsonl the attachment changes type, file with the whole content against at_mention_reference with the list of paths:
jq -r 'select(.type=="attachment") | .attachment
| select(.type=="file" or .type=="at_mention_reference")
| "\(.type)\t\(.filename // (.mentions|join(",")))"' \
~/.claude/projects/<your-project>/<session>.jsonl
What each path costs
The same question, "what does handler42 return in @src/payments.ts", on a 22 KB file in a scratch project, in -p runs of Claude Code v2.1.310. New tokens are the ones the turn adds to context (cache_creation_input_tokens), the same ones re-read on every later turn:
model default with the variable
Sonnet 5.5 whole file attached, 16,418 new note + grep, 5,637 new
Haiku 5.5 whole file attached, 16,415 new note + cat of the file, 16,611 new
About 4,700 of each figure is session startup, the same in both columns. The rest is what changes:
- Sonnet ran
grep -n handler42and pulled 25 lines: close to a thousand tokens against the file's 11,700. And what stays in the history is the fragment, not the file. - Haiku read the whole file anyway, with a
cat. The same cost as the attachment, plus one call. The variable saves nothing on its own: it hands the model the decision you used to make, and a small model with a vague question reads everything.
Asking for a summary of the file saves nothing either: the model needs the whole file and reads it with Read, which adds line numbers (15,955 new over two calls against 18,222 for the attachment, on Haiku). The saving comes when the question points at a part.
The large file that already came in this way, silently
By default @ attaches the file only when it fits the Read tool's limit, 25,000 tokens. Above that, on v2.1.310 it attaches nothing and says nothing: with a 60 KB file the mention stayed as text in the message, with no attachment of any kind, and Haiku read the whole thing in two Read calls, 36,188 new tokens, to summarize it. With the variable that file arrives with the same note as the small ones, and the pointed question about it (handler217, on Sonnet) cost 5,339 new with it and 6,677 without, a grep in both cases.
How to turn it on
1. In your shell or in user settings
# this session only
CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 claude
# every session: in ~/.zshrc or ~/.bashrc
export CLAUDE_CODE_DISABLE_FILE_MENTIONS=1
Or in ~/.claude/settings.json, which applies however you launch claude:
{
"env": {
"CLAUDE_CODE_DISABLE_FILE_MENTIONS": "1"
}
}
2. Not in the project
Claude Code ignores this key in .claude/settings.json and .claude/settings.local.json, so a cloned repository can't decide what Claude reads. With --debug it says so:
[WARN] CLAUDE_CODE_DISABLE_FILE_MENTIONS in .claude/settings.json is ignored — project-scoped settings can't set this key. Set it in ~/.claude/settings.json or managed settings instead.
3. Turn it off for one session when you want the whole file
1 and true turn it on; 0 or an empty value turns it off, even with the export in place:
CLAUDE_CODE_DISABLE_FILE_MENTIONS=0 claude
4. Ask with a target
With the variable, @ only tells Claude where to look. "Review @src/auth.ts" reads the whole file; "why does refreshToken in @src/auth.ts return null" allows a grep. A directory, @src/, arrives as the same note and Claude runs ls: 173 new tokens for the listing. The other ways to hand over context are in five ways to give Claude Code the right context.
And one effect that is not about tokens: PreToolUse hooks don't run on an @ attachment, because there is no tool call. With the variable they do, because Claude reads with Read or Bash. If a PreToolUse hook decides what Claude may read, the variable closes that gap.
Reference
| Case | What Claude receives | What it costs |
|---|---|---|
| Default, file under 25,000 tokens | The whole file and the CLAUDE.md of its tree, before the first turn |
The whole file, this turn and re-read on every later one |
| Default, file above that | Nothing: the path stays in your text and Claude reads it on its own, unwarned | Whatever Claude decides to read, with no note asking for portions |
With CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 |
A note with the paths and the instruction to read them with its tools | What the question needs, plus one call |
Official docs: Environment variables
Requirements
- Claude Code v2.1.299 or later. Measured on v2.1.310, in
-pruns on a scratch project, with Sonnet 5.5 and Haiku 5.5. - The variable is read from the environment, user settings or managed settings; project settings ignore it.