Skills consume full token count at startup instead of progressive disclosure (frontmatter only)

Open 💬 17 comments Opened Dec 20, 2025 by atournayre

Problem

According to the skills documentation, skills should use progressive disclosure:

"Claude reads these files only when needed, using progressive disclosure to manage context efficiently."

The expected behavior:

  1. Metadata (name + description) - Always in context (~100 words)
  2. SKILL.md body - When skill triggers (<5k words)
  3. Bundled resources - As needed by Claude

Observed behavior

Running /context shows skills consuming their full token count at startup, not just the frontmatter:

Skills and slash commands · /skills

Plugin
└ Skill Development: 5.5k tokens
└ Command Development: 4.6k tokens
└ Hook Development: 3.9k tokens
└ plugin-dev:create-plugin: 3.7k tokens
└ Plugin Structure: 3.2k tokens
└ MCP Integration: 3.0k tokens
...

These are from the official claude-plugins-official/plugin-dev plugin.

The irony

The "Skill Development" skill (5.5k tokens) explicitly states:

"Keep SKILL.md lean: Target 1,500-2,000 words"

Yet it contains ~5000+ words in its body, and appears to be fully loaded at startup.

Questions

  1. Is /context showing the total potential size or what's actually loaded?
  2. If progressive disclosure works correctly, why do skills show full token counts?
  3. If the full body IS loaded at startup, is this intended behavior or a bug?

Impact

With multiple plugins installed, skills alone consume 50k+ tokens before any conversation starts, severely limiting the usable context window.

Environment

  • Claude Code version: 2.0.74
  • OS: Linux
  • Multiple plugins installed from official marketplace

Expected behavior

Only frontmatter (~100 tokens per skill) should be loaded at startup. The body should load only when the skill is invoked.

View original on GitHub ↗

17 Comments

atournayre · 6 months ago

Additional UX concern

If the workaround is to enable/disable plugins per task, this creates significant friction:

  • Working on Symfony feature → enable symfony plugin, disable others
  • Switching to git operations → enable git plugin, disable symfony
  • Back to coding → re-enable dev plugins

This defeats the purpose of having a plugin ecosystem. Users shouldn't have to manually manage plugin state for every context switch.

Suggested solutions:

  1. Fix progressive disclosure - Only load frontmatter at startup (as documented)
  2. Lazy loading - Load skill bodies on first invocation, cache for session
  3. Context-aware loading - Auto-detect relevant plugins based on current task/files
  4. Plugin profiles - Quick-switch between plugin sets (e.g., "symfony-dev", "git-ops")

The current state makes multi-plugin workflows impractical for users with limited context windows.

github-actions[bot] · 6 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/14834
  2. https://github.com/anthropics/claude-code/issues/14851

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

dpaluy · 6 months ago

Controlled Test Confirms: Full Content Loading (Not Display Bug)

I ran a controlled experiment to definitively answer the question raised in this issue.

Test Methodology

Created a minimal test skill with known token sizes:

---
name: test-token-measurement
description: Test skill for measuring token loading behavior.
---

# Test Skill

This is additional content that should NOT load if only descriptions are loaded at startup.

## Section One
Lorem ipsum dolor sit amet...
[~200 tokens total content]

Expected vs Actual

| Loading Behavior | Expected Tokens |
|-----------------|-----------------|
| Description only (per docs) | ~10-15 tokens |
| Full content | ~170-220 tokens |

Actual result from /context:

└ test-token-measurement: 156 tokens

Conclusion

156 tokens ≈ full content, NOT description-only (~10-15)

