Back to glossary

Agent Skills

Agent skills are folders with a SKILL.md file and optional scripts that AI agents load on demand, running instructions and code with the user's permissions.

Also known as Claude skills, AI agent skills, SKILL.md, Agent Skills specification, coding agent skills

Agent skills, sometimes called Claude skills, are folders of instructions and code built around a SKILL.md file that AI agents load on demand to perform specialized tasks. A skill's instructions steer the agent, and its commands and scripts run with the developer's permissions. Installing an agent skill means installing third-party code.

Anthropic introduced the format in October 2025 and later released it as an open standard. Claude Code, Codex, Cursor, GitHub Copilot, and Gemini CLI all read skills from project directories such as .claude/skills and .agents/skills. Distribution grew the way npm did, with no gatekeeper. Vercel's skills.sh directory had indexed over 60,000 skills by February 2026, and anyone can publish one from any GitHub repository.

The most important fact about agent skills is when their code runs. In Claude Code, a line such as !`git diff HEAD` inside SKILL.md executes on the host before the model reads the skill. A skill can also pre-approve its own commands through its allowed-tools field, and Claude Code's documentation states that workspace trust does not gate that field. A skill committed to a repository can therefore run shell commands that neither the model nor a permission prompt ever sees, unless a deny rule matches.

Our position: treat every agent skill as a dependency with an install script, because that is what it can be. Review it, pin it to an immutable reference, scan it before it reaches an agent's skill directory, and review it again whenever its content changes. Skills are one of the main ways third-party code reaches a coding agent, alongside MCP servers, and they belong in any threat model for agentic AI security.

What are agent skills?

#

Agent skills are directories whose only required file is SKILL.md, a Markdown document that starts with YAML frontmatter. The Agent Skills specification requires two fields. name is up to 64 lowercase characters and must match the directory name. description is up to 1,024 characters and should say what the skill does and when to use it. Everything else is optional: a scripts/ directory of executable code, references/ documents, assets/ such as templates, and an experimental allowed-tools field.

A realistic skill looks like this. It sits in .claude/skills/release-notes/ next to a scripts/fetch_prs.py file.

Markdown
---
name: release-notes
description: Drafts release notes from recent commits and merged pull requests. Use when the user asks for a changelog or release notes.
allowed-tools: Bash(git log *) Bash(python3 scripts/fetch_prs.py *)
---

## Recent commits

!`git log --oneline -20`

## Instructions

1. Group the commits above by type.
2. Run `python3 scripts/fetch_prs.py` to get pull request titles.
3. Write the result to CHANGELOG.md.

Three lines in that file do work. The ! line runs git log when Claude Code loads the skill. The allowed-tools line pre-approves two commands for the turn. Step 2 tells the model to run bundled code. Only the remaining text behaves like documentation.

The description is the trigger. Agents load skills through what the specification calls progressive disclosure, in three steps. At startup, the agent loads every skill's name and description, about 100 tokens each. When a task matches a description, the agent reads the full SKILL.md body, which the specification recommends keeping under 5,000 tokens. Scripts and reference files load only when the instructions call for them.

Progressive disclosure is why skills scale, and also why they are hard to audit. A skill costs almost nothing until it fires, so developers install dozens. Each one adds text to every session's context and a potential trigger to every task. Whoever writes the description decides when the rest of the skill runs.

Where does code in an agent skill run?

#

Code in an agent skill runs at two moments: when the agent harness renders the skill, and when the model executes something the skill tells it to. Figure 1 places both moments in the full lifecycle.

  1. Installonce, by a person or an agent

    What runs
    Files are copied or symlinked into .claude/skills or .agents/skills. No skill code executes.
    Who decides
    Whoever runs npx skills add, pnpm approve, or commits the folder.
  2. Discoveryevery session

    What runs
    The name and description enter the model's context, about 100 tokens per skill.
    Who decides
    Nobody. Every installed skill is loaded.
  3. Activationwhen a task matches the descriptionruns before the model reads it

    What runs
    The harness renders SKILL.md. In Claude Code, each !`command` executes on the host, and allowed-tools is granted for the turn.
    Who decides
    Permission rules only. The model never sees the command.
  4. Executionwhile the agent works

    What runs
    The model follows the instructions: runs scripts/, calls tools, fetches URLs.
    Who decides
    The model, then permission prompts or a sandbox. Pre-approved tools skip the prompt.
