Claude Skills Integration Guide
What is a Claude Skill?
An on-demand capability folder with three loading levels — metadata, body, bundled resources.
Level 1: name+description (~100 tokens) live in the system prompt. Level 2: the SKILL.md body is read via bash when the description matches. Level 3: reference files are read only when linked, and scripts are executed (their source never enters context — only stdout costs tokens). The same folder works in Claude Code (.claude/skills/), via the API (/v1/skills + container), and on claude.ai. For codeAmani, skills encode fiddly house conventions (M-Pesa phone normalisation, KES formatting, callback idempotency) once and reuse them across every project.
Six things you reach for first
Author one SKILL.md, tune the description, let progressive disclosure keep context cheap.
██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗ ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝
██║ ██║ ███████║██║ ██║██║ ██║█████╗ ███████╗█████╔╝ ██║██║ ██║ ███████╗
██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ╚════██║██╔═██╗ ██║██║ ██║ ╚════██║
╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗ ███████║██║ ██╗██║███████╗███████╗███████║
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝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, …), $ARGUMENTS, dynamic context |
| Use Skills with the API | https://platform.claude.com/docs/en/build-with-claude/skills-guide | The /v1/skills endpoints, container + code-execution wiring, beta headers |
No package to install. Agent Skills are a file format, not a library — there is nothing to
npm installto author one. Claude Code, claude.ai, and the Claude API each readSKILL.mdnatively. (If you want to load skills from your own agent runtime, that is the Claude Agent SDK — a separate concern covered in the ai-agents guide.)
Frontmatter fields
Every SKILL.md opens with YAML between --- markers. Two fields drive the universal format; Claude Code adds several optional ones.
| Field | Where | Notes |
|---|---|---|
name | universal | Lowercase letters, numbers, hyphens only. Max 64 chars. Cannot contain anthropic or claude (reserved). In Claude Code the directory name is the command, so name is just the display label. |
description | universal | Non-empty, max 1024 chars, third person. What it does + when to use it. This is the trigger. |
allowed-tools | Claude Code | Tools pre-approved (no per-use prompt) while the skill is active, e.g. Bash(git add *) Bash(git commit *). Does not restrict the pool — it grants. |
disable-model-invocation | Claude Code | true = only the human can run it via /name (use for side-effecting workflows like deploy/commit). |
user-invocable | Claude Code | false = only Claude can load it (background knowledge, not a user command). |
context: fork / agent | Claude Code | Run the skill in an isolated subagent with the chosen agent type. |
The reserved-word rule matters here: a skill about Claude Skills themselves cannot be named claude-skills in the universal format. 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 every time, while leaving every other tool governed by your normal permission settings. Grant narrowly — Bash(python3 *), not bare Bash. For a side-effecting skill (deploy, send money) add disable-model-invocation: true so only a human can fire it.
(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; needs workspace-trust accept for allowed-tools |
| Claude Code (plugin) | <plugin>/skills/<name>/SKILL.md | wherever the plugin is enabled; namespaced plugin:name |
| Claude API | upload via the /v1/skills endpoints, then reference its skill_id in the container param alongside the code-execution tool | workspace-wide |
| claude.ai | upload a .zip under Settings → Features | 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. 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.
How progressive disclosure loads a skill
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.
How Claude decides to load a skill
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.
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/claude. -
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.