@-Mentions, Multi-Dir & Image Hygiene
Pull the right things into context — and keep them out
- Reference files, directories, and MCP resources precisely with @path, @directory, and @mcp_resource:protocol://path, using Tab completion
- Extend a running workspace with /add-dir and --add-dir, and work around the known autocomplete bug by typing full paths
- Reason about the token cost of reading large files, and distinguish piping stdin (cat | claude -p) from an actual @-read
- Diagnose and prevent the image-accumulation trap that causes permanent 400 errors on resume of image-heavy sessions
- Use /clear and /compact to manage visual artifacts, and understand why shell mode (! prefix) adds output without triggering verification
Claude Code's @-mention syntax pulls files, directories, and MCP resources into context on demand; this lesson teaches the exact reference syntax, how to extend the workspace mid-session, and how to avoid the screenshot-accumulation trap that can permanently break a session on resume.
- 1The mental model: context is a budget you spend by reading
- 2@-mentions: files, directories, and MCP resources
- 3Extending the workspace: /add-dir and --add-dir
- 4Token cost, and why piping is not reading
- 5The image trap: how screenshots permanently break sessions
- 6Image hygiene: /clear, /compact, and shell mode
The mental model: context is a budget you spend by reading
Claude Code works inside a fixed context window — 200K tokens (up to 1M in some configurations), of which only ~150–170K is usable after the system prompt, tool definitions, and CLAUDE.md. Everything Claude knows about your code at any moment is whatever has been read into that window. The window is a budget, and the single biggest lever you control is what you pull in and what you keep out.
There are two ways content lands in context, and confusing them causes real bugs:
- Pulling things in is deliberate and surgical.
@-mentionsread a specific file, directory listing, or MCP resource — exactly what you point at, nothing more. - Things accumulating is passive and dangerous. Pasted screenshots, Chrome-MCP screenshots, and shell output pile up in the session transcript whether or not you still need them. Left unmanaged, that pile-up doesn't just slow Claude down — it can make a session impossible to resume.
CONTEXT WINDOW (a budget you spend by reading)
pull IN (deliberate) keep OUT / manage (passive)
───────────────────── ───────────────────────────
@file @dir @mcp_resource screenshots stored at full res
Tab-complete after @ Chrome-MCP screenshots
full file read into context shell ! output
│ │
▼ ▼
precise, cheap if small grows silently → 400 on resumeThis lesson covers both halves: the precise syntax for pulling the right things in, and the hygiene for keeping the wrong things out before they break your session.
Key insight
Reference, don't dump
Claude can already search and read on its own. You don't need to paste a file's contents into the prompt — point at it with @path/to/file and Claude reads exactly that file. The skill is pulling in just enough to do the task, not loading the whole repo.
@-mentions: files, directories, and MCP resources
The @ prefix references something by path and reads it into context before Claude responds. There are three forms, and Tab completion works after you type @:
| Syntax | What it pulls in |
|---|---|
@path/to/file | The full contents of that file, read into context |
@directory | A listing of that directory (so Claude sees what's inside) |
@mcp_resource:protocol://path | A resource exposed by a connected MCP server (e.g. a database table, a Notion page, a Figma frame) |
A few mechanics worth internalizing:
- Tab completes after
@. Type@src/compthen press Tab to fill in the matching path — faster and less error-prone than typing the whole thing. You can drop several mentions in one prompt:Compare @src/old.ts with @src/new.ts and explain the regression. - A file mention is a full read.
@config/settings.jsonloads the entire file into context, not a summary or a slice. That is exactly what you want for a small config — and exactly what you must think twice about for a 5,000-line log (covered next). - A directory mention is a listing, not a recursive read.
@src/apishows Claude what's in that folder so it can decide what to open; it does not slurp every file. Use it to orient Claude, then let it (or you)@the specific files that matter. - MCP resources use a protocol URI. The form
@mcp_resource:protocol://pathreaches into a connected server. The available protocols depend on which MCP servers you've connected — check/mcpto see what's wired up.
Tip
Let Tab do the typing
After @, Tab completion resolves real paths from your workspace, so you avoid typos and reference files that actually exist. If a path doesn't autocomplete, that's a signal — either it's outside the workspace, or you've hit the /add-dir autocomplete bug (next section).
Extending the workspace: /add-dir and --add-dir
By default Claude Code can only touch files under your current working directory. When a task spans repos — say your app needs to read a shared library two folders up — you extend the workspace to grant access to extra directories.
| Where | Syntax | When |
|---|---|---|
| At startup | claude --add-dir ../apps ../lib | You already know you need extra dirs |
| Mid-session | /add-dir <path> | You discover mid-task you need another folder |
Both grant file access to the named directories for the session. One important limit: --add-dir grants access but does not discover those directories' own .claude/ configuration — their CLAUDE.md, settings, and skills are not loaded. You're adding files to reach, not adopting another project's setup.
There's a known, currently-live bug here you must work around:
Watch out
Known bug: /add-dir files don't appear in @ autocomplete (#5605)
Directories added with /add-dir (or --add-dir) are accessible to Claude, but their files do not show up in @ Tab completion (GitHub issue #5605). The fix is simple: type the full path manually in the @-mention instead of relying on Tab. Also note Tab does not expand ~ — write out the real path (e.g. @/Users/you/lib/util.ts) rather than @~/lib/util.ts.
Note
Access vs. discovery
--add-dir is about reachability: Claude can now read and edit files there. It is not about configuration: the added directory's .claude/ (CLAUDE.md, rules, skills, settings) is ignored. If you need that project's conventions, state them in your prompt or your own CLAUDE.md.
Token cost, and why piping is not reading
Because the context window is a budget, the size of what you reference matters. A small config file costs almost nothing. A 5,000-line generated log, a giant lockfile, or a minified bundle can swallow a large chunk of usable context in a single @-mention — and once it's in, it competes with everything else for Claude's attention. Performance degrades as context fills: it's about quality, not just whether you hit the ceiling.
Practical habits:
- Reference narrowly.
@-mentionthe specific file you mean, not a directory full of large files. - Filter before you feed. For huge logs, don't
@the whole file — pipe a slice:tail -200 app.log | claude -p "find anomalies". - Watch the meter.
/contextshows a colored breakdown of what's consuming the window;/usageshows session cost. Check them when a session feels sluggish.
That piping example exposes a subtle but critical distinction. There are two completely different ways to get content to Claude:
| Mechanism | What actually happens |
|---|---|
@path/to/file | Claude reads the file into context as a file reference |
cat file.txt | claude -p "..." | The piped text is added as stdin — Claude does not auto-read any files mentioned in it |
When you pipe, you are handing Claude a blob of text on standard input. If that text happens to name a file, Claude will not go open it unless you ask. Piping adds the bytes you piped; it does not trigger a file read.
Example
Pipe a slice, don't @ the whole log
Debugging a 50MB log? @server.log would blow your context budget and likely degrade Claude's focus. Instead: grep ERROR server.log | tail -100 | claude -p "what's the root cause pattern?". You filter first, then hand Claude only the relevant lines on stdin — cheap, focused, and effective.
Watch out
Stdin is not a file read
cat notes.md | claude -p "summarize the TODOs" works because the file's text is on stdin. But echo "see config.json" | claude -p "check it" will not make Claude open config.json — the pipe added the sentence, not the file. To have Claude read a file, @-mention it or ask explicitly.
The image trap: how screenshots permanently break sessions
Images are uniquely dangerous to context, and this is the part most users learn the hard way.
When you paste a screenshot (Cmd+V on macOS/iTerm2, Ctrl+V on Windows/Linux, Alt+V on WSL) it appears in the conversation as [Image #N]. What's not visible: that image is stored in the session's JSONL transcript at its original resolution. A few full-screen 4K screenshots are enormous. They don't compress, they don't shrink, and they don't go away on their own.
Two failure modes follow directly:
- Permanent 400 errors on resume (#34025). As images accumulate in the transcript, the stored payload grows. Eventually it exceeds what the API will accept, and every attempt to resume that session fails with a 400 error — permanently. The conversation is effectively bricked, because the offending data is baked into the saved transcript.
- Chrome-MCP screenshot token drain (#27869). If you use the Chrome integration / browser MCP, automated screenshots can be taken repeatedly and stored the same way, causing extreme token drain that burns through your context window fast.
The through-line: visual artifacts are heavy, they're stored verbatim, and they persist. Treat them as something you actively manage, not something the tool cleans up for you.
Watch out
#34025 — image accumulation can brick resume
Screenshots are saved in the session JSONL at original resolution. Enough of them and the transcript grows past the API's limit, producing a permanent 400 error every time you resume. There's no in-session undo for this once it's saved — the fix is prevention: keep image-heavy sessions short and clear visual artifacts before they pile up.
Image hygiene: /clear, /compact, and shell mode
Knowing the trap, the discipline is simple: manage visual artifacts before they accumulate, and start fresh for image-heavy work.
Two commands are your tools here:
| Command | Effect | Use it when |
|---|---|---|
/clear | Empties the context entirely (prior conversation still reachable via /resume) | Before starting a new, image-heavy task; or when a session already feels image-bloated |
/compact [instructions] | Summarizes the conversation, keeping CLAUDE.md / skills / memory | Mid-task, to reclaim context without losing the thread |
The playbook: if you're about to do a lot of visual work (mockup comparisons, browser screenshots, error captures), /clear first so you start with a clean transcript — and consider clearing again partway through. Don't let a 200-message debugging session full of 4K screenshots sit in the transcript until resume breaks. Clearing or compacting before resuming an image-heavy session is the cheap insurance against #34025.
Finally, a related context mechanic that's easy to misread — shell mode:
Tip
Shell mode (! prefix) adds output, but doesn't trigger verification
Prefixing a line with ! runs the command directly and adds its output to context with no Claude interpretation. That's great for feeding Claude the result of !git status or !npm test — but the output landing in context does not make Claude verify or act on it. If you want Claude to evaluate the result, pair the ! command with an explicit ask (e.g. run !npm test, then ask "why is that test failing?").
Example
A clean image-heavy workflow
Comparing an implementation to a Figma mockup across ten screens?
/clearto start fresh.- Paste one mockup, ask Claude to compare it to the live page, get the fix.
- Before the next screen,
/clearagain (or/compactif you need continuity).
You never let more than a screen's worth of screenshots sit in the transcript, so the session stays resumable and the token budget stays healthy.
Try it: Reference precisely, then survive the image trap
Practice both halves of the lesson in a throwaway project. Use a directory with a few small source files plus one large file (generate one if needed: seq 1 5000 > big.log).
- Reference a file: in a session, type
@big.logis intentionally heavy — instead start with@README.md(or any small file) and confirm Claude can quote its contents. Then run/contextand note the window usage. - Reference a directory: type
@src(or any folder) and confirm Claude reports a listing, not the full contents of every file. - Feel the token cost: now
@big.log, run/contextagain, and observe the jump. Then/clearto recover the budget. - Pipe vs. read: from the shell, run
tail -50 big.log | claude -p "what's the last line?"and confirm it answers from stdin. Separately runecho "open big.log" | claude -p "do it"and confirm Claude does not auto-open the file — proving stdin is not a file read. - Extend the workspace: create a sibling dir
../extrawith a file in it. Runclaude --add-dir ../extra, then try@../extra/<file>with Tab — observe the autocomplete miss (bug #5605) and confirm typing the full path works. - Image hygiene drill: start a fresh session with
/clear, paste a screenshot (any image), and run/contextto see its footprint. Then/clearagain and confirm the footprint is gone — internalize that you'd do this before a long visual session, not after it breaks.
Deliverable: a short note recording (a) the /context numbers before and after @big.log, (b) what happened with each of the two pipe commands and why they differ, and (c) one sentence on how you'd structure a 10-screenshot mockup-comparison session to avoid issue #34025.
Key takeaways
- 1Reference content surgically: `@path/to/file` reads the full file, `@directory` lists a folder, `@mcp_resource:protocol://path` pulls an MCP resource — and Tab completes after `@`.
- 2Extend the workspace with `/add-dir <path>` or `claude --add-dir <dirs>`; both grant file access but do NOT load the added dirs' `.claude/` config.
- 3Known bug #5605: `/add-dir` files don't show in `@` autocomplete — type the full path; and Tab does not expand `~`, so write the real path.
- 4Large `@`-reads spend a big slice of the 200K window and degrade focus; pipe a filtered slice (`tail -200 log | claude -p "..."`) — but stdin is NOT an auto file read.
- 5Screenshots are stored in the session JSONL at original resolution; accumulation causes permanent 400 errors on resume (#34025), and Chrome-MCP screenshots drain tokens (#27869).
- 6Use `/clear` or `/compact` to manage visual artifacts before image-heavy work; shell mode (`!`) adds output to context but does not trigger verification.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.You add a sibling library with `claude --add-dir ../shared-lib`, then type `@../shared-lib/util` and press Tab — but nothing autocompletes. What's going on, and what's the fix?
2.You run `echo "please review config.json" | claude -p "do it"`. Claude responds but never actually opens config.json. Why?
3.A teammate's session now throws a 400 error every single time they try to resume it. They'd been pasting many full-screen screenshots over a long debugging run. What's the most likely cause?
4.You prefix a line with `!` to run `!npm test`. The failing test output lands in context, but Claude just acknowledges it and doesn't investigate. What should you understand?
Go deeper
Hand-picked sources to keep learning
@-mentions, Tab completion, shell mode (! prefix), image paste shortcuts, and other in-session input mechanics.
How the 200K window is budgeted, why quality degrades as it fills, and using /context, /clear, and /compact.
The permanent 400-error-on-resume trap from screenshots stored at original resolution in the session JSONL.
Exact syntax for --add-dir, -p/--print, and stdin piping behavior.
Connecting servers and referencing their resources via @mcp_resource:protocol://path; managing connections with /mcp.
Source, changelog, and issues including #5605 (/add-dir autocomplete) and #27869 (Chrome-MCP screenshot token drain).