This guide walks through creating your first clictl tool spec from scratch. By the end, you will have a working tool that you and your AI agent can run.
A tool spec is a YAML file that describes how to interact with an API, website, CLI tool, MCP server, or skill. clictl reads the spec and gives your agent (Claude Code, Cursor, etc.) the ability to call it.
Think of it as a contract: you describe the endpoints, parameters, and auth, and clictl handles the rest.
curl -fsSL https://download.clictl.dev/install.sh | bashVerify it works:
clictl version
clictl search weatherLet's create a spec for the Open-Meteo weather API (no auth required).
mkdir my-tools && cd my-toolsCreate weather.yaml:
name: weather
version: "1.0"
description: Current weather and forecasts from Open-Meteo
category: weather
tags: [weather, forecast, temperature]
actions:
- name: current
description: Get current weather for a location
url: https://api.open-meteo.com/v1/forecast
params:
- name: latitude
type: string
required: true
description: Latitude of the location
example: "52.52"
in: query
- name: longitude
type: string
required: true
description: Longitude of the location
example: "13.41"
in: query
- name: current_weather
type: string
default: "true"
in: query
transform:
- type: json
extract: "$.current_weather"clictl test weather.yamlThis validates the spec and runs each action against the live API.
clictl run weather.yaml current --latitude 52.52 --longitude 13.41You should see current weather data for Berlin.
clictl install weather.yamlThis creates a SKILL.md file that your agent reads, teaching it how to use the weather tool.
Every spec has these sections:
name: my-tool # Kebab-case identifier
version: "1.0" # Semver version
description: What it does # One-line description
category: developer # Category for browsing
tags: [api, utility] # Searchable tagsActions are the operations your tool supports. Each action is an API call:
actions:
- name: get-user # Kebab-case action name
description: Get a user # What this action does
method: GET # HTTP method (default: GET)
url: https://api.example.com # Base URL
path: /users/{username} # Path with parameter placeholders
params: # Input parameters
- name: username
required: true
in: path # Where the param goes: path, query, header, body
output: json # Response format: json, text, html, markdown
transform: # Optional: shape the response
- type: json
select: [id, name, email]If the API requires auth, add an auth block:
auth:
env: GITHUB_TOKEN # Vault key name
header: Authorization # HTTP header to set
value: "Bearer ${GITHUB_TOKEN}" # Header value with variableStore the key in the vault:
clictl vault set GITHUB_TOKEN ghp_your_token_hereThe token is encrypted at rest and injected at request time. It never appears in the spec or in your shell history.
Each parameter describes an input:
params:
- name: query # Parameter name
type: string # Type: string, int, bool, float
required: true # Is it required?
description: Search # What it does
default: "" # Default value
example: "react" # Example for documentation
in: query # Where it goes: query, path, header, body
values: [a, b, c] # Allowed values (enum)Transforms shape API responses before your agent sees them:
transform:
- type: json
extract: "$.data" # JSONPath extraction
select: [id, name, status] # Keep only these fields
- type: truncate
max_items: 20 # Limit array lengthCommon transforms:
| Type | Purpose | Example |
|---|---|---|
json |
Extract/select fields | extract: "$.results" |
truncate |
Limit items | max_items: 10 |
html_to_markdown |
Convert HTML | For website scraping |
template |
Format output | Go template syntax |
sort |
Sort results | field: "name" |
filter |
Filter results | jq-style filtering |
redact |
Remove fields | Hide sensitive data |
Validate responses before processing:
assert:
- type: status
values: [200, 201]auth:
env: MY_API_KEY
header: Authorization
value: "Bearer ${MY_API_KEY}"auth:
env: API_KEY
param: api_key
value: "${API_KEY}"auth:
env: API_KEY, API_SECRET
header: Authorization
value: "Basic ${API_KEY}:${API_SECRET}"One spec can target multiple APIs:
actions:
- name: list-repos
url: https://api.github.com
path: /user/repos
auth:
env: GITHUB_TOKEN
header: Authorization
value: "Bearer ${GITHUB_TOKEN}"
- name: list-gists
url: https://api.github.com
path: /gists
# Inherits auth from above if you add a top-level auth blockWebsites use HTTP with output: html and a transform:
actions:
- name: front-page
url: https://news.ycombinator.com
output: html
transform:
- type: html_to_markdown
remove_images: trueFor real-time data streams:
server:
type: websocket
url: wss://stream.example.com/ws
actions:
- name: subscribe
message: '{"channel": "${topic}"}'
wait: 5s
collect: 3
params:
- name: topic
required: trueWrap existing CLI tools:
server:
type: command
shell: bash
requires:
- name: docker
check: docker --version
actions:
- name: ps
description: List running containers
run: docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"Skills are prompt-based tools with source files:
source:
repo: your-org/skills
path: skills/my-skill/
ref: main
files:
- sha256: abc123...
path: SKILL.md
requires_system:
- name: python3
check: python3 --versionSee Skill Packs for the full guide.
- Create your spec in
toolbox/{letter}/{name}/{name}.yaml - Submit a PR to github.com/clictl/toolbox
clictl login
clictl publish my-tool.yamlOr use the web editor at clictl.dev (Settings > Published Tools > Create Tool).
- Spec Format Reference - full field reference
- Spec 1.0 Reference - complete schema
- Transforms Guide - all transform types
- CLI Reference - all commands