Skills never trigger in -p mode with heavy config (40+ skills, MCP servers)
Summary
Skills are never auto-triggered when using claude -p in environments with many skills (40+), multiple MCP servers (8), and extensive rules/config. The model bypasses the Skill tool entirely and goes straight to built-in tools (Read, Bash, etc.), even when the skill description explicitly says "ALWAYS use this skill."
This makes the skill-creator plugin's run_eval.py and run_loop.py description optimization pipeline unusable in heavy-config environments - every should-trigger query scores 0%.
Environment
- Claude Code version: 2.1.76
- Model: claude-opus-4-6 (also tested with claude-sonnet-4-6)
- macOS (Darwin 25.3.0)
- 40+ skills loaded (project + global + plugin skills)
- 8 MCP servers connected (memory, github, playwright, ms365, obsidian, gmail, gcal, google-workspace)
- 15+ rule files in .claude/rules/
Reproduction
- Have a project with many skills installed (I have 28 custom skills in .claude/skills/ plus plugin skills)
- Run a query that should clearly trigger a skill:
claude -p "Create a PDF report summarizing API test results with a cover page, tables, and charts. Save as report.pdf" \
--output-format stream-json --verbose --include-partial-messages
- Observe: the model invokes
ReadorBashdirectly, neverSkill("pdf")
- Repeat with the skill-creator's run_eval.py:
cd ~/.claude/plugins/.../skill-creator/skills/skill-creator
python -m scripts.run_eval \
--eval-set trigger-eval.json \
--skill-path /path/to/skills/pdf \
--timeout 60 --runs-per-query 1 --model claude-sonnet-4-6 --verbose
Result: 0/10 should-trigger queries trigger. 10/10 should-not-trigger correctly don't trigger.
What I tested
| Approach | Result |
|----------|--------|
| Raw claude -p with 20s timeout (Spence method) | Zero output within timeout due to heavy config startup |
| run_eval.py temp command (skill-creator harness) | 0/10 trigger - model picks real skill over temp command |
| Custom eval checking real skill name | 0/10 trigger - model bypasses Skill tool entirely |
| --disable-slash-commands from clean /tmp dir | Skills still loaded from global config, model still goes to Read |
Expected behavior
When a query clearly matches a skill description (e.g., "create a PDF report" matching the pdf skill that says "ALWAYS use this skill when the user asks to create a PDF report"), the model should invoke Skill("pdf") at least some of the time in -p mode.
Actual behavior
The model never invokes the Skill tool in -p mode. It handles everything directly with Read, Bash, Write, etc. The skill descriptions (including "ALWAYS use this skill" and "Trigger on any mention of PDF") are completely ignored.
Impact
- The skill-creator description optimization pipeline (
run_loop.py) cannot function because the eval baseline is always 0% - There is no way to programmatically test or optimize skill triggering accuracy
- Users with rich skill libraries get no benefit from auto-triggering - skills only work via explicit
/skill-nameinvocation
Possible contributing factors
- Skill count saturation: With 40+ skill descriptions in the system prompt, the model may deprioritize skill consultation as cognitive overhead
- Built-in tool preference: The model seems trained to prefer direct tool use (Read for PDFs, Bash for scripts) over the indirection of consulting a skill first
-pmode behavior: The model may behave differently in single-prompt mode vs interactive sessions regarding skill consultation- System prompt length: The combined skill descriptions, rules, CLAUDE.md, and MCP tool definitions create a very large system prompt that may dilute skill triggering signals
Suggestion
Consider either:
- A mechanism to boost skill triggering confidence in
-pmode (or at least match interactive session behavior) - A dedicated
--test-skillflag forclaude -pthat forces the model to evaluate skills before using built-in tools - Documenting the limitation that skill triggering evals require minimal-config environments
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