13 KiB
Claude Code tool & context audit
Date: 2026-06-30 Question: Are too many built-in CC tools loaded into context, slowing sessions and hurting focus? Which can be excluded globally vs. situationally?
Method: Mechanically parsed every tool-call (tool_use) across 224 main
sessions + 1,946 subagent transcripts = 2,170 transcripts in ~/.claude/projects/,
cross-referenced against the full tools reference
(https://code.claude.com/docs/en/tools-reference.md), and measured the actual
bytes loaded into one representative session's startup context.
TL;DR — the hypothesis is mostly outdated
Excluding built-in tools used to meaningfully shrink the prompt. It no longer
does, because Claude Code now defers rarely-used tool schemas (tool search):
they load on demand via ToolSearch, not at startup.
- Total static startup context in a typical session here ≈ 6,300 tokens.
- Built-in tool schemas are a small slice of that; MCP cost = 0 (no MCP servers configured). Skills (50) ≈ 3,700 tok are the single largest contributor.
- Of 24 built-in tools never used in 2,170 transcripts, only one (
Workflow) is both always-loaded and unused. The other 23 are already deferred, platform- impossible, or trivially small.
So: a confident global tool exclusion list exists, but its payoff is small. The real levers for "feels slow / unfocused" are elsewhere (see §5).
⚠️ Honest caveat: trimming context lowers prefill latency and cost and may sharpen focus, but it does not change Opus's per-token generation speed. Opus is simply slower to generate than Sonnet/Haiku; no amount of tool pruning fixes that.
1. What tools actually get used (2,170 transcripts)
| Tool | Calls | Transcripts using it |
|---|---|---|
| Bash | 9,168 | 1,248 |
| Read | 5,955 | 1,185 |
| Edit | 1,983 | 501 |
| Agent | 1,948 | 541 |
| Write | 981 | 382 |
| WebFetch | 288 | 72 |
| ToolSearch | 169 | 139 |
| WebSearch | 124 | 36 |
| AskUserQuestion | 108 | 70 |
| Skill | 104 | 84 |
| TaskUpdate / TaskCreate | 54 / 31 | 10 / 7 |
| SendMessage | 40 | 27 |
| Monitor | 21 | 12 |
| TaskStop / ScheduleWakeup | 8 / 4 | 5 / 3 |
| RemoteTrigger / ReportFindings | 1 / 1 | 1 / 1 |
Never used anywhere (0 calls): Artifact, CronCreate/Delete/List,
EnterPlanMode/ExitPlanMode, EnterWorktree/ExitWorktree, Glob, Grep,
ListMcpResourcesTool, LSP, NotebookEdit, PowerShell, PushNotification,
ReadMcpResourceTool, SendUserFile, ShareOnboardingGuide,
TaskGet/TaskList/TaskOutput, TodoWrite, WaitForMcpServers, Workflow.
Note
Grep/Globshow 0 — search runs throughBash(ripgrep/find directly, reinforced by the RTK proxy). They are still worth keeping (see §4).
2. The decisive cross-reference: never-used ∩ actually-loaded
The "never used" list conflates three very different things. Only the first bucket gives back real context when excluded.
| Bucket | Tools | Exclusion payoff |
|---|---|---|
| Always-loaded & unused | Workflow |
Real — large schema |
| Already deferred (loaded on demand, not at startup) | CronCreate/Delete/List, EnterPlanMode/ExitPlanMode, EnterWorktree/ExitWorktree, LSP, NotebookEdit, PushNotification, TaskGet/List/Output |
~0 — not in the prompt anyway |
| Platform/plan-impossible or not loaded here | PowerShell (Linux), Artifact/ShareOnboardingGuide (need Team/Ent.), SendUserFile/PushNotification (need Remote Control), TodoWrite (disabled by default), WaitForMcpServers (only when tool-search is OFF), Glob/Grep, MCP-resource tools |
Cosmetic — either absent or trivially small |
This is why manual exclusion mattered more in the past: tool-search deferral now does the trimming automatically.
3. Where the startup context actually goes (~6,300 tok measured)
| Contributor | Est. tokens | # items |
|---|---|---|
| Skills (name + description) | ~3,700 | 50 |
| Subagent types | ~917 | 18 |
| CLAUDE.md (project) | ~842 | 1 |
| MEMORY.md auto-index | ~427 | 1 |
| RTK.md | ~241 | 1 |
| CLAUDE.md (global) | ~148 | 1 |
| MCP tool schemas | 0 | 0 servers |
| Total | ~6,300 |
Skills + agents = ~74% of static context. Heaviest single items: the built-in
claude-api skill (~269 tok), personal the-humanizer (~239), impeccable (~226),
memsearch:memory-recall (~185).
4. Recommendation A — global tool exclusions (small but safe)
Add to ~/.claude/settings.json permissions.deny. The mechanism: a denied tool is
removed from the advertised set (its schema isn't loaded), per the tools reference
("to disable a tool entirely, add its name to the deny array"). Verify by checking the
session token count before/after if you want proof.
{
"permissions": {
"deny": [
"Workflow", // 0/2170 uses; largest always-loaded built-in schema.
// KEEP it ONLY if you use /workflows or "ultracode".
"PowerShell", // Linux host — never available. Cosmetic.
"Artifact", // needs Team/Enterprise plan. Cosmetic.
"ShareOnboardingGuide" // paid team feature. Cosmetic.
]
}
}
Workflowis the only entry with real payoff. It is the multi-agent orchestration tool (the big JS-script one) — notAgent. You useAgentconstantly (1,948 calls) andWorkflownever. KeepAgent; dropWorkflowunless/until you adopt/workflows.- Do NOT deny
Glob/Grep(tiny; the sanctioned search path — denying pushes everything toBash grep/find, which can add permission prompts), anything deferred (no savings), or plan-mode/worktree/Task tools (deferred and UI-driven — denyingExitPlanModewould break Shift-Tab plan mode).
Net context saved: roughly the Workflow schema. Honest, but modest.
5. Recommendation B — the real lever: situational plugins/skills
Skills/agents (~4,600 tok) dwarf the built-in-tool headroom, and they load every
session regardless of relevance. These are controlled per-machine via
enabledPlugins in ~/.claude/settings.json (not easily per-project), so the
practical move is toggling plugins by what you're doing.
Currently enabled: superpowers (already disabled ✓), ruby-lsp, perspectives,
git-context, invoice-ninja, memory, memsearch, doc-hygiene, codex.
| Plugin / skill group | Relevant to | Drop when not… |
|---|---|---|
codex:* (5 skills + agent) |
coding handoffs to Codex | …coding |
ruby-lsp |
Ruby projects | …in a Ruby repo |
perspectives:* (9 agents) |
design/plan critique | …planning/designing |
invoice-ninja |
freelance billing | …doing client billing |
deep-research, web tools |
research | …researching |
personal writing/UI: the-humanizer, impeccable, edit-article, prototype |
content/frontend | …writing or building UI |
doc-hygiene:* |
docs repos | …maintaining docs |
memory:* + memsearch:* |
cross-session recall | rarely — but see below |
Suggested situational profiles (toggle enabledPlugins):
- Coding repo: keep
codex,ruby-lsp(if Ruby),git-context. Dropinvoice-ninja, writing/UI skills,deep-research,perspectives. - Freelance / PM / client: keep
invoice-ninja,git-context. Dropcodex,ruby-lsp,perspectives, coding skills (tdd/diagnose/run/verify). - Research / writing: keep
deep-research, writing skills. Dropcodex,ruby-lsp,invoice-ninja. - Infra / sysadmin (this repo): Bash/Read/Edit-heavy. Drop writing/UI,
invoice-ninja,codex, coding frameworks; keepdoc-hygiene,git-context.
Reality check: even the full ~6.3k static context is small next to a working session that fills 50k–150k+ tokens. Trimming skills is good hygiene and helps focus, but it is not where most of a slow session's time goes.
6. What is probably actually slowing sessions
Ranked by likely impact, from the data:
- Opus generation speed. Inherent; switch to Sonnet/Haiku for mechanical work,
or use
/fast(faster Opus output) for interactive coding. - Heavy
Agentdelegation. 67% of sessions spawn subagents; 541 transcripts usedAgent. Each subagent is a fresh context with its own prefill + sequential wall-clock wait. Great for big fan-out; pure latency for small lookups you could do inline. - Per-prompt / per-tool hooks. The
memoryplugin registers all 5 hook types, incl. PostToolUse (runs after every tool call) and SessionStart/UserPromptSubmit (the large memory dump at session start).memsearchadds more. This is a per-operation tax that tool-count pruning won't touch — worth timing/disabling to test. Most promising thing to measure next. - Long sessions → context summarization passes. Use
/clearbetween unrelated tasks rather than carrying one giant session. - Static tool/skill context (this audit) — real but the smallest of these.
Appendix — four representative sessions (loaded vs. used)
- Coding (
llf-schema/19320d96) — implement an OpenSpec change. 191 calls, only Edit/Read/Write/Bash. No web, noAgent, no skills. Pure local code loop. - Infra, wide (
proxmox-hermes-01/c24faa0a) — plan a Proxmox agent setup. Used nearly the whole belt: Bash/Agent/WebFetch/WebSearch/Read/Edit/Write/ AskUserQuestion/Skill(git-context:repo-init). The legit case for keeping web tools loaded. - Infra, ops (
systems-admin/ffed234f) — "how did the backup do this week?" 50 calls, only Bash/Read/Edit/Write. Textbook shell-only profile. - PM / orchestration (
ovh-prod-01/991913d9) — decide next server-consolidation phase. Lead agent used only Agent + AskUserQuestion; 360+ tool calls happened in 40 subagents. Never touched a file or shell itself.
Three of four would have been unaffected by stripping every web tool, every skill, and (for #1/#3) all delegation tooling.
Addendum (2026-06-30) — follow-up: skills, hooks, and orchestration
Skill loading — there IS a native lever (no "dispatcher" needed)
Per the docs: a skill's body loads only on invocation ("costs almost nothing
until you need it"); only name + description load at startup, and that listing is
budget-capped (skillListingBudgetFraction, ~1% of context; skillListingMaxDescChars
default 1,536/skill). There is no skill-level ToolSearch equivalent.
To reduce always-loaded skill weight, use skillOverrides in settings rather than
a community dispatcher skill:
"name-only"— keep the skill available but drop its description from context."off"— hide it entirely. PlusenabledPlugins,disableBundledSkills: true. The dispatcher/index pattern is not officially recommended — the budget +skillOverridesalready do it.
Hooks are synchronous but cheap (measured on this machine)
Hooks block the turn until exit (up to timeout); "async": true makes one
fire-and-forget. The memory plugin's hooks are synchronous but fast:
post_tool_use_write ~38 ms (Write|Edit only, early-exits off-vault),
session_context ~33 ms/prompt (injects ~0 tokens normally),
rtk hook ~24 ms/Bash. Negligible vs. multi-second model turns. Not a latency
source. The big memory dump is SessionStart-only (once).
Orchestration audit — subagent model mismatch (1,962 transcripts)
Model is recorded per subagent transcript. Distribution: Sonnet 36.7% · Haiku 36.5% · Opus 26.6%. By work profile:
| Subagent model | n | % that did only file-edit/shell work |
|---|---|---|
| Opus | 521 | 53% (+38% spawned their own subagents) |
| Sonnet | 720 | 64% |
| Haiku | 716 | 83% (well-matched) |
Finding: 27% of subagents run on Opus, most doing mechanical edits. Root cause:
when an Agent spawn omits model, the subagent inherits the parent model (Opus) —
so the CLAUDE.md routing table is advisory and leaks.
Subagent model resolution order (docs): CLAUDE_CODE_SUBAGENT_MODEL env →
per-invocation model param → subagent frontmatter model: → inherited main model.
Enforcement fix: set CLAUDE_CODE_SUBAGENT_MODEL=haiku (or sonnet) so
subagents default cheap instead of inheriting Opus; pin per-type model: frontmatter
for specialized agents; escalate by explicit criteria only.
Instruction tuning (pilot on one project first):
- Drop "no minimum complexity threshold" — direct-operate trivial single-file ops; delegate only when parallelizable, large, or context-heavy. Mandatory delegation of one-line edits is a net latency loss.
- Make routing enforced, not advisory (env var above + "always pass model on spawn").
- Relax "never read before delegating" — allow a cheap orienting read when paths/ structure are uncertain, to avoid blind-spec retry loops.