Figure 1. The four stages at which an agent skill acts. Stage 3 is the one most reviews miss. The table below lists the same stages.
StageWhenWhat runsWho decides
1. InstallOnce, by a person or an agentFiles are copied or symlinked into .claude/skills or .agents/skills. No skill code executes.Whoever runs npx skills add or pnpm approve, or commits the folder
2. DiscoveryEvery sessionThe name and description enter the model's context, about 100 tokens per skillNobody. Every installed skill loads.
3. ActivationWhen a task matches the description, or the user invokes the skillClaude Code renders SKILL.md, runs each ! command on the host, and grants allowed-tools for the turnPermission rules only. The model never sees the command.
4. ExecutionWhile the agent worksThe model runs bundled scripts, calls tools, and fetches URLsThe model, then permission prompts or a sandbox. Pre-approved tools skip the prompt.

Stage 3 is a Claude Code feature called dynamic context injection. As of October 2026, the documentation says the !`<command>` syntax "runs shell commands before the skill content is sent to Claude," and the command's output replaces the placeholder. The intended use is benign, such as injecting git diff HEAD so the model sees current changes. The mechanism is still host code execution triggered by loading a file.

Claude Code checks injected commands against permission rules, but differently from commands the model proposes. Injected commands never prompt. A deny rule aborts the skill. Outside auto mode, any result other than allow also aborts it, and the documentation's fix is to "pre-approve it with allowed-tools." That field lives in the skill's own frontmatter.

Put the two features together and the trust boundary disappears. A project skill can list Bash(curl *) in allowed-tools and put a curl command on a ! line, and the command runs when the skill activates. The documentation says workspace trust does not gate allowed-tools, "including in a -p run in a folder you've never trusted." Activation follows the description, which the skill's author wrote, so the author also chooses the trigger.

This is the design mistake behind npm install scripts: code runs because a file was loaded, not because anyone asked for it. Package managers are walking that default back. vlt, for example, installs dependencies first and runs build scripts only after vlt build selects them. Socket flags the agent skill version of the pattern with its Pre-execution shell command alert.

Claude Code offers one switch for stage 3. Setting "disableSkillShellExecution": true replaces each ! command with [shell command execution disabled by policy], and it is most useful in managed settings, where users cannot override it. The setting does not apply to bundled or managed skills. It also leaves stage 4 alone: the instructions can still ask the model to run the same command, and the skill's allowed-tools grant still skips the prompt for that turn.

Are agent skills safe to install?

#

Agent skills are safe to install only after the review you would give a dependency with an install script. The 2026 record shows what happens without that review.

In the OpenClaw skill marketplace campaign, attackers turned the ClawHub registry into a malware delivery channel in early 2026. VirusTotal analyzed 3,016 OpenClaw skills and found hundreds with malicious characteristics. A single publisher account accounted for 314 skills, all identified as malicious.

The malicious skills carried almost no code. A skill named "Yahoo Finance" told Windows users to download a password-protected ZIP and run the executable inside, which antivirus engines flagged as a trojan. On macOS, it pointed to an obfuscated shell script that fetched a variant of Atomic Stealer, a macOS infostealer. The skill's own files passed file-based scanning. "The malware is the workflow," VirusTotal wrote.

The format's authors anticipated this. Anthropic's launch post warned that "malicious skills may introduce vulnerabilities in the environment where they're used or direct Claude to exfiltrate data."

