Claude Skills Integration Guide
Technology: claude-skills · Category: ai · Last reviewed: 2026-08-23
Source: https://tech-stack.codeamanilabs.org/guide/claude-skills
Insight:
An Agent Skill is a
SKILL.mdfolder Claude loads on demand — a tiny always-ondescriptionadvertises it (~100 tokens) and the full body plus bundled scripts load only when a request matches. It is a file format, not a library (nothing tonpm install): the same folder runs in Claude Code, the Agent SDK, the Claude API (/v1/skills, now GA — no beta header), and claude.ai, and Claude Code now follows the open Agent Skills standard (agentskills.io), with custom/commandsfolded into skills. For codeAmani, skills bank fiddly house conventions (M-Pesa phone normalisation, integer KES, callback idempotency) once and reuse them everywhere — the trade-off is that a skill runs code with your permissions, so audit every third-party one.
██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗ ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝
██║ ██║ ███████║██║ ██║██║ ██║█████╗ ███████╗█████╔╝ ██║██║ ██║ ███████╗
██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ╚════██║██╔═██╗ ██║██║ ██║ ╚════██║
╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗ ███████║██║ ██╗██║███████╗███████╗███████║
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝
Claude Skills Integration Guide
Focus: how to author an Agent Skill — a
SKILL.mdfolder Claude loads on demand. The whole system is one idea: a tiny always-loadeddescriptionadvertises the skill, and the full instructions (plus any scripts and reference files) load only when a request matches. This is the Agent Skills format, distinct from building an agent loop — here you are packaging reusable expertise, not wiring tools into a runtime.
Overview
A Skill is a folder containing a SKILL.md file: YAML frontmatter (name + description) followed by markdown instructions. Optionally it bundles extra markdown reference files and executable scripts. Claude discovers skills automatically and pulls each one into context only when relevant — so you can install dozens of skills for roughly 100 tokens each until one actually fires.
The mechanism that makes this cheap is progressive disclosure, three levels of loading:
- Metadata (always loaded) — the
nameanddescriptionfrom every skill's frontmatter sit in the system prompt. This is all Claude knows by default: that the skill exists and when to use it. - Instructions (loaded when triggered) — when a request matches a skill's
description, Claude reads theSKILL.mdbody off the filesystem (via bash). Only now do the procedures enter context. Keep this under ~500 lines. - Resources (loaded as needed) — bundled reference files (
REFERENCE.md,EXAMPLES.md) are read only when the body points to them; bundled scripts are executed, never read into context (only their output costs tokens). There is no practical limit on bundled content because it costs zero until accessed.
The single highest-leverage thing you write is the description. Claude uses it to choose among potentially 100+ skills, so it must say both what the skill does and when to reach for it — phrased in the third person with the trigger words a user would actually type.
Official Documentation
| Source | URL | What it covers |
|---|---|---|
| Agent Skills overview | https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview | What a Skill is, frontmatter fields, the 3 progressive-disclosure levels, where Skills run |
| Authoring best practices | https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices | Writing descriptions, degrees of freedom, bundling scripts, anti-patterns, the authoring checklist |
| Skills in Claude Code | https://code.claude.com/docs/en/skills | .claude/skills/ discovery, full frontmatter reference (allowed-tools, disable-model-invocation, model, paths, …), $ARGUMENTS, dynamic context |
| Skills in the Agent SDK | https://code.claude.com/docs/en/agent-sdk/skills | The skills option ("all" / list / []), settingSources loading, dispatch by /name, confirming load via the init message |
| Use Skills with the API | https://platform.claude.com/docs/en/build-with-claude/skills-guide | The /v1/skills endpoints, the container.skills + code-execution wiring (GA — no beta header) |
| Agent Skills standard | https://agentskills.io | The open cross-tool spec Claude Code now follows; the six portable frontmatter fields |
No package to install. Agent Skills are a file format, not a library — there is nothing to
npm installto author one. Claude Code, the Claude Agent SDK, claude.ai, and the Claude API each readSKILL.mdnatively, and the format is now an open cross-tool standard (agentskills.io). In Claude Code, custom/commandshave merged into skills: a legacy.claude/commands/deploy.mdand a.claude/skills/deploy/SKILL.mdboth create/deployand behave the same way (skills just add a folder for supporting files, richer frontmatter, and automatic model invocation). Loading skills from your own agent runtime is the Claude Agent SDK — see How the Agent SDK loads skills below and the ai-agents guide.
Frontmatter fields
Every SKILL.md opens with YAML between --- markers. Only description is strictly needed for Claude to know when to use a skill; every other field is optional. Six fields are part of the portable Agent Skills standard and work everywhere; the rest are Claude Code extensions.
| Field | Where | Notes |
|---|---|---|
name |
portable | Lowercase letters, numbers, hyphens only. Max 64 chars. Cannot contain anthropic, claude, or XML tags (reserved). In Claude Code name is optional — it defaults to the directory name and only sets the display label; the slash command always comes from the directory. On claude.ai / the API it identifies the skill. |
description |
portable | Third person, what it does + when to use it — this is the trigger. Max 1024 chars in the portable spec; Claude Code truncates the combined description + when_to_use at 1,536 chars in the skill listing, so put the key use case first. |
allowed-tools |
portable | Tools pre-approved (no per-use prompt) for the turn that invokes the skill — the grant clears on your next message. Space- or comma-separated, or a YAML list, e.g. Bash(git add *) Bash(git commit *). Grants, never restricts the pool. |
license / metadata / compatibility |
portable | Spec metadata: SPDX license; a free-form metadata map for your own tooling; a compatibility string (≤500 chars). Claude Code accepts but doesn't act on them. |
disable-model-invocation |
Claude Code | true = only the human can run it via /name, and its description leaves Claude's context (use for side-effecting workflows like deploy/commit). |
user-invocable |
Claude Code | false = only Claude can load it (background knowledge, hidden from the / menu). |
context: fork + agent |
Claude Code | context: fork runs the skill in an isolated subagent (background by default; set background: false to block the turn). The separate agent: field picks the subagent type (Explore, Plan, general-purpose, or a custom one). |
disallowed-tools |
Claude Code | Tools removed from the pool while the skill is active (e.g. block AskUserQuestion in a background loop). Clears on your next message. |
model / effort |
Claude Code | Override the model / effort level for the turn the skill is active (inherit keeps the current model). |
paths |
Claude Code | Glob patterns that gate auto-activation — Claude loads the skill only when working on matching files. |
arguments / argument-hint |
Claude Code | Declare named positional args for $name substitution and an autocomplete hint. $ARGUMENTS, $0, $1 work without declaration. |
hooks / shell |
Claude Code | Register hooks for the session when the skill fires; shell: powershell runs !`command` injection via PowerShell instead of bash. |
Portable six. Outside Claude Code — claude.ai uploads, the Skills API, and
package_skill.pyfrom anthropics/skills — onlyname,description,license,compatibility,metadata, andallowed-toolsare accepted; any Claude Code-only field makes packaging/upload hard-fail with an "unexpected key" error. Keep a skill you intend to publish to those surfaces on the portable six.
The reserved-word rule matters here: a skill about Claude Skills themselves cannot be named claude-skills. Name it after the activity instead — see the worked example below.
Build your first skill — a worked example
We'll build a real codeAmani-relevant skill: normalising Kenyan phone numbers to the 2547XXXXXXXX format Daraja requires. This is a perfect skill candidate — it's a fiddly, deterministic rule you'd otherwise paste into chat every time, and it has a clear "when to use" trigger.
(a) Directory layout
A skill is a directory; SKILL.md is the entrypoint. We bundle one helper script and one reference file to demonstrate progressive disclosure:
.claude/skills/normalising-mpesa-phones/
├── SKILL.md # always-discoverable metadata + concise instructions
├── REFERENCE.md # the full edge-case table (loaded only when needed)
└── scripts/
└── normalise.py # deterministic normaliser (executed, never read)
Use gerund-form names (
normalising-mpesa-phones), forward slashes always, and descriptive filenames — neverdoc2.md.
(b) SKILL.md frontmatter — the description is the trigger
The description is the only thing loaded until the skill fires, so phrase it with when-to-use cues (the words a teammate would actually say):
---
name: normalising-mpesa-phones
description: >-
Normalises Kenyan phone numbers to the 2547XXXXXXXX / 2541XXXXXXXX format the
Daraja (M-Pesa) API requires. Use when formatting a phone number for an STK Push,
a B2C payout, an SMS, or whenever a number arrives as 07.., +2547.., or 2547...
allowed-tools: Bash(python3 *)
---
Compare a bad description — description: Helps with phone numbers — which gives Claude no trigger to match on and no idea what "help" means. Be specific; include key terms (Daraja, STK Push, 07.., +254).
(c) The body — concise instructions
Assume Claude is already smart. State the rule and the canonical path; don't explain what a phone number is:
# Normalising M-Pesa phone numbers
Daraja rejects anything that is not `254` followed by 9 digits (e.g. `254712345678`).
Normalise every number to that shape **before** calling any Daraja endpoint.
## Rules
- Strip spaces, hyphens, and a leading `+`.
- `07XXXXXXXX` or `01XXXXXXXX` → replace the leading `0` with `254`.
- `7XXXXXXXX` / `1XXXXXXXX` (9 digits, no prefix) → prepend `254`.
- Already `2547…` / `2541…` (12 digits) → leave as-is.
- Anything else → reject; do not guess.
## Use the bundled script (preferred — deterministic)
Run it rather than reimplementing the rule:
python3 ${CLAUDE_SKILL_DIR}/scripts/normalise.py "0712 345 678"
# → 254712345678
For the full edge-case table (Safaricom vs Airtel prefixes, invalid lengths),
see [REFERENCE.md](REFERENCE.md).
Note the two progressive-disclosure links: REFERENCE.md is read only if Claude needs the edge cases, and normalise.py is executed (its source never enters context). Keep references one level deep — link every supporting file directly from SKILL.md, never a chain of a.md → b.md → c.md.
(d) The bundled script — deterministic, self-contained
A pre-made script is more reliable than asking Claude to regenerate the regex each time, and it costs zero context until run:
#!/usr/bin/env python3
"""Normalise a Kenyan phone number to Daraja's 2547XXXXXXXX format."""
import re, sys
def normalise(raw: str) -> str:
s = re.sub(r"[\s\-]", "", raw).lstrip("+")
if re.fullmatch(r"0[17]\d{8}", s): # 07.. / 01..
return "254" + s[1:]
if re.fullmatch(r"[17]\d{8}", s): # bare 9-digit
return "254" + s
if re.fullmatch(r"254[17]\d{8}", s): # already canonical
return s
raise ValueError(f"Not a valid Kenyan mobile number: {raw!r}")
if __name__ == "__main__":
print(normalise(sys.argv[1]))
(e) allowed-tools — pre-approve just enough
The frontmatter line allowed-tools: Bash(python3 *) lets Claude run the helper without a permission prompt during the turn that fires the skill, while leaving every other tool governed by your normal permission settings. The grant is turn-scoped — it clears when you send your next message, then re-applies each time the skill is invoked again. Grant narrowly — Bash(python3 *), not bare Bash — and use disallowed-tools to remove a tool from the pool for a locked-down skill. For a side-effecting skill (deploy, send money) add disable-model-invocation: true so only a human can fire it. (In an Agent SDK session this frontmatter field is ignored for project/personal skills — pre-approve via the SDK's allowedTools option instead.)
(f) Where it lives and how it's discovered
The same SKILL.md works across every surface; only the install path changes:
| Surface | How to install | Sharing scope |
|---|---|---|
| Claude Code (personal) | ~/.claude/skills/<name>/SKILL.md |
all your projects |
| Claude Code (project) | .claude/skills/<name>/SKILL.md (commit it) |
this repo (loads from cwd + every parent to the repo root) |
| Claude Code (enterprise) | .claude/skills/<name>/ in the managed-settings directory |
every user in the org; overrides personal and project |
| Claude Code (plugin) | <plugin>/skills/<name>/SKILL.md |
wherever the plugin is enabled; namespaced plugin:name |
| Claude Agent SDK | the same filesystem folders, gated by settingSources + the skills option |
whatever the SDK session's setting sources load |
| Claude API | upload via the /v1/skills endpoints, then reference the skill in container.skills (type/skill_id/version) alongside the code-execution tool |
workspace-wide |
| claude.ai | upload a .zip under Settings → Features (code execution must be enabled) |
per-user only |
In Claude Code the directory name becomes the slash command (/normalising-mpesa-phones) and the project skill is picked up automatically from .claude/skills/ in the cwd and every parent up to the repo root. A same-named skill at a higher level wins (enterprise > personal > project) and also overrides a bundled skill of that name. Note that a project skill's allowed-tools grant is not gated by workspace trust — Claude Code applies it even in an untrusted -p run — so review the allowed-tools of any skill checked into a repo before running Claude Code there. (Adding a .claude-plugin/plugin.json to a skill folder does require accepting the workspace-trust dialog first.) Custom Skills do not sync across surfaces — a skill uploaded to the API is not on claude.ai, and Claude Code skills are filesystem-only; you can optionally pull skills you enabled on claude.ai into ~/.claude/skills/synced/ with CLAUDE_CODE_SYNC_SKILLS=1.
How progressive disclosure loads a skill
flowchart TD
M["Level 1 · Metadata<br/>name + description<br/>(always in system prompt, ~100 tok)"] --> B["Level 2 · SKILL.md body<br/>read via bash when triggered<br/>(under ~5k tok)"]
B --> R["Level 3 · REFERENCE.md / EXAMPLES.md<br/>read only if the body points to them"]
B --> S["Level 3 · scripts/normalise.py<br/>EXECUTED via bash · source never loaded<br/>(only stdout costs tokens)"]
The cost ladder is the whole point: thousands of words of edge-case docs and a dozen scripts sit on disk at zero token cost until the one file a task needs is actually opened. One nuance to design around: once the body loads it stays in context for the rest of the session (Claude Code doesn't re-read the file each turn), so write it as standing guidance, keep it lean, and expect a large skill to be trimmed by auto-compaction. The allowed-tools grant is the exception — that resets every message.
How Claude decides to load a skill
flowchart TD
U["User request arrives"] --> C{"Does any skill's<br/>description match?"}
C -->|"no"| N["Answer normally · no skill loaded"]
C -->|"yes"| I{"disable-model-invocation?"}
I -->|"true"| H["Only a human /command can run it"]
I -->|"false / unset"| L["Read SKILL.md body into context"]
L --> D{"Body references<br/>a resource or script?"}
D -->|"reference file"| RF["bash read just that file"]
D -->|"script"| EX["bash execute · capture output only"]
D -->|"no"| W["Do the work"]
If a skill never triggers, the fix is almost always the description: add the keywords users actually say. If it triggers too eagerly, make the description more specific or set disable-model-invocation: true.
How the Agent SDK loads skills
The Claude Agent SDK reads the same filesystem skills as the CLI — there is no programmatic registration API for skills (unlike subagents, which you can define inline via the agents option). Two knobs govern them:
settingSources/setting_sourcesmust include'user'and/or'project'for skills to load at all. With defaultquery()options both are loaded, so~/.claude/skills/,<cwd>/.claude/skills/, and every parent.claude/skills/up to the repo root are discovered. If you setsettingSourcesexplicitly and omit those sources, no skills load — a common gotcha.skillsscopes which discovered skills Claude may auto-invoke:"all"(default when omitted) enables everything, a list of exact names allows only those, and[]disables auto-invocation. Setting it auto-adds theSkilltool toallowedTools; if you pass an explicittoolslist, include"Skill"yourself.
const options = {
cwd: process.cwd(), // .claude/skills/ here or in a parent
settingSources: ["user", "project"], // required — load skills from disk
skills: "all", // or ["formatting-kes", "normalising-mpesa-phones"]
allowedTools: ["Read", "Write", "Bash"],
};
Dispatch a skill directly by putting /<name> in the prompt string — this works even if the name is not in your skills allowlist. Confirm what loaded by reading the init system message: its skills array lists user-invocable skills, and slash_commands lists every dispatchable command (built-ins, bundled skills, your skills, .claude/commands/ files). One SDK-only caveat: for project/personal skills the allowed-tools frontmatter field is ignored — grant those tools through the SDK's allowedTools / allowed_tools option instead. In non-interactive (-p / SDK) runs a context: fork skill always blocks for its result rather than backgrounding.
Authoring checklist
-
descriptionis third-person and states what it does + when to use it, with real trigger keywords. -
nameis lowercase-hyphen, ≤64 chars, and avoids the reserved wordsanthropic/claudeand XML tags (or is omitted in Claude Code to inherit the directory name). -
SKILL.mdbody is under ~500 lines; long material is split into bundled files. - Supporting files are referenced one level deep from
SKILL.md. - Reference files >100 lines start with a table of contents.
- Scripts handle their own errors (don't punt back to Claude) and document any constants.
- File paths use forward slashes; no time-sensitive "before August 2025" text.
-
allowed-toolsgrants narrowly; side-effecting skills setdisable-model-invocation: true. - Tested with the models you'll run it on (Haiku needs more guidance than Opus).
codeAmani notes
- Secrets stay server-side. A skill bundles instructions and scripts, never credentials. The phone-normaliser script takes a number, not a key. If a skill must call Daraja or Supabase, it reads
DARAJA_CONSUMER_SECRET/SUPABASE_SERVICE_ROLE_KEYfrom the environment at runtime — theSKILL.mdand its scripts go in git, so they must contain zero secrets. Audit any third-party skill before trusting it; a malicious one can run code with your permissions. - AI routing stays Claude-primary. Skills are an Anthropic-native capability — there's no provider choice to make. They make Claude a specialist for a repeated task, complementing (not replacing) the house routing: Claude for reasoning/coding, OpenAI for structured output, HuggingFace for open models.
- High-leverage codeAmani skills to build first: the
normalising-mpesa-phonesskill above; aformatting-kesskill (integer KES,Ksh 1,234display, no decimals to Daraja); averifying-mpesa-callbacksskill that encodes the idempotency-on-CheckoutRequestID+ reconciliation rule (see daraja-api and webhooks); and a Swahili-tone copy skill for support replies. Each is a fiddly house convention you currently re-explain — exactly what a skill is for. - Ship them as a project skill. Commit
.claude/skills/<name>/to the repo so every teammate (and every agent in the repo) inherits the convention. For org-wide reuse, package them into a Claude Code plugin'sskills/directory — the same pattern this very tech-stack repo uses to publish its guides.
Official docs:
- https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview
- https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices
- https://code.claude.com/docs/en/skills
- https://code.claude.com/docs/en/agent-sdk/skills
- https://platform.claude.com/docs/en/build-with-claude/skills-guide
- https://agentskills.io