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

269 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
```jsonc
{
"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.