Skill Tool - Permission Isolation
Using the Skill tool for callable skills with temporary permission grants
Skill Tool - Permission Isolation
The Skill tool allows skills to be invoked as first-class tools with temporary permission grants. This enforces skill-based access patterns and prevents agents from bypassing skills to directly access underlying tools.
Overview
Traditional skills inject instructions into the system prompt, but the agent can still bypass them and call tools directly. The Skill tool solves this by:
- Making skills callable — LLM can invoke
Skill("skill-name")as a tool - Temporary permission grants — Skill's
allowed-toolsare granted during execution - Automatic revocation — Permissions are revoked after execution (RAII pattern)
- Permission isolation — Parent agent cannot bypass skills to access tools
How It Works
┌─────────────────────────────────────────────────────────────┐
│ User Prompt: "Use data-processor to read README.md" │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ LLM (Kimi/Claude/GPT-4) │
│ Calls: Skill("data-processor", prompt="Read README.md") │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ SkillTool.execute() │
│ 1. Get skill from SkillRegistry │
│ 2. Create PermissionPolicy from allowed-tools │
│ 3. Create new AgentLoop with skill permissions │
│ 4. Execute skill with temporary permissions │
│ 5. Return result │
│ 6. Permissions automatically revoked (RAII) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Result returned to LLM │
└─────────────────────────────────────────────────────────────┘Basic Usage
Step 1: Define a Skill
Create a skill file with allowed-tools:
---
name: data-processor
description: Process and analyze data files
allowed-tools: read(*), grep(*)
---
# Data Processor Skill
You are a data processing specialist. You can:
- Read files using the read tool
- Search for patterns using grep
- Analyze and summarize file contents
You CANNOT:
- Write files
- Execute bash commands
- Edit filesStep 2: Create Session with Skill
import { Agent } from '@a3s-lab/code';
const agent = await Agent.create('agent.hcl');
const session = agent.session('.', {
skillDirs: ['./skills'],
});
// LLM can now invoke the skill
const result = await session.send(
'Use the data-processor skill to read and analyze README.md'
);from a3s_code import Agent, SessionOptions
agent = Agent.create("agent.hcl")
session = agent.session(".", SessionOptions(
skill_dirs=["./skills"],
))
# LLM can now invoke the skill
result = session.send(
"Use the data-processor skill to read and analyze README.md"
)Permission Isolation
The key feature of the Skill tool is permission isolation — the parent agent can only access tools through approved skills.
Example: Restricted Agent
import { Agent, SessionOptions, PermissionPolicy, PermissionRule } from '@a3s-lab/code';
const agent = await Agent.create('agent.hcl');
// Agent can ONLY use Skill tool, not read/grep directly
const session = agent.session('.', {
skillDirs: ['./skills'],
permissionPolicy: new PermissionPolicy({
allow: [new PermissionRule('Skill(*)')], // Only allow Skill tool
deny: [
new PermissionRule('read(*)'), // Deny direct read
new PermissionRule('grep(*)') // Deny direct grep
],
defaultDecision: 'deny'
})
});
// ✅ This works - agent calls Skill tool
const result1 = await session.send(
'Use data-processor to read file.txt'
);
// ❌ This fails - agent tries to call read directly
const result2 = await session.send(
'Read file.txt'
);
// Error: Tool 'read' is blocked by permission policyfrom a3s_code import Agent, SessionOptions, PermissionPolicy, PermissionRule
agent = Agent.create("agent.hcl")
# Agent can ONLY use Skill tool, not read/grep directly
session = agent.session(".", SessionOptions(
skill_dirs=["./skills"],
permission_policy=PermissionPolicy(
allow=[PermissionRule("Skill(*)")], # Only allow Skill tool
deny=[
PermissionRule("read(*)"), # Deny direct read
PermissionRule("grep(*)") # Deny direct grep
],
default_decision="deny"
)
))
# ✅ This works - agent calls Skill tool
result1 = session.send("Use data-processor to read file.txt")
# ❌ This fails - agent tries to call read directly
result2 = session.send("Read file.txt")
# Error: Tool 'read' is blocked by permission policyUse Cases
1. Restricted Environments
Allow agents to use powerful tools only through approved skills:
# Skill: safe-executor
allowed-tools: bash(npm test), bash(cargo test)Agent can only run approved bash commands through the skill.
2. Audit Trails
Track which skills are invoked and what tools they use:
// Hook into skill invocations
session.on('tool_start', (event) => {
if (event.name === 'Skill') {
console.log(`Skill invoked: ${event.args.skill_name}`);
}
});3. Skill Composition
Build complex workflows by chaining skill invocations:
# Skill: data-pipeline
allowed-tools: Skill(data-processor), Skill(data-validator)One skill can invoke other skills.
4. Security Boundaries
Enforce security policies at the skill level:
# Skill: production-deployer
allowed-tools: bash(kubectl apply -f *), bash(helm upgrade *)Only allow specific deployment commands.
Real-World Example
Scenario: Data Analysis Agent
You want an agent that can analyze data files but cannot modify them or execute arbitrary commands.
Step 1: Create Skills
---
name: data-reader
description: Read and parse data files
allowed-tools: read(*), grep(*)
---
# Data Reader
Read and parse data files. You can:
- Read CSV, JSON, and text files
- Search for patterns
- Extract specific data---
name: data-analyzer
description: Analyze data and generate insights
allowed-tools: Skill(data-reader)
---
# Data Analyzer
Analyze data files and generate insights. You can:
- Use data-reader to load data
- Calculate statistics
- Identify trendsStep 2: Configure Agent
const session = agent.session('.', {
skillDirs: ['./skills'],
permissionPolicy: new PermissionPolicy({
allow: [new PermissionRule('Skill(*)')],
defaultDecision: 'deny'
})
});
// Agent can only access data through skills
const result = await session.send(
'Analyze sales data in data/sales.csv and identify trends'
);session = agent.session(".", SessionOptions(
skill_dirs=["./skills"],
permission_policy=PermissionPolicy(
allow=[PermissionRule("Skill(*)")],
default_decision="deny"
)
))
# Agent can only access data through skills
result = session.send(
"Analyze sales data in data/sales.csv and identify trends"
)Result:
- ✅ Agent can read data files through
data-readerskill - ✅ Agent can analyze data through
data-analyzerskill - ❌ Agent cannot write files
- ❌ Agent cannot execute arbitrary bash commands
- ❌ Agent cannot bypass skills to access tools directly
Testing
The Skill tool enforces permission isolation end-to-end:
Test Summary:
- ✅ LLM correctly invokes
Skill("file-reader")tool - ✅ Skill uses
readandgreptools internally - ✅ Permissions are granted during execution
- ✅ Permissions are revoked after execution
- ✅ Parent agent cannot bypass skill to access tools
Best Practices
1. Principle of Least Privilege
Only grant skills the minimum tools they need:
# ❌ Too permissive
allowed-tools: "*"
# ✅ Specific tools only
allowed-tools: "read(*), grep(*)"2. Skill Composition
Break complex tasks into focused skills:
# ❌ One skill does everything
allowed-tools: "read(*), write(*), bash(*), grep(*)"
# ✅ Multiple focused skills
# Skill 1: data-reader (read, grep)
# Skill 2: data-writer (write)
# Skill 3: data-processor (Skill(data-reader), Skill(data-writer))3. Clear Descriptions
Write clear skill descriptions so the LLM knows when to use them:
# ❌ Vague
description: Process data
# ✅ Specific
description: Read and analyze CSV/JSON data files, extract statistics, identify trends4. Permission Policies
Combine Skill tool with permission policies for defense-in-depth:
// Layer 1: Permission policy (only allow Skill tool)
permissionPolicy: new PermissionPolicy({
allow: [new PermissionRule('Skill(*)')],
defaultDecision: 'deny'
})
// Layer 2: Skill allowed-tools (only allow read/grep)
allowed-tools: "read(*), grep(*)"Troubleshooting
Skill Not Found
Error: Skill 'data-processor' not found
Solution: Ensure the skill is loaded:
const session = agent.session('.', {
skillsDir: './skills' // Directory containing skill files
});Permission Denied
Error: Tool 'read' is blocked by permission policy
Solution: Check the skill's allowed-tools:
# Skill must allow the tool
allowed-tools: "read(*), grep(*)"Nested Skill Invocation
Question: Can a skill invoke another skill?
Answer: Yes! Use Skill(*) in allowed-tools:
# Skill: orchestrator
allowed-tools: "Skill(data-reader), Skill(data-writer)"Related
- Skills System — Complete skills documentation
- Security — Security features and permissions
- Tools — Built-in tools reference