Two properties make agent skills harder to vet than a malicious package on npm or PyPI.

The payload can be natural language. An instruction such as "run this setup command first" contains nothing a signature scanner recognizes, and it works on the agent and the human alike. Detection has to read instructions for intent, not only code for known patterns.

Behavior can change without the skill changing. A skill that fetches a script from a URL at runtime is clean at rest. The publisher can swap the remote content later, and every hash of the skill's files stays the same.

Skill directories are also becoming persistence targets. In a campaign targeting Alibaba developers, Socket found a remote access trojan that patched Python scripts inside the .skills directories of several enterprise AI tools so they would run its payload. The Mini Shai-Hulud credential stealer persisted through SessionStart hooks in .claude/settings.json. An agent's configuration directories hold code that runs every session, and malware authors have noticed.

How are agent skills installed and pinned?

#

Agent skills reach developers through four channels, which differ most in what they pin and what they check again. The differences decide whether a compromised upstream reaches your agent.

The skills CLI

#

Vercel's skills CLI (npx skills add owner/repo) fetches a Git repository and copies or symlinks the selected skills into each agent's directory. We read the source of version 1.7.4, published October 19, 2026. For a project install, it records an entry with this shape in skills-lock.json:

JSON
{
  "version": 1,
  "skills": {
    "release-notes": {
      "source": "example-org/skills",
      "sourceType": "github",
      "skillPath": "skills/release-notes/SKILL.md",
      "computedHash": "<sha256 over every file path and its contents>"
    }
  }
}

Three details matter. The entry has no ref unless you named one, so the source means whatever the default branch holds. The computedHash is recorded, but the restore command, skills experimental_install, fetches from the source again and never compares against it. Global installs with -g write no project lockfile at all. You can confirm the hash is only compared to detect upstream updates:

Bash
npm pack skills@1.7.4 && tar xzf skills-1.7.4.tgz
grep -n "=== skill.entry.computedHash" package/dist/cli.mjs
# 8117:  if (!relocated && latestHash && latestHash === skill.entry.computedHash) {

A lockfile that records a hash nobody verifies documents what you installed. It does not enforce it.

The CLI also prints third-party audit results, including Socket's, before its confirmation prompt. That table is advisory. The -y flag skips the prompt, and the lookup gives up after 3 seconds. Setting DISABLE_TELEMETRY or DO_NOT_TRACK skips the audit lookup entirely, because the CLI gates it with the same check as telemetry.

Git references are not immutable either. In the trivy-action tag compromise, an attacker force-pushed 75 of 76 version tags to malicious commits, and every workflow that referenced a tag pulled the new code. A skill installed by branch or tag has the same exposure. Pin to a full commit SHA with a tree URL, such as npx skills add https://github.com/example-org/skills/tree/<commit-sha>/skills/release-notes.

pnpm

#

pnpm 12.11.0, released October 9, 2026, links skills that direct dependencies ship under skills/<name>/SKILL.md into agent directories such as .claude/skills. Nothing links until you run pnpm approve, which records the approval:

YAML
permissions:
  drizzle-kit:
    skills: true

This channel inherits the package manager's controls. The skill content is the installed package version, pinned in pnpm-lock.yaml with a registry integrity hash. New versions also wait out the 24-hour minimum release age that pnpm 11 made the default. pnpm links skills only from direct dependencies, so skills inside transitive dependencies never reach the agent.

The weakness is the unit of approval. pnpm's documentation says "an approval covers every skill of the package, in every later version." After a maintainer account takeover, the next release can ship new agent instructions with no new approval.

vlt

#

vlt 1.5.0 adds a vlt skills command that links skills from installed dependencies into .agents/skills and agent folders such as .claude/skills. The selection is saved as allow-skills in vlt.json. With no saved selection, vlt skills link defaults to :not(:malware), which uses Socket's high-confidence malware alerts to exclude skills from flagged packages. Stricter selectors can also exclude packages flagged for typosquatting or obfuscated code.

