How to Create Agent Skills: CLI API Tester Example
Skills let you package domain expertise into reusable instruction packs that agents can auto-load. Here's how to build one from scratch, using a CLI API Tester skill as a real example.
Tob
Backend Developer
TL;DR: Agent skills are instruction packs that auto-load when relevant tasks appear. They're just markdown files with frontmatter. This guide walks through building a real CLI API Tester skill from scratch — covering skill anatomy, the auto-loading mechanism, tool permissions, and patterns for making skills that agents actually use correctly.
---
What Are Agent Skills?
Agent skills are specialized instruction packs that AI agents can load based on context. When you ask an agent to "test my API endpoints," a matching skill auto-loads and provides structured guidance tailored to that task.
The skill system has three components:
- SKILL.md — the entry point, a markdown file with YAML frontmatter
- Supporting files (optional) — scripts, data files, templates
- Auto-loading logic — the agent matches task descriptions to skill triggers
Skills live in .agents/skills/ in your workspace. Each skill is a directory with a SKILL.md file:
.agents/skills/
├── technical-writing/
│ └── SKILL.md
├── cli-api-tester/ # our example
│ ├── SKILL.md
│ └── scripts/
│ └── test-runner.py---
Skill Anatomy: SKILL.md Structure
Every skill has YAML frontmatter and a markdown body:
---
name: skill-identifier # unique name
description: "When to use this skill..." # triggers auto-loading
allowed-tools: Read Write Edit Glob Grep # tool permissions
metadata:
author: your-name
version: "1.0.0"
tags: api, testing, cli
---
# Skill Title
Content...Frontmatter Fields
| Field | Required | Purpose |
|---|---|---|
name | Yes | Unique identifier for the skill |
description | Yes | Trigger phrases — what tasks should load this skill |
allowed-tools | No | Which tools the agent can use (defaults to all) |
metadata | No | Author, version, tags, supported platforms |
The description is the most important field — it's how the agent decides when to load the skill. Make it specific and include common trigger phrases.
---
Building the CLI API Tester Skill
Let's build a skill that helps agents test REST APIs via CLI. This is a realistic use case: agents can run curl commands, validate responses, and generate test reports.
Step 1: Create the Skill Directory
.agents/skills/cli-api-tester/
├── SKILL.md
└── scripts/
└── test-runner.pyStep 2: Write SKILL.md
---
name: cli-api-tester
description: "Use this skill when testing APIs, running curl commands, validating HTTP responses, or checking REST endpoints. Triggers on: 'test my API', 'check the endpoint', 'validate the response', 'run API tests', 'curl the server', 'verify HTTP status codes'."
allowed-tools: Read Write Edit Glob Grep Bash
metadata:
author: your-name
version: "1.0.0"
tags: api, testing, curl, http, rest, cli
platforms: Claude, ChatGPT, Gemini
---
# CLI API Tester Skill
A skill for testing REST APIs via command-line tools like curl and httpx. Use this when you need to validate API endpoints, check response bodies, verify status codes, or generate automated test reports.
## Core Testing Workflow
### 1. Discover Endpoints
First, understand what you're testing:
curl -s http://localhost:3000/api/routes | jq .
### 2. Test Individual Endpoints
curl -s -w "\nHTTP_CODE:%{http_code}\n" http://localhost:3000/api/users
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/profile
curl -s -X POST http://localhost:3000/api/users \ -H "Content-Type: application/json" \ -d '{"name":"Test User","email":"test@example.com"}'
curl -s -X PUT http://localhost:3000/api/users/123 \ -H "Content-Type: application/json" \ -d '{"name":"Updated Name"}'
curl -s -X DELETE http://localhost:3000/api/users/123
### 3. Validate Responses
Always validate the response structure and data types:
response=$(curl -s -w "%{http_code}" -o response.json http://localhost:3000/api/users) if [ "$response" = "200" ]; then echo "PASS: Status code 200" else echo "FAIL: Expected 200, got $response" fi
jq -e '.users[] | has("id") and has("name")' response.json > /dev/null && echo "PASS: Valid structure"
jq -e '.users[] | select(.email == "admin@example.com")' response.json > /dev/null && echo "PASS: Found admin user"
### 4. Test Error Cases
curl -s -w "%{http_code}" -o /dev/null -X POST http://localhost:3000/api/users \ -H "Content-Type: application/json" \ -d '{"email":"invalid-email"}'
curl -s -w "%{http_code}" -o /dev/null http://localhost:3000/api/profile
curl -s -w "%{http_code}" -o /dev/null http://localhost:3000/api/users/999999
### 5. Performance Testing
curl -s -w "Time: %{time_total}s\n" http://localhost:3000/api/users
ab -n 100 -c 10 http://localhost:3000/api/users/
for i in {1..20}; do curl -s -w "%{time_total}\n" -o /dev/null http://localhost:3000/api/users & done wait
## Response Validation Patterns
### JSON Response Validation
jq -e '.data[] | has("id") and has("name") and has("email")' response.json
jq -e '.data[] | (.id | type == "number") and (.name | type == "string")' response.json
jq -e '.data[] | .status == "active" or .status == "inactive"' response.json
jq -e '.data[] | .age >= 18 and .age <= 120' response.json
### Header Validation
content_type=$(curl -sI http://localhost:3000/api/users | grep -i content-type) echo "$content_type" | grep -q "application/json" && echo "PASS" || echo "FAIL"
curl -sI -X OPTIONS http://localhost:3000/api/users \ -H "Origin: http://localhost:3001" | grep -i "access-control"
### Authentication Flow Testing
curl -s -X POST http://localhost:3000/api/auth/register \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","password":"Test123!","name":"Test User"}' > register.json
curl -s -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","password":"Test123!"}' > login.json
TOKEN=$(jq -r '.token' login.json)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/profile
curl -s -w "%{http_code}" -o /dev/null -H "Authorization: Bearer invalid-token" \ http://localhost:3000/api/profile
## Test Report Template
Generate structured test reports:
#!/bin/bash
REPORT="api-test-report.md" echo "# API Test Report" > "$REPORT" echo "Generated: $(date)" >> "$REPORT" echo "" >> "$REPORT"
test_endpoint() { local name=$1 local url=$2 local expected=$3
response=$(curl -s -w "%{http_code}" -o /tmp/response.json "$url")
if [ "$response" = "$expected" ]; then echo "✅ $name: PASS (got $response)" | tee -a "$REPORT" else echo "❌ $name: FAIL (expected $expected, got $response)" | tee -a "$REPORT" fi }
test_endpoint "GET Users" "http://localhost:3000/api/users" "200" test_endpoint "GET User 1" "http://localhost:3000/api/users/1" "200" test_endpoint "GET Nonexistent" "http://localhost:3000/api/users/999999" "404"
echo "" >> "$REPORT" echo "## Response Samples" >> "$REPORT" echo '``json' >> "$REPORT" cat /tmp/response.json >> "$REPORT" echo '``' >> "$REPORT"
## Common Issues and Debugging
| Issue | Diagnosis | Resolution |
|-------|-----------|------------|
| Connection refused | Server not running | Start server: `npm run dev` |
| Timeout | Server overloaded or slow | Check server logs, increase timeout: `-m 30` |
| 502 Bad Gateway | Upstream server error | Check reverse proxy and upstream services |
| CORS errors | Missing CORS headers | Add `Access-Control-Allow-Origin` header |
| SSL errors | Self-signed certs | Use `-k` flag: `curl -k https://localhost` |
## When to Use This Skill
Use this skill when:
- Testing new API endpoints during development
- Validating API behavior after code changes
- Checking third-party API integrations
- Generating test reports for PRs
- Debugging API failures in CI/CD
Skip this skill when:
- The task is to write API code (use a coding skill instead)
- You need to set up the API server (not testing it)
- The task is purely about API design or documentation