This proves:

  1. /context shows actual token consumption, not "potential maximum"
  2. Skills are fully loaded at startup, contradicting documentation which states: "At startup, Claude loads only the name and description of each available Skill"
  3. This is a loading behavior issue, not just a display bug (contra #14834)

Additional Evidence

| Skill | Expected (description-only) | Actual | Matches |
|-------|----------------------------|--------|---------|
| agent-sdk-guide | ~60 tokens | 1.8k tokens | Full content ✓ |
| brainstorming | ~50 tokens | 566 tokens | Full content ✓ |
| test-token-measurement | ~15 tokens | 156 tokens | Full content ✓ |

Environment

  • Claude Code v2.0.76
  • macOS Darwin 25.1.0
  • Model: claude-opus-4-5-20251101

Related

  • #15530 - Same finding with project skills
  • #14834 - Claims display-only bug (this test disproves that theory)
esotericalsolutions · 6 months ago

@dpaluy quick question, if you remove the skills from your skills directory, does the context go down at all? (i.e. overall percentage)?

I removed all skills and they no longer show on /context, but context hasn't gone down, leading me to believe that perhaps this is just a visual representation of what they WILL load when they load in (which is misleading, but my context window did not reduce more than a very small amount for the meta).

gene-jelly · 6 months ago

Adding data from investigating this today:

Partial workaround exists: ENABLE_EXPERIMENTAL_MCP_CLI=true enables lazy loading for MCP tool schemas (reduced MCP from ~71% → ~16% of context). However, this doesn't affect skills/agents.

Potential path forward: Issue #12836 requests Tool Search beta support, which would allow marking tools/skills with defer_loading: true. This is already available at the API level and could provide a unified solution rather than separate implementations for MCP vs skills.

Current state: MCP lazy loading ✅ | Skills lazy loading ❌

dpaluy · 6 months ago

@esotericalsolutions removing all skills didn't show me the empty context. I assume either it's a Claude Code bug or Anthropic prefills the context with other content until the limit is reached.

I was experimenting with ENABLE_TOOL_SEARCH=true but @gene-jelly recommendation looks interesting.

esotericalsolutions · 6 months ago

@dpaluy Interesting, I didn't get down to zero (there are lots of things I could see it come up with) but I did notice only a very minor shift (in what I anticipated would just be the meta frontloading) between having any skills in the directory and the directory empty.Twill be interesting to see what the devs find.

atournayre · 6 months ago

Seems to be fixed in 2.1.1

PaulRBerg · 6 months ago

This was just a display/visual bug in /context command. Skills' token usage was incorrect.

See https://github.com/anthropics/claude-code/issues/14834

The bug was fixed in v2.1.1

s-celles · 6 months ago

Token count discrepancy in /skills vs actual content

Following up on the discussion about token counting accuracy. I ran some tests using the official Claude tokenizer (Xenova/claude-tokenizer from HuggingFace, which is the tokenizer from Anthropic's SDK).

Test case

File: skills/julia-development/SKILL.md (~389 lines)

| Method | Token count |
|--------|-------------|
| /skills command | 34 tokens |
| tiktoken (cl100k_base - OpenAI) | 2,180 tokens |
| Xenova/claude-tokenizer (Anthropic SDK) | 2,439 tokens |

Analysis

The /skills output (~34 tokens) matches only the YAML frontmatter (~32 tokens when tokenized):

---
name: julia-development
description: Expert guidance for Julia package development...
---

This suggests /skills displays metadata tokens only, which is correct per the progressive disclosure design. However, there's no way for users to know:

  1. What's actually in context - Is only metadata loaded, or the full body?
  2. True cost when activated - The skill body is 2,439 tokens, ~70x what /skills shows

Suggestion

Add a flag or column to /skills showing both:

  • Loaded: tokens currently in context (metadata only)
  • Full: total tokens when skill is invoked

Example:

julia-development · 34 tokens (loaded) / 2,439 tokens (full)

This would give users visibility into the actual context budget impact.

Environment

  • Claude Code: 2.1.3
  • Tokenizer: Xenova/claude-tokenizer via transformers library
grahama1970 · 4 months ago

Real-world impact data from a 246-skill power user

I have 246 custom skills. In today's session (2026-03-16), the full skill list was re-injected in every <system-reminder> block — which fires on nearly every tool result (Bash, Read, Write, Glob, Grep, etc.).

Measured impact this session:

  • ~40+ tool calls = ~40+ full skill list injections
  • Each injection: ~30-50K tokens of skill descriptions
  • Estimated total waste: 1.2-2M tokens of repeated, identical skill metadata
  • At Opus 4.6 pricing on Max plan, this is significant cost for zero value
  • The skill descriptions are static per session — they never change between messages

The system-reminder attached to THIS comment (visible in the conversation transcript) contains the full 246-skill list. Again. For the 43rd+ time this session.

Proposed fix (progressive disclosure):

  1. Inject skill names only (one line each) in system reminders — ~2K tokens instead of ~40K
  2. Use the existing <available-deferred-tools> mechanism to defer full skill descriptions until the Skill tool is actually invoked with a specific skill name
  3. Or: inject full descriptions once at session start, then only names in subsequent reminders
ivanovishado · 4 months ago

Was there a regression recently? I feel this has been happening again in the last few days.

SutanuNandigrami · 4 months ago

Is this not getting fixed to keep Anthropics earnings up ?

jvalenzano · 3 months ago

Real-world data from a power user setup (v2.1.92, Opus 4.6 1M context)

I ran forensics on my session startup context consumption and can confirm this issue at scale:

  • 22 enabled plugins (mix of official + custom) producing 81+ skill entries in the system-reminder
  • 46 custom skills totaling 1.37MB on disk (top skill: 100KB)
  • ~15-20% of 1M context consumed before typing a single command

The skill listing alone (name + multi-line description × 81 entries) costs an estimated ~4-5K tokens per session. Combined with CLAUDE.md files, auto-memory, tool schemas, and output reservation, I'm at 81% context remaining after just running /resume.

Notably, MCP tools already have lazy-loading via Tool Search (auto-triggers at 10% context threshold, achieving ~85% token reduction). Skills have no equivalent mechanism — every skill description is fully injected into the system-reminder at startup regardless of whether it will be used.

I've filed a companion feature request for "SkillSearch" — the Tool Search analog for skills — as a constructive design proposal: #43816

The progressive disclosure the docs promise (frontmatter only at startup) would be the minimum fix. SkillSearch would be the proper long-term solution.

edenfunf · 2 months ago

Heavy Claude Code user here — the behavior flagged in this issue (skill bodies loading even when frontmatter / description should suffice) is exactly the worst-case I kept running into on my own setup, so I built promptc to measure it.

It walks .claude/ and reports a per-skill exposure multiplier (body_tokens / description_tokens), the aggregate worst-case load if every skill is fully loaded, and an A–F context-debt grade. I cite this issue in the report's methodology section because it's the public evidence that the loading behavior described in the docs isn't always what users observe in practice.

Local-only, no API keys:

pip install promptc
promptc analyze .

Would value any feedback — especially whether the multiplier numbers match the intuition of folks who've hit this in production.

ray-amjad · 2 months ago

Hitting this hard on 2.1.116. With ~50 skills installed (mostly the bundled document-skills plugin pack plus a handful of project + personal skills), the full skill catalog — every skill's name plus its full multi-line description — gets re-injected as a <system-reminder> block after every single tool result and every user prompt.

Concrete observation from one session: a smoke-test task that involved ~7 parallel curl calls and a few short follow-up questions reached 120K input tokens after roughly 10 user turns. Scrolling the transcript, the skill catalog blob appears 8+ times verbatim, ~10–15K tokens each. That's the entire token climb — the actual work is a tiny fraction.

This is the same root cause as #35051 / #42650 / #46339. The behavior I'd expect (and what most users seem to expect): the full catalog is sent once at session start, and subsequent reminders only carry deltas or session-relevant context. Today every reminder is a full re-broadcast.

Compounding factors I noticed:

  1. Plugin packs (e.g. document-skills with ~15 sub-skills) inject every sub-skill description individually — half the catalog volume comes from one pack the user often doesn't need on a given project.
  2. Disabling individual skills inside a plugin pack appears not to take effect (matches #13344).
  3. Even when prompt caching covers these blocks server-side, the displayed token count + the visible context-window pressure pushes users to start fresh sessions, which defeats caching anyway.

Would strongly support the deferred-skill-discovery design proposed in #42650 (lazy-load like ToolSearch handles deferred tools). Even an interim flag like skills.alwaysLoad: false per-skill — mirroring the alwaysLoad MCP option added in 2.1.121 — would unblock heavy users.

antmid · 2 months ago

Confirming on 2.1.x with 148 SKILL.md files installed (plugins + personal + project). Measured: ~41KB of skill descriptions inject at session start ≈ ~10K tokens before any user turn.

This ticket should stay distinct from the NOT_PLANNED closures (#29711, #35051, #42650) because the framing is a docs contract violation, not a feature: the skills docs promise "progressive disclosure" and "Claude reads these files only when needed" — observed behavior loads every description at startup.

Precedent already shipped: ToolSearch defers ~75 tool schemas (visible in any session — Cron, Task, MCP tools listed by name only). For comparison: that mechanism saves ~37K tokens per session in my setup — 3-4× larger than the skill surface. The pattern works.

Asymmetry: tools defer, skills don't.