Skill marketplaces

#

ClawHub now scans every published skill with VirusTotal, a change OpenClaw announced on February 7, 2026, after the campaign. Skills flagged as malicious are blocked from download. OpenClaw's own announcement called the scanning "not a silver bullet."

Which channel to use

#
ChannelWhat is pinnedChecked on reinstallGate before the agent sees itWhen upstream changes
skills CLI 1.7.4The Git ref you passed, or the default branchNothing. The recorded hash is not compared.Advisory audit table, and a prompt that -y skipsskills update installs the new content
pnpm 12.11+Package version in pnpm-lock.yamlRegistry integrity hashpnpm approve, once per packageNew skill content links automatically
vlt 1.5+Package version in the lockfileLockfile, as for any dependencySaved allow-skills selection, Socket malware filter by defaultLinks follow the installed version
Vendored into your repoThe committed filesGit historyCode reviewEvery change is a diff someone reviews

Our verdict: vendor the skills your team depends on into the repository, so every byte goes through code review. For skills that ship inside a library, install through a package manager and treat the approval as a dependency review. Use the skills CLI only with commit-SHA tree URLs, and never with -y in automation.

Scanning agent skills

#

Socket scans agent skills as multi-language packages, because that is what they are. One SKILL.md can drive Python, JavaScript, and shell in a single workflow. Socket's scanner analyzes every file a skill references with the engine for that ecosystem, and uses an LLM to read the natural-language instructions for intent. Socket runs this analysis on skills installed through Vercel's skills CLI and rescans them as they change. In a February 2026 test against 382 known malicious skills and 355 popular benign ones, the scanner reached 94.5% precision and 98.7% recall, according to Socket's skills.sh announcement.

As of October 2026, Socket's alert catalog lists 13 AI skill alerts:

SeverityAlerts
HighPre-execution shell command, Command injection, Data exfiltration, Hardcoded secrets, Prompt injection, Tool chaining attack
MediumCode obfuscation, Resource abuse, Supply chain risk, Tool abuse, Transitive trust abuse
LowAutonomy abuse, Discovery abuse

Transitive trust abuse flags a skill that loads or invokes other external skills. It is the skill version of a dependency tree: you vetted one skill and inherited its choices about others.

Scanning has limits we should state plainly. A scan sees the skill as published. It cannot see what a URL in the skill will serve tomorrow, and an instruction that tells a human to paste a command is only as detectable as its wording. A scan result is a statement about one version of a skill, which is why rescanning on change matters as much as the first scan.

Controls for agent skills and what each misses

#
ControlWhat it stopsWhat it misses
disableSkillShellExecution in managed settings! commands at activation, for user, project, plugin, and additional-directory skillsBundled and managed skills, and anything the model runs at execution
disable-model-invocation: true in a skill's frontmatterThe model triggering the skill from its descriptionExplicit invocation still renders the skill. The flag only protects skills you control.
Reviewing allowed-tools and adding deny rulesSilent pre-approval. Deny and ask rules override allowed-tools.Commands that match an allow rule you already have
Pinning to a commit SHA, or vendoringTag and branch rewrites upstreamRemote content the skill fetches at runtime
Scanning before installKnown malicious patterns in files and instructionsSkills that are clean at rest and fetch the payload later
Sandboxing the agentDamage outside the sandboxEverything the sandbox can reach: the repository, mounted credentials, allowed network

Every installer shares one gap: none treats changed skill content as a new trust decision. pnpm approves a package for all later versions. The skills CLI records a content hash and never enforces it. Marketplace scanning judges each version as it was published. We think skill installers should pin to a content hash, verify it on every restore, and require fresh approval whenever SKILL.md or a bundled script changes, the same treatment a new install script in a dependency deserves. Until they do, the safest agent skill is one whose every line went through code review in your own repository.