Manage skills and tool sets through the Control Plane SDK
The Skills Service gives you programmatic control over the capabilities and tool sets that agents can use on the Kubiya platform. Skills are collections of tools and permissions that define what an agent can do, such as managing cloud resources, running scripts, or integrating with external systems. By managing skills, you can tailor agent abilities to your organization’s needs, enforce best practices, and ensure agents only have access to the tools they require.With this service, you can:
List and filter available skills to see what capabilities are available for your agents, including their categories and tool sets.
Retrieve detailed skill information to understand what each skill enables, which tools it includes, and any required integrations.
Create and update custom skills to define new sets of capabilities or modify existing ones as your requirements evolve.
Validate skill configurations before deployment, ensuring your skills are correctly set up and ready for use.
Associate skills with agents, teams, or other entities to control who can use which capabilities.
This service is especially valuable for platform administrators and advanced users who want to manage agent permissions, standardize tool usage, and maintain security and compliance. By using the Skills Service, you can confidently control what agents can do, avoid misconfigurations, and streamline the process of updating agent capabilities.
For conceptual information about skills and how they’re used in agents, see Skills Core Concepts.
The Skills Service provides a set of high-level methods designed to be intuitive and flexible, supporting a wide range of operational and administrative tasks:
list(skip, limit): Retrieve all available skills, with support for pagination. This is the primary entry point for discovering which skills are available in your workspace.
get(skill_id): Fetch detailed information for a specific skill, including its tools, capabilities, and configuration. Use this to inspect or audit skills before assigning them to agents.
create(skill_data): Register a new custom skill. This is typically used to define new sets of capabilities or tool sets for your organization.
update(skill_id, skill_data): Modify the configuration of an existing skill, such as updating its description, tools, or required integrations.
delete(skill_id): Remove a skill from the registry. This operation is restricted to ensure that no active agents or teams are using the skill at the time of deletion.
associate(entity_id, skill_ids): Assign skills to agents, teams, or other entities to control access to capabilities.
validate(skill_config): Validate a skill configuration before deployment. This helps catch errors early and ensures your skills are ready for use.
By using these methods, you can build robust, dynamic workflows that adapt to changes in skill requirements, enforce organizational policies, and provide a seamless experience for both developers and end-users.The following sections provide practical guidance, detailed examples, and best practices for leveraging the Skills Service effectively in your own projects.
from kubiya import ControlPlaneClient# Initialize clientclient = ControlPlaneClient(api_key="your-api-key")# List all available skillsskills = client.skills.list()for skill in skills: print(f"Skill: {skill['name']} - Category: {skill.get('category', 'N/A')}")# Get specific skill detailsskill = client.skills.get("skill-uuid")print(f"Tools: {skill.get('tools', [])}")
# List skills with paginationskip = 0limit = 20while True: skills = client.skills.list(skip=skip, limit=limit) if not skills: break for skill in skills: print(f"Skill: {skill['name']}") skip += limit
def filter_skills_by_category(category: str): """Filter skills by category""" all_skills = client.skills.list(limit=1000) # Get all skills return [s for s in all_skills if s.get('category') == category]# Usagecloud_skills = filter_skills_by_category("Cloud")print(f"Cloud skills: {len(cloud_skills)}")devops_skills = filter_skills_by_category("DevOps")print(f"DevOps skills: {len(devops_skills)}")
def associate_skills_bulk(entity_id: str, skill_names: list): """Associate skills by name""" # Find skill IDs by name all_skills = client.skills.list(limit=1000) skill_ids = [] for name in skill_names: for skill in all_skills: if skill['name'].lower() == name.lower(): skill_ids.append(skill['uuid']) break if len(skill_ids) != len(skill_names): print(f"Warning: Only found {len(skill_ids)} of {len(skill_names)} skills") # Associate skills result = client.skills.associate(entity_id, skill_ids) return result# Usageskill_names = ["kubernetes", "terraform", "aws"]result = associate_skills_bulk("agent-uuid", skill_names)print(f"Associated {len(skill_names)} skills")
The following examples show how to use the Skills Service for common real-world scenarios, such as discovering skills that match specific requirements, auditing your skill inventory, and managing skill configurations. Each example includes a short explanation of when and why you might use it.
Use this approach to find skills that match a set of required capabilities or categories. This is helpful when you want to assign the most relevant skills to an agent or team based on their responsibilities.
def discover_skills(requirements: dict): """Discover skills matching requirements""" all_skills = client.skills.list(limit=1000) required_capabilities = set(requirements.get('capabilities', [])) required_category = requirements.get('category') matching_skills = [] for skill in all_skills: skill_capabilities = set(skill.get('capabilities', [])) skill_category = skill.get('category') # Check category match if required_category and skill_category != required_category: continue # Check capabilities match if required_capabilities and not required_capabilities.issubset(skill_capabilities): continue # Calculate match score match_score = len(required_capabilities & skill_capabilities) matching_skills.append({ "skill": skill, "match_score": match_score }) # Sort by match score matching_skills.sort(key=lambda x: x['match_score'], reverse=True) return matching_skills# Usagerequirements = { "capabilities": ["cloud_management", "automation"], "category": "Cloud"}matches = discover_skills(requirements)print(f"Found {len(matches)} matching skills:")for match in matches[:5]: # Top 5 skill = match['skill'] print(f" - {skill['name']} (score: {match['match_score']})")
Generate a summary report of all skills in your environment, including how many are enabled, disabled, or missing descriptions. This is useful for platform administrators who need to audit skill inventory or prepare compliance reports.
def generate_skill_audit(): """Generate audit report of all skills""" all_skills = client.skills.list(limit=1000) audit = { "total_skills": len(all_skills), "enabled": 0, "disabled": 0, "by_category": {}, "tool_count": 0, "skills_without_description": [] } for skill in all_skills: # Count enabled/disabled if skill.get('enabled', True): audit['enabled'] += 1 else: audit['disabled'] += 1 # Count by category category = skill.get('category', 'Uncategorized') audit['by_category'][category] = audit['by_category'].get(category, 0) + 1 # Count tools audit['tool_count'] += len(skill.get('tools', [])) # Track skills without description if not skill.get('description'): audit['skills_without_description'].append(skill['name']) return audit# Usageaudit = generate_skill_audit()print(f"Skill Audit Report:")print(f" Total Skills: {audit['total_skills']}")print(f" Enabled: {audit['enabled']}")print(f" Disabled: {audit['disabled']}")print(f" Total Tools: {audit['tool_count']}")print(f"\nBy Category:")for category, count in audit['by_category'].items(): print(f" {category}: {count}")
Use this helper class to create, validate, and clone skill configurations efficiently. This is valuable for organizations that want to standardize skill creation or quickly replicate existing skills for new use cases.
Check that all required integrations for a skill are available before deployment. This helps prevent runtime errors and ensures your skills are ready to use in your environment.
def validate_skill_dependencies(skill_config: dict): """Validate that all required integrations are available""" required_integrations = skill_config.get('required_integrations', []) if not required_integrations: return {"valid": True, "message": "No dependencies required"} # Get available integrations available_integrations = client.integrations.list() available_names = [i['name'] for i in available_integrations] missing = [] for required in required_integrations: if required not in available_names: missing.append(required) if missing: return { "valid": False, "message": "Missing required integrations", "missing": missing } return { "valid": True, "message": "All dependencies available", "required": required_integrations }# Usageskill_config = { "name": "aws-ops", "required_integrations": ["AWS", "Slack"]}deps_check = validate_skill_dependencies(skill_config)if deps_check['valid']: print(f"✅ {deps_check['message']}")else: print(f"❌ {deps_check['message']}") print(f"Missing: {deps_check['missing']}")
When working with skills, you may encounter errors such as requesting a non-existent skill, creating a skill with missing fields, or failing validation. These examples show how to handle such errors gracefully and provide fallback options to keep your workflows running smoothly.
from kubiya.resources.exceptions import SkillErrortry: # Try to get a skill skill = client.skills.get("non-existent-skill")except SkillError as e: print(f"Skill error: {e}") # Handle error - maybe list all skills instead print("Available skills:") skills = client.skills.list(limit=10) for skill in skills: print(f" - {skill['name']}")# Create with error handlingtry: skill_data = { "name": "test-skill", "category": "Testing" } skill = client.skills.create(skill_data) print(f"Created skill: {skill['uuid']}")except SkillError as e: print(f"Creation failed: {e}")# Validate with error handlingtry: validation = client.skills.validate(skill_data) if not validation.get('valid'): print(f"Validation errors: {validation.get('errors')}")except SkillError as e: print(f"Validation error: {e}")
Follow these best practices to make your use of the Skills Service more robust, efficient, and maintainable. These patterns help you avoid common pitfalls and ensure your code is resilient to changes in skill availability or configuration.
Always validate your skill configuration before creating or updating a skill. This helps catch errors early and ensures your skills are correctly set up for use by agents and teams.
# Always validate skill configuration before creatingdef create_skill_safely(skill_data: dict): """Create skill with validation""" # Validate first validation = client.skills.validate(skill_data) if not validation.get('valid'): raise ValueError(f"Invalid skill configuration: {validation.get('errors')}") # Create skill skill = client.skills.create(skill_data) return skill# Usageskill_data = { "name": "my-skill", "category": "Custom", "tools": [{"name": "tool1", "type": "shell"}]}try: skill = create_skill_safely(skill_data) print(f"✅ Created skill: {skill['uuid']}")except ValueError as e: print(f"❌ {e}")
To reduce API calls and improve performance, cache skill and skill details information locally after the first retrieval. This is especially useful in applications that repeatedly access the same skill details.
# Cache skills to reduce API callsclass SkillCache: def __init__(self, client): self.client = client self._skills_cache = None self._skill_details_cache = {} def get_all_skills(self, force_refresh=False): """Get cached skills""" if self._skills_cache is None or force_refresh: self._skills_cache = self.client.skills.list(limit=1000) return self._skills_cache def get_skill(self, skill_id: str, force_refresh=False): """Get cached skill details""" if skill_id not in self._skill_details_cache or force_refresh: self._skill_details_cache[skill_id] = self.client.skills.get(skill_id) return self._skill_details_cache[skill_id] def invalidate_cache(self): """Clear the cache""" self._skills_cache = None self._skill_details_cache = {}# Usagecache = SkillCache(client)skills = cache.get_all_skills() # API callskills_again = cache.get_all_skills() # From cache
Choose clear, descriptive names and categories for your skills. This makes it easier for others to understand what each skill does and helps with auditing and compliance.
# Good - descriptive namesskill_data = { "name": "aws-ec2-management", "description": "Manage AWS EC2 instances including start, stop, and monitoring", "category": "Cloud/AWS", "tools": [...]}# Bad - vague namesskill_data = { "name": "skill1", "description": "Does stuff", "category": "Other", "tools": [...]}
Provide clear descriptions for all tools within a skill. This ensures that anyone using or maintaining the skill understands what each tool does and how to use it safely.
# Include clear descriptions for all toolsskill_data = { "name": "database-admin", "category": "Database", "tools": [ { "name": "query_database", "description": "Execute SQL queries against the database", "type": "shell", "command": "psql -c \"{{query}}\"", "parameters": [ { "name": "query", "description": "SQL query to execute", "required": True } ] } ]}