server-desktop-01/docs/cc-tool-audit.md

13 KiB
Raw Permalink Blame History

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/Glob show 0 — search runs through Bash (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.
    ]
  }
}
  • Workflow is the only entry with real payoff. It is the multi-agent orchestration tool (the big JS-script one) — not Agent. You use Agent constantly (1,948 calls) and Workflow never. Keep Agent; drop Workflow unless/until you adopt /workflows.
  • Do NOT deny Glob/Grep (tiny; the sanctioned search path — denying pushes everything to Bash grep/find, which can add permission prompts), anything deferred (no savings), or plan-mode/worktree/Task tools (deferred and UI-driven — denying ExitPlanMode would 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. Drop invoice-ninja, writing/UI skills, deep-research, perspectives.
  • Freelance / PM / client: keep invoice-ninja, git-context. Drop codex, ruby-lsp, perspectives, coding skills (tdd/diagnose/run/verify).
  • Research / writing: keep deep-research, writing skills. Drop codex, ruby-lsp, invoice-ninja.
  • Infra / sysadmin (this repo): Bash/Read/Edit-heavy. Drop writing/UI, invoice-ninja, codex, coding frameworks; keep doc-hygiene, git-context.

Reality check: even the full ~6.3k static context is small next to a working session that fills 50k150k+ 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:

  1. Opus generation speed. Inherent; switch to Sonnet/Haiku for mechanical work, or use /fast (faster Opus output) for interactive coding.
  2. Heavy Agent delegation. 67% of sessions spawn subagents; 541 transcripts used Agent. 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.
  3. Per-prompt / per-tool hooks. The memory plugin registers all 5 hook types, incl. PostToolUse (runs after every tool call) and SessionStart/UserPromptSubmit (the large memory dump at session start). memsearch adds 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.
  4. Long sessions → context summarization passes. Use /clear between unrelated tasks rather than carrying one giant session.
  5. Static tool/skill context (this audit) — real but the smallest of these.

Appendix — four representative sessions (loaded vs. used)

  1. Coding (llf-schema/19320d96) — implement an OpenSpec change. 191 calls, only Edit/Read/Write/Bash. No web, no Agent, no skills. Pure local code loop.
  2. 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.
  3. Infra, ops (systems-admin/ffed234f) — "how did the backup do this week?" 50 calls, only Bash/Read/Edit/Write. Textbook shell-only profile.
  4. 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. Plus enabledPlugins, disableBundledSkills: true. The dispatcher/index pattern is not officially recommended — the budget + skillOverrides already 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):

  1. 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.
  2. Make routing enforced, not advisory (env var above + "always pass model on spawn").
  3. Relax "never read before delegating" — allow a cheap orienting read when paths/ structure are uncertain, to avoid blind-spec retry loops.