OpenClaw available_skills Filtering: How Skills Get Selected for Agent Context
Problem
When I started building AI agents with skill systems, I assumed the model decided which skills to use. If a skill appeared in the <available_skills> section of the system prompt, the model could call it. Simple, right?
Not quite. I noticed skills disappearing from context without explanation. I’d configure a skill, run the agent, and it wouldn’t show up. No error message, no rejection at call time—just absence.
The model wasn’t rejecting these skills. They never reached the model in the first place.
What Happened?
Let me reconstruct the scenario.
I configured a new skill in my settings:
skills: entries: my-dangerous-skill: enabled: false # I'll enable it laterThen I expected the skill to appear in context when the agent started. But when I checked the system prompt:
<available_skills> <!-- my-dangerous-skill is missing --></available_skills>The skill wasn’t there. I checked the configuration—everything looked correct. I restarted the agent. Still nothing.
Then I noticed the enabled: false flag I’d set for testing. That was the problem.
The Four Filtering Layers
OpenClaw doesn’t just dump all configured skills into context. It applies four distinct filtering layers before any skill reaches the <available_skills> section.
Layer 1: Enable/Disable Switch
The first and most direct filter is the enabled flag:
skills: entries: dangerous-skill: enabled: false # Kill switch engaged safe-skill: enabled: true # Explicit opt-in default-skill: {} # enabled: true by defaultWhen enabled === false, the skill exits the pipeline immediately. This is your emergency shutoff for any skill that’s causing problems.
I use this for skills that are experimental or temporarily broken:
skills: entries: experimental-feature: enabled: false # Not ready for productionLayer 2: Bundled Skills Allowlist
Platform-provided skills have a separate governance mechanism:
skills: allowBundled: true # Allow platform-provided skills entries: custom-skill: enabled: trueWhen allowBundled === false, all bundled skills disappear from context—even if they’re individually enabled. This gives you control over platform-provided skills that ship with the agent framework.
// Pseudo-code for the bundled checkif (skill.source === 'bundled' && !config.skills.allowBundled) { return false; // Filter out}This separation matters for security-conscious deployments where you want to audit every skill before allowing it.
Layer 3: Environment Eligibility
Skills can declare requirements that get checked at runtime:
interface SkillRequirements { binaries?: string[]; // Required executables envVars?: string[]; // Required environment variables config?: string[]; // Required configuration keys os?: string[]; // Supported operating systems platform?: string; // Required platform (e.g., "remote")}The filtering logic checks each requirement:
// Pseudo-code for eligibility filteringfunction checkEligibility(skill: Skill): boolean { const requires = skill.requires || {};
// Check for required binaries if (requires.binaries?.some(b => !hasBinary(b))) { return false; }
// Check for required environment variables if (requires.envVars?.some(e => !process.env[e])) { return false; }
// Check for required configuration if (requires.config?.some(c => !hasConfig(c))) { return false; }
// Check OS compatibility if (requires.os && !requires.os.includes(currentOS)) { return false; }
// Check platform requirements if (requires.platform === 'remote' && !isRemotePlatform()) { return false; }
return true;}Here’s a practical example. A skill that requires Git:
skills: entries: git-operations: requires: binaries: - git enabled: trueOn a system without Git installed, this skill never appears in <available_skills>. The model never sees it, never tries to call it, and no error occurs.
Layer 4: Model Visibility Control
Some skills exist for internal use only—testing, auditing, pipeline operations. The disable-model-invocation flag hides them from the model:
skills: entries: audit-logger: disable-model-invocation: true # Internal onlyThis is different from enabled: false. The skill might pass all eligibility checks and be fully functional, but it’s marked for internal use only:
// Layer 4: Model visibilityif (skill.disableModelInvocation) { return false; // Don't show to model}I use this for skills that my orchestration layer calls directly, without model involvement.
The Complete Filtering Pipeline
Here’s how all four layers work together:
// Complete filtering logicfunction filterSkills(skills: Skill[], config: Config): Skill[] { return skills.filter(skill => { // Layer 1: Enable/disable if (config.skills.entries[skill.key]?.enabled === false) { return false; }
// Layer 2: Bundled allowlist if (skill.source === 'bundled' && !config.skills.allowBundled) { return false; }
// Layer 3: Eligibility checks const requires = skill.requires || {};
if (requires.binaries?.some(b => !hasBinary(b))) { return false; }
if (requires.envVars?.some(e => !process.env[e])) { return false; }
if (requires.config?.some(c => !hasConfig(c))) { return false; }
if (requires.os && !requires.os.includes(currentOS)) { return false; }
if (requires.platform && !isPlatform(requires.platform)) { return false; }
// Layer 4: Model visibility if (skill.disableModelInvocation) { return false; }
return true; });}Skills that pass all four checks appear in <available_skills>. Skills that fail any check get filtered out before reaching the model.
Why This Design?
The philosophy is simple: visibility precedes callability.
If a skill shouldn’t be used, don’t show it to the model. This prevents several problems:
-
No confusing error messages. The model never tries to call a skill it can’t use, so users never see cryptic “skill not found” errors.
-
Clean context window. Only relevant skills occupy context space, leaving more room for actual work.
-
Fail-fast validation. Configuration problems surface early, not mid-conversation when a user tries to invoke a missing skill.
-
Security by obscurity. Skills the model doesn’t know about can’t be accidentally invoked or exploited through prompt injection.
Common Mistakes
I made several mistakes when I first worked with this system:
Mistake 1: Checking the wrong layer. I assumed a skill was broken when it didn’t appear. Actually, I’d set enabled: false during testing and forgotten to re-enable it.
Mistake 2: Missing dependency checks. I configured a Docker skill but didn’t have Docker installed. The skill disappeared silently, and I spent hours debugging configuration that was actually correct.
Mistake 3: Confusing enabled with visible. I set disable-model-invocation: true thinking it would prevent execution. It only hides the skill—the orchestration layer can still call it directly.
Mistake 4: Platform-specific skills. I developed a skill on macOS with os: ["darwin", "linux"], then deployed to Windows. The skill vanished because Windows wasn’t in the allowlist.
Debugging Missing Skills
When a skill doesn’t appear in <available_skills>, I check in this order:
# 1. Is it explicitly disabled?grep -r "enabled: false" ~/.config/openclaw/skills/
# 2. Are bundled skills blocked?grep -r "allowBundled: false" ~/.config/openclaw/
# 3. Are dependencies missing?which git docker npm # Check for required binaries
# 4. Are environment variables set?echo $MY_REQUIRED_VAR
# 5. Is it hidden from the model?grep -r "disable-model-invocation: true" ~/.config/openclaw/skills/Configuration Best Practices
I follow these patterns for clean skill management:
# Good: Clear organization with explicit statesskills: allowBundled: true
entries: # Production skills - always visible file-operations: enabled: true requires: binaries: []
# Experimental skills - disabled by default experimental-feature: enabled: false requires: binaries: [node] envVars: [OPENAI_API_KEY]
# Internal skills - functional but hidden audit-logger: enabled: true disable-model-invocation: trueSummary
In this post, I explained how OpenClaw filters skills through four layers before they appear in <available_skills>. The key point is that visibility precedes callability—if a skill shouldn’t be used, the system hides it from the model rather than rejecting it at call time.
The four layers are: enable/disable flags, bundled skills allowlist, environment eligibility checks, and model visibility control. Understanding this pipeline helps debug missing skills and design better skill configurations.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 OpenClaw Documentation
- 👨💻 Agent Architecture Best Practices
- 👨💻 Skill-based Agent Design Patterns
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments