Claude Code: The Complete Mastery Guide (2026)
My CLAUDE.md was 4,200 tokens before I understood what it was actually for. Context costs climbed. Instructions contradicted themselves. Claude started ignoring the bottom half of the file entirely. I rewrote the whole thing from scratch at 480 tokens and the sessions got better.
This guide is what I know now after months of daily use. It covers the full stack: memory, MCP, personas, hooks, team workflow, drift prevention, and benchmarking. Not the happy path. The actual system.
1. The Real Mental Model
Most people open Claude Code and treat it like a smarter chat window. They type prompts. They get responses. They type corrections. They get frustrated when behavior is inconsistent across sessions.
The naive approach: Ask Claude to do the thing. Paste more context when it misses. Repeat. Wonder why results vary so much between Monday and Friday.
The actual system: Claude Code is a stateful agentic runtime. It runs a loop — pick a tool, execute it, observe the result, decide the next step — until the task is done or it asks you. The tools are real: file reads, bash commands, web search, MCP calls. The session is a context window with a hard ceiling. Every token in that window is either working for you or wasting budget.
The mental shift that matters: you are not prompting Claude. You are configuring an operating environment. The CLAUDE.md is not a polite instruction. The hooks are not optional reminders. The .mcp.json is not a feature list. Together, they define the rules of the machine you are running.
What I’d do first: Before writing a single line of CLAUDE.md, ask what behavior you want to be automatic — not requested, not reminded, but guaranteed. That list becomes the skeleton of every file in the system.
What breaks in practice: Treating the setup as a one-time configuration. The environment needs maintenance the same way code does. When behavior drifts, the environment is the first place to look — not the model.
2. Install and First Session
The problem
Every guide says npm install -g @anthropic-ai/claude-code. That’s deprecated. The npm path installs an older version that doesn’t support the current hook schema, the current MCP scope flags, or the current permission model.
The actual system
Use the native binary installer. It’s the path Anthropic now recommends and the one that ships the current feature set without dependency conflicts.
macOS:
# Via the native installer (recommended)
curl -fsSL https://claude.ai/install.sh | sh
# Verify
claude doctor
Linux:
curl -fsSL https://claude.ai/install.sh | sh
claude doctor
Windows (WSL2 — required, not optional):
# Inside your WSL2 shell
curl -fsSL https://claude.ai/install.sh | sh
claude doctor
Native Windows without WSL2 has known issues with stdio-based MCP servers and hook execution. Don’t fight it. WSL2 is the correct path.
First session, first sanity check
# Navigate to any repo
cd ~/projects/my-app
# Start Claude
claude
# Run the sanity check command
/doctor
The /doctor output tells you which config files are loaded, which MCP servers are connected, and whether the memory file is in scope. Read it before you trust anything else.
What to verify before trusting output:
- Memory file is loaded (
CLAUDE.md found) - No MCP auth errors
- Model is what you expect (
/modelto confirm) - Token budget visible (
/costafter one round-trip)
3. Build the Memory Layer
The context window resets between sessions. CLAUDE.md does not. That’s the entire value proposition of the file, and most people bury it under 3,000 tokens of preferences that could have been a shorter sentence.
What CLAUDE.md does — and does not do
It does: inject persistent context at the start of every session. Stack instructions, project structure, conventions, non-negotiables.
It does not: persist state across tool calls within a session. It does not survive sub-context launches. It does not transfer automatically to subagents unless explicitly referenced.
Project-level vs global memory
| Scope | File location | When to use |
|---|---|---|
| Project | .claude/CLAUDE.md or CLAUDE.md in repo root | Repo-specific rules, stack, naming, what not to touch |
| Global | ~/.claude/CLAUDE.md | Cross-project identity, personal conventions, default tools |
The global file loads first. The project file loads after. If they conflict, project wins. Keep them separate by concern: global handles how I work, project handles how this codebase works.
How to structure CLAUDE.md
Four sections. In this order.
# Identity
You are working on [project name], a [one-sentence description].
Stack: Node 22 / TypeScript / Postgres 16 / Redis 7.
# Non-negotiables
- Never push directly to `main` or `master`
- Never delete files without confirmation
- Never commit `.env` files
# Conventions
- Use named exports, not default exports
- Tests in `__tests__/` adjacent to source
- Error messages follow the format: `[SERVICE] action failed: reason`
# Current priorities
Working on: payment webhook reliability (Stripe → internal queue)
Known issues: race condition in `src/jobs/processor.ts` line 112
Under 500 tokens. Specific. Testable. The “current priorities” section is the only one that changes week to week.
What I’d do first
Write the non-negotiables first. These are the rules that, if violated once, create real problems. Everything else is preference. Non-negotiables are the floor.
What breaks in practice: Long files with soft language. “Try to use TypeScript strict mode” means nothing. “All new files must pass tsc --strict” means something. Soft language gets treated as a suggestion and eventually ignored.
4. Use MCP Servers Without Chaos
The problem
MCP looked like a feature list when I first saw it. Connect everything. Query Jira. Pull from Notion. Read Sentry. Within two weeks I had eleven servers loaded in user scope, half of them timing out on every session start, and Claude spending three tool calls checking things I didn’t ask about.
What MCP is in practice
It’s the connection layer between Claude Code and external systems. Each server exposes tools Claude can call: read a ticket, create a PR, query a database, fetch documentation. The protocol is open. The scope is configurable. The trust model matters more than the feature list.
MCP defines three capabilities a server can expose: resources (read-only data), tools (actions Claude can execute), and prompts (structured inputs for complex workflows). Most servers you’ll use are tool-heavy.
Scope: where the config lives
# Project scope — committed to repo, shared with team
claude mcp add --scope project --transport http notion https://mcp.notion.com/mcp
# User scope — personal, not committed
claude mcp add --scope user --transport http linear https://mcp.linear.app/mcp
# Verify what's loaded
claude mcp list
The split that works: project scope for team-standard tools with safe read/write access to the repo’s systems. User scope for personal tools, personal auth, and anything that’s about how you work rather than how the project works.
Keeping .mcp.json clean
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp",
"headers": {
"Authorization": "Bearer ${GITHUB_PAT}"
}
},
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp"
}
}
}
Two rules for .mcp.json: no secrets in the file (use environment variables), and only servers the whole team uses. Personal servers go in user scope and stay out of source control.
Trust and prompt injection risk
Every MCP server that fetches external content is a potential injection vector. A server that reads Slack messages and surfaces them as context can be fed a message that says “ignore previous instructions.” Treat external-content servers the way you treat external dependencies: verify the source, pin versions, scope permissions to the minimum needed.
The fix: Before connecting a server, read what tools it exposes and what data it can access. Connect servers in project scope only when the whole team has agreed on the trust boundary.
5. Add Custom Personas
The problem
The same Claude Code session that writes careful, defensive library code will also write sprawling imperative scripts if you let it. Not because the model changed — because the task context changed and nothing constrained the behavior.
Why personas help
A persona is a narrow behavioral contract for a specific task type. It doesn’t add intelligence. It removes options. The researcher persona doesn’t implement anything. The reviewer persona doesn’t rewrite anything. The implementer persona doesn’t comment on architecture.
Constraint is the point.
When to use personas instead of longer prompts
Use a persona when:
- The task type repeats (same shape of work, different input)
- The failure mode of the wrong behavior is significant (a reviewer that starts editing code)
- You work with other people who run the same sessions
Skip personas when the task is one-off or the behavioral drift doesn’t matter.
Persona structure
Personas live in .claude/agents/ as markdown files.
# .claude/agents/reviewer.md
---
name: reviewer
description: Code review agent — reads, evaluates, comments. Does not edit.
---
You are a code reviewer. Your job is to read the diff I provide and produce
a structured review: what's broken, what's fragile, what's missing tests,
what could be simpler.
You do not write code. You do not suggest rewrites. You identify problems
and explain why they are problems.
Output format:
- **Critical**: must fix before merge
- **Advisory**: worth addressing but not blocking
- **Nit**: style or naming, lowest priority
# .claude/agents/researcher.md
---
name: researcher
description: Research agent — investigates and summarizes. Does not implement.
---
You are a research agent. Given a question or a system behavior, investigate
using available tools (web search, file reads, MCP sources) and produce a
summary with sources.
You do not write implementation code. You do not edit files.
Your output is always a markdown summary with citations.
Keep each persona under 200 tokens. Specificity matters more than length. The reviewer persona that says “you do not write code” is doing more work than a 400-token persona that hedges.
6. Add Hooks for Enforcement
The problem
I had a rule in CLAUDE.md: “Never push directly to main.” Claude read it. Claude understood it. Claude pushed to main during an agentic session that ran faster than I was watching. CLAUDE.md is memory. It’s not enforcement.
What hooks actually are
Hooks are scripts that run at specific points in Claude Code’s lifecycle. They receive tool inputs via stdin as JSON, can block execution (exit 2), and can modify outputs. They run outside Claude’s reasoning — the model doesn’t decide whether the hook fires.
PreToolUse → fires before Claude executes a tool. Can block.
PostToolUse → fires after a tool runs. Can modify output or log.
UserPromptSubmit → fires when you submit a prompt. Can transform input.
Hook configuration
Hooks live in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "CMD=$(jq -r '.tool_input.command'); if echo \"$CMD\" | grep -qE 'git[[:space:]]+push.*(main|master)'; then echo 'BLOCKED: Use feature branches. Direct push to main is not allowed.' >&2; exit 2; fi"
},
{
"type": "command",
"command": "CMD=$(jq -r '.tool_input.command'); if echo \"$CMD\" | grep -qE 'rm[[:space:]]+-[^[:space:]]*r[^[:space:]]*f'; then echo 'BLOCKED: Use trash instead of rm -rf' >&2; exit 2; fi"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.path'); if [[ \"$FILE\" == *.ts || \"$FILE\" == *.tsx ]]; then npx prettier --write \"$FILE\" 2>/dev/null; fi"
}
]
}
]
}
}
Use cases where hooks beat reminders
| Rule | With reminder | With hook |
|---|---|---|
| Don’t push to main | Claude might | Claude can’t |
| Format on write | Claude sometimes | Always |
| Log all bash commands | Never | Every time |
| Block credential reads | Depends on context | Blocked at the gate |
What breaks in practice: Hooks that are too aggressive. A hook that blocks all bash and requires manual confirmation on every command defeats the purpose of agentic execution. Scope hooks to specific matchers — Bash(git push*) rather than Bash — so the pre-approved list stays narrow and targeted.
The fix: Start with one hook. The “no push to main” block. Run it for a week. Add hooks only when you find a real gap — not because a rule exists in CLAUDE.md, but because you caught Claude violating it in a session.
7. Make Team Workflows Consistent
The problem
Every developer on the team has a different global CLAUDE.md. Different default behaviors. Different MCP servers. Different assumptions about what Claude will and won’t do. The first time an agentic session runs in CI or on another developer’s machine, everything breaks differently.
Shared repo rules
The repo should contain exactly three Claude-related files committed to source control:
.claude/
├── CLAUDE.md # Project memory — stack, conventions, non-negotiables
├── settings.json # Shared permissions, shared hooks
└── .mcp.json # Team-standard MCP servers (no secrets)
Everything else is local. Hooks that play sounds. Personal agent personas. User-scope MCP servers with personal auth. Those stay out of the repo.
Onboarding a new contributor
When a new developer clones the repo, they get the project memory, the shared hooks, and the approved MCP server list. What they don’t have is auth. The onboarding checklist:
# 1. Install Claude Code
curl -fsSL https://claude.ai/install.sh | sh
# 2. Clone and navigate to repo
cd my-project
# 3. Set up environment variables (team uses a shared .env.example)
cp .env.example .env.local
# Fill in personal tokens: GITHUB_PAT, LINEAR_API_KEY, etc.
# 4. Verify setup
claude /doctor
claude mcp list
The .env.example documents which tokens are needed. The MCP config reads them from environment. No tokens in source control.
Branching and commit discipline
Put this in the project CLAUDE.md. Not as suggestions — as rules:
# Git conventions
- Branch format: `type/TICKET-description` (e.g., `fix/ENG-421-payment-timeout`)
- Commit format: Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`)
- Never force-push to any shared branch
- PRs require at least one passing CI check before merge
Pair the CLAUDE.md rule with a hook that blocks non-conventional commit messages:
MSG=$(jq -r '.tool_input.command' | grep -oP '(?<=-m ")[^"]+');
if ! echo "$MSG" | grep -qE '^(feat|fix|refactor|test|docs|chore|style|perf|ci|build|revert)(\(.+\))?: .+'; then
echo 'BLOCKED: Commit message must follow Conventional Commits format' >&2; exit 2;
fi
What breaks in practice: Individual preference files fighting shared rules. A developer with a global CLAUDE.md that says “always use semicolons” and a project that enforces no-semicolons. The project file wins in Claude Code’s loading order, but the conflict creates noise and confusion. Keep global files about behavior patterns, not code style. Code style is a project concern.
8. Prevent Drift and Debug It
The problem
The session starts following the rules. Forty messages in, it isn’t. CLAUDE.md is still there. Hooks are still running. But Claude is formatting differently, skipping the test-before-commit convention, and abbreviating output where it previously expanded.
This is context drift. The context window is full, the system instructions are competing with 80,000 tokens of conversation, and the early rules have lost priority.
Signs Claude is ignoring the system
- Formatting changes mid-session without a style change request
- Steps being skipped that were always in the workflow
- Shorter, less structured responses than the session started with
- A specific file or path that should be read-only getting edits
How to detect sub-context failures
Subagents don’t automatically inherit the parent CLAUDE.md. When Claude spawns a subagent for an isolated task, that subagent starts with its own context. If the subagent instructions don’t include the project memory reference, it’s operating without your rules.
Test it:
# In a session, ask directly
What files define your current operating rules for this project?
# If it doesn't name your CLAUDE.md, the file isn't loaded
# Check with:
/doctor
How to test whether CLAUDE.md is actually loaded
# Add a canary line to your CLAUDE.md
## Canary
If asked "what is the canary?", respond: "Orange triangle, session is healthy."
# In your session:
What is the canary?
If it doesn’t respond with the exact phrase, the file isn’t loading correctly. Check the path — Claude Code looks for CLAUDE.md in the current directory and .claude/CLAUDE.md. It does not recursively search.
What to do about drift
For long sessions: restart the session and let it re-read the files. This is not a failure — it’s expected behavior with finite context windows.
For subagent failures: explicitly pass the memory file reference in the subagent invocation:
# In your task prompt to a subagent
Before starting, read `.claude/CLAUDE.md` for project conventions.
Follow all non-negotiables listed there.
For persistent drift on specific rules: the rule needs a hook, not more prominence in the memory file. If it’s important enough to be consistently enforced, it belongs in settings.json.
9. Benchmark the Workflow
Most Claude Code setups are never measured. Impressions accumulate. “It feels faster.” “It seems to make fewer mistakes.” You can’t optimize what you haven’t measured.
The benchmark approach
Run the same task type twice: once with raw prompts in a fresh session (no CLAUDE.md, no MCP, no hooks), once with the full structured setup. Measure five numbers.
Benchmark table
| Metric | Raw prompt session | Structured Claude Code | Delta |
|---|---|---|---|
| Task completion time | — min | — min | —% |
| Correction rounds needed | — | — | — |
| Failures requiring human fix | — | — | — |
| Token cost (input + output) | $— | $— | —% |
| Files touched unnecessarily | — | — | — |
Run this on three task types: a feature implementation, a refactor, and a debugging session. Average the results. The pattern will be clear.
Repeatable scoring rubric
Task completion time: Start timer when prompt is submitted. Stop when the output passes your standard review. Don’t stop at “Claude says it’s done” — stop at “I’d merge this.”
Correction rounds: Count how many times you typed a correction or clarification after the initial prompt. Zero is ideal. More than three on a bounded task means the initial setup or prompt failed.
Failures requiring human fix: Count bugs or errors that Claude introduced that you had to fix manually. Not rejections of Claude’s approach — actual introduced errors that didn’t exist before the session.
Token cost: Use /cost at the end of each session. Claude Code reports input tokens, output tokens, and cache hits. The structured setup should reduce token waste through tighter context and fewer correction rounds.
Files touched unnecessarily: Review the session diff. Count files Claude modified that weren’t part of the task scope. A refactor that touches 22 files when the task was in 3 is a scope failure.
What the numbers tell you
A well-configured setup typically reduces correction rounds by 40–60% compared to raw prompting on the same task. Token cost can go either direction — richer context costs more upfront but prevents expensive correction loops. The metric that matters most is “failures requiring human fix.” If that number is nonzero consistently, the hooks or memory layer has a gap.
10. Real-World Use Cases
Refactoring a small codebase
The task: migrate a 3,000-line Express API from CommonJS to ES modules.
The setup that worked:
# Added to CLAUDE.md for this task
Current task: CJS → ESM migration
Files in scope: src/ only. Do not touch test/ or scripts/ without asking.
Migration steps:
1. Update package.json (`"type": "module"`)
2. Rename .js to .mjs OR update imports — pick one approach, confirm with me first
3. Update all require() calls
4. Run test suite after each file batch
The session ran for 2.3 hours, touched 47 files, required four correction rounds. The hook blocking rm -rf fired once — caught correctly, Claude was cleaning temp files in a way that would have caught the cache directory.
Writing a feature with tests
The task: implement a rate-limiting middleware for an auth endpoint.
The pattern that reduces back-and-forth:
1. Research: subagent reads existing middleware patterns in the codebase
2. Plan: Claude proposes the implementation approach, waits for approval
3. Implement: code only, no tests yet
4. Test: separate prompt, Claude writes tests against the implementation
5. Integrate: Claude wires middleware into the route, runs test suite
Split into five explicit steps rather than one “implement this with tests” prompt. Total time: 34 minutes. One correction round (test coverage missed one edge case on the rate limit reset logic).
Debugging a failing integration
The task: a Stripe webhook handler was dropping events intermittently, no consistent error.
Most valuable MCP tool for debugging: a server with access to your log aggregation. Being able to ask “show me all webhook requests from the last 6 hours where status was 200 but no database write followed” is worth more than reading files manually.
Without that MCP connection, the debugging session cost 19 tool calls and three manual log pastes. With it: 6 tool calls, one paste, found the race condition in the idempotency key check.
Generating docs from code
The persona pattern matters here. The researcher persona reads the code. Produces a summary. The implementer persona does not generate docs — docs are a researcher/reviewer task.
# Start with the researcher persona loaded
claude --agent researcher "Read src/api/ and produce an OpenAPI-style summary of every route: method, path, input schema, output schema, auth required."
Output goes into a draft. Then a second pass with a reviewer persona checks accuracy. The two-pass approach on documentation catches more inaccuracies than a single generation step.
Building a repeatable solo-dev operating system
The full structure that’s stable enough to maintain:
~/.claude/
├── CLAUDE.md # Global: identity, personal conventions
└── agents/
├── researcher.md
├── reviewer.md
└── implementer.md
[each repo]/.claude/
├── CLAUDE.md # Project: stack, conventions, current priorities
├── settings.json # Hooks, permissions
└── .mcp.json # Project-scoped servers
Six files. No overlap. Global handles you. Project handles the codebase. Hooks handle enforcement. MCP handles tools. That’s the whole system.
11. The 90-Day Fluency Roadmap
Weeks 1–2: Core setup and memory
Install the native binary. Write the first CLAUDE.md — project scope, under 500 tokens. Run /doctor until you understand what every line means. Run the same task twice: with memory and without. Measure the difference.
At the end of week 2, you should be able to answer: what does my CLAUDE.md actually contain, and why is each line there?
Weeks 3–4: MCP and persona work
Pick two MCP servers: one for your issue tracker, one for documentation fetch (Context7 is a sensible default for library docs). Add them in the correct scope. Write one persona for your most repeated task type.
At the end of week 4, you should be able to run a research task without pasting a single external link, and a review task without Claude touching implementation files.
Weeks 5–8: Hooks and team workflow
Add three hooks: block direct push to main, auto-format on write, log all bash commands to a session file. Introduce the repo-committed Claude config structure (CLAUDE.md + settings.json + .mcp.json) to one collaborator and document the onboarding steps. Run the benchmark at least twice.
At the end of week 8, you should have eliminated at least one class of error that was previously catching you manually.
Weeks 9–12: Benchmarking and optimization
Run the full benchmark table on three different task types. Identify which metric has the worst delta between raw and structured setup. Fix the gap — whether that’s a missing hook, a weak memory section, or a persona that needs tightening. Repeat.
The goal at the end of week 12 is not a setup that’s impressive. It’s a setup that’s boring — because it works the same way every time.
12. Conclusion
The goal is not a smarter Claude. The goal is behavior that doesn’t change between Monday and Friday, between your machine and your team’s CI, between the first message in a session and the fiftieth.
Smart is the model’s job. Consistent is your job.
The system that achieves consistency has four working parts: memory that’s short and specific, MCP that’s scoped and trusted, hooks that enforce what actually matters, and a benchmark that tells you when something has drifted.
The practical next step: Not “set up the full system.” One thing. Write a CLAUDE.md under 500 tokens for one active project. Run /doctor and confirm it loads. That’s the foundation. Everything else is built on whether this file actually works.
Once it does, add one MCP server. Then write one hook. Then measure.
The system that’s stable in three months is the one you built slowly and understood completely — not the one you copied from a setup guide.
→ See also: Claude Code Personal OS guide — how I turned ~/.claude/ into a portable operating environment, not just a dev tool.
→ Related: Building AI Agent Systems — agent memory blueprints for more complex multi-agent setups.