Generate Typer CLI commands from OpenAPI 3.0/3.1 specs
Project description
specli
The CLI generator for the age of LLMs.
Point specli at any OpenAPI 3.x spec and get a production-ready CLI with auth, retries, rich output, and tab completion. Then enrich it from your source code, export strings for LLM rewriting, and generate Claude Code skills -- all in one pipeline.
specli init --spec examples/uspto-openapi.json
specli api root list # list available datasets
specli api fields list oa_citations v1 # searchable fields
Why specli?
Traditional API clients are either hand-coded or generated as lifeless SDK stubs. specli takes a different approach: it reads your OpenAPI spec, understands your source code, and produces a CLI that feels hand-crafted -- with help text pulled from your docstrings, descriptions polished by LLMs, and skill files that teach AI assistants how to use your API.
For API authors: Ship a CLI alongside your API with zero maintenance. Source enrichment means your Python docstrings become CLI help text automatically.
For API consumers: Turn any OpenAPI spec into a working CLI in seconds. No code generation, no boilerplate.
For AI-assisted workflows: Generate Claude Code skills so AI assistants can discover and use your API with full context -- endpoints, parameters, auth setup, and examples.
Table of Contents
- Install
- Quick Start
- Building Standalone CLIs
- Global Flags
- Advanced Usage
- Plugins
- Source Code Enrichment
- String Export / Import
- Using Generated Skills
- Path Rules
- Configuration
- Development Setup
- License
Install
pip install specli
Quick Start
# 1. Initialize from any OpenAPI 3.x spec (URL, file, or stdin)
specli init --spec examples/uspto-openapi.json
# 2. Fix the base URL (the spec uses a server variable that needs resolving)
# Edit ~/.config/specli/profiles/uspto-data-set-api.json and change:
# "base_url": "{scheme}://developer.uspto.gov/ds-api"
# to:
# "base_url": "https://developer.uspto.gov/ds-api"
# 3. Explore the API
specli inspect paths
specli inspect info
# 4. Make API calls (the USPTO API is public -- no auth needed)
specli api root list # list available datasets
specli api fields list oa_citations v1 # searchable fields for a dataset
# 5. POST with a request body (form-encoded, auto-detected from spec)
specli api records create v1 oa_citations \
--body '{"criteria": "*:*", "start": 0, "rows": 5}'
# 6. JSON output for scripting
specli --json api records create v1 oa_citations \
--body '{"criteria": "Patent_PGPub:US*", "rows": 3}' | jq '.[] | keys'
Building Standalone CLIs
specli can compile your API into a standalone binary or a pip-installable package. The OpenAPI spec, profile config, and all dependencies are baked in -- the result is a single command that needs no setup.
Compile a binary (PyInstaller)
specli build compile -p uspto-data-set-api -n uspto --output ./dist
# Output: ./dist/uspto (about 19 MB)
./dist/uspto root list
./dist/uspto --help
Generate a pip-installable package
specli build generate -p uspto-data-set-api -n uspto
# Output: ./uspto/ (package directory)
pip install ./uspto
uspto root list
Both modes support the full enrichment pipeline. See Build Plugin for all flags.
Global Flags
These flags work with every specli command:
| Flag | Short | Description |
|---|---|---|
--version |
Show version and exit | |
--profile |
-p |
Profile name override |
--json |
Force JSON output format | |
--plain |
Force plain-text output | |
--no-color |
Disable color and Rich markup | |
--quiet |
-q |
Suppress non-essential output |
--verbose |
-v |
Enable debug output |
--dry-run |
-n |
Preview HTTP requests without sending |
--force |
-f |
Skip interactive confirmations |
--no-input |
Disable all interactive prompts | |
--output |
-o |
Redirect primary output to a file |
Advanced Usage
Dry-run mode
Preview the exact HTTP request without sending it:
specli -n api records create v1 oa_citations \
--body '{"criteria": "*:*", "rows": 1}'
# [dry-run] POST https://developer.uspto.gov/ds-api/oa_citations/v1/records
# Content-Type: application/x-www-form-urlencoded
# Body: criteria=*:*&rows=1
Load body from a file
echo '{"criteria": "*:*", "rows": 10}' > query.json
specli api records create v1 oa_citations --body @query.json
Override the base URL
SPECLI_BASE_URL=https://developer.uspto.gov/ds-api specli api root list
Build with full enrichment pipeline
specli build compile \
-p uspto-data-set-api \
-n uspto \
--source ./src \
--import-strings ./strings.json \
--generate-skill ./skill \
--cli-version 2.1.0
Enrichment-only (no build)
Export strings and generate skills without compiling a binary:
specli build compile \
-p uspto-data-set-api -n uspto \
--export-strings ./strings.json \
--generate-skill ./skill \
--no-build
Plugins
specli ships with four plugin groups: auth, build, skill, and completion.
Auth Plugins
Authentication is configured per-profile in the auth section. specli includes 10 auth plugins covering every common pattern.
All credential sources use the format <type>:<value>:
| Source | Example | Description |
|---|---|---|
env:VAR |
env:MY_API_KEY |
Read from environment variable |
file:/path |
file:~/.token |
Read first line of file |
prompt |
prompt |
Interactive hidden input |
store:PROFILE |
store:myapi |
Local credential store |
keyring:svc:acct |
keyring:myapp:token |
System keyring |
API Key (api_key)
Injects an API key via header, query parameter, or cookie.
{
"auth": {
"type": "api_key",
"header": "X-API-Key",
"location": "header",
"source": "env:MY_API_KEY"
}
}
| Field | Required | Default | Description |
|---|---|---|---|
header / param_name |
yes | Key name | |
location |
no | "header" |
"header", "query", or "cookie" |
source |
yes | Credential source | |
secret_source |
no | Second credential for dual-key auth | |
secret_header / secret_param_name |
no | X-API-Secret |
Name for the secret key |
HTTP Basic (basic)
Sends Authorization: Basic <base64> per RFC 7617.
{
"auth": {
"type": "basic",
"source": "env:BASIC_CREDS"
}
}
The source must resolve to "username:password".
| Field | Required | Description |
|---|---|---|
source |
yes | Credential source (must be user:pass format) |
Bearer Token (bearer)
Sends Authorization: Bearer <token>. Static injection, no refresh logic.
{
"auth": {
"type": "bearer",
"source": "env:MY_TOKEN"
}
}
| Field | Required | Description |
|---|---|---|
source |
yes | Credential source for the token |
OAuth2 Client Credentials (oauth2_client_credentials)
Non-interactive server-to-server auth. Exchanges client ID and secret for an access token. Tokens cached with auto-refresh.
{
"auth": {
"type": "oauth2_client_credentials",
"token_url": "https://provider.example.com/token",
"client_id_source": "env:CLIENT_ID",
"client_secret_source": "env:CLIENT_SECRET",
"scopes": ["read:api", "write:api"]
}
}
| Field | Required | Description |
|---|---|---|
token_url |
yes | Token endpoint URL |
client_id_source |
yes | Credential source for client ID |
client_secret_source |
yes | Credential source for client secret |
scopes |
no | List of OAuth scope strings |
OAuth2 Authorization Code + PKCE (oauth2_auth_code)
Interactive browser-based OAuth2 with PKCE. Opens browser, receives callback on local server, exchanges code for tokens. Supports automatic refresh.
{
"auth": {
"type": "oauth2_auth_code",
"authorization_url": "https://provider.example.com/authorize",
"token_url": "https://provider.example.com/token",
"client_id_source": "env:CLIENT_ID",
"scopes": ["openid", "profile"]
}
}
| Field | Required | Description |
|---|---|---|
authorization_url |
yes | Authorization endpoint |
token_url |
yes | Token endpoint |
client_id_source |
no | Credential source for client ID |
client_secret_source |
no | Credential source for client secret |
scopes |
no | List of OAuth scopes |
OpenID Connect (openid_connect)
Auto-discovers OAuth2 endpoints from a .well-known/openid-configuration document, then runs the authorization code flow.
{
"auth": {
"type": "openid_connect",
"openid_connect_url": "https://provider.example.com/.well-known/openid-configuration",
"client_id_source": "env:CLIENT_ID"
}
}
| Field | Required | Description |
|---|---|---|
openid_connect_url |
yes | OIDC discovery URL |
client_id_source |
no | Credential source for client ID |
client_secret_source |
no | Credential source for client secret |
scopes |
no | List of scopes |
Browser Login (browser_login)
Opens a browser for interactive login. Two modes:
OAuth mode (when authorization_url + token_url + client_id_source are set): Full OAuth2 with PKCE and persistent refresh tokens.
{
"auth": {
"type": "browser_login",
"authorization_url": "https://provider.example.com/authorize",
"token_url": "https://provider.example.com/token",
"client_id_source": "env:CLIENT_ID",
"persist": true
}
}
Simple mode (when login_url is set): Opens a login page and captures a credential from the redirect callback.
{
"auth": {
"type": "browser_login",
"login_url": "https://provider.example.com/login",
"capture_name": "session_id",
"callback_capture": "cookie",
"persist": true
}
}
| Field | Required | Description |
|---|---|---|
authorization_url |
OAuth mode | Authorization endpoint |
token_url |
OAuth mode | Token endpoint |
client_id_source |
OAuth mode | Credential source for client ID |
login_url |
simple mode | Login page URL |
capture_name |
simple mode | Name of the credential to capture |
callback_capture |
simple mode | "cookie", "header", "query_param", or "body_field" |
location |
no | "header" (default), "query", "cookie" |
persist |
no | Save credential for reuse |
Device Code (device_code)
OAuth2 Device Authorization Grant (RFC 8628) for headless environments -- SSH sessions, Docker containers, CI pipelines. Displays a code, user authorizes on any device.
{
"auth": {
"type": "device_code",
"device_authorization_url": "https://provider.example.com/device_authorization",
"token_url": "https://provider.example.com/token",
"client_id_source": "env:CLIENT_ID",
"persist": true
}
}
| Field | Required | Description |
|---|---|---|
device_authorization_url |
yes | Device authorization endpoint |
token_url |
yes | Token endpoint |
client_id_source |
yes | Credential source for client ID |
scopes |
no | List of scopes |
persist |
no | Save refresh token for reuse |
Manual Token (manual_token)
Prompts for a token via hidden input. Simplest interactive auth. Optionally persists the token so subsequent runs skip the prompt.
{
"auth": {
"type": "manual_token",
"persist": true,
"credential_name": "X-Custom-Token"
}
}
| Field | Required | Description |
|---|---|---|
location |
no | "header" (default), "query", "cookie" |
credential_name |
no | Stored credential name |
persist |
no | Save token for reuse |
API Key Generation (api_key_gen)
Calls a remote endpoint to provision an API key, then persists and reuses it. For APIs that require key creation before use.
{
"auth": {
"type": "api_key_gen",
"key_create_endpoint": "https://api.example.com/keys",
"key_response_field": "api_key",
"key_create_auth_source": "env:BOOTSTRAP_TOKEN",
"persist": true
}
}
| Field | Required | Default | Description |
|---|---|---|---|
key_create_endpoint |
yes | POST endpoint to create the key | |
key_response_field |
no | "api_key" |
Field name in the response JSON |
key_create_body |
no | {} |
JSON body for the creation request |
key_create_auth_source |
no | Bootstrap auth for key creation | |
location |
no | "header" |
Where to inject the key |
header / param_name |
no | "X-API-Key" |
Key name |
persist |
no | true |
Save the key for reuse |
Build Plugin
The build plugin compiles your API profile into a standalone CLI.
specli build compile
Produces a self-contained PyInstaller binary.
| Flag | Short | Required | Default | Description |
|---|---|---|---|---|
--profile |
-p |
yes | Profile to bake in | |
--name |
-n |
yes | Output binary name | |
--output |
-o |
no | ./dist |
Output directory |
--cli-version |
no | 1.0.0 |
Version string | |
--onedir |
no | false |
Directory bundle instead of single file | |
--clean/--no-clean |
no | true |
Remove build artifacts after | |
--source |
-s |
no | Source dir for enrichment | |
--export-strings |
no | Export strings to JSON | ||
--import-strings |
no | Import strings from JSON | ||
--generate-skill |
no | Generate skill files to dir | ||
--no-build |
no | false |
Run pipeline only, skip compilation |
specli build generate
Produces a pip-installable Python package.
| Flag | Short | Required | Default | Description |
|---|---|---|---|---|
--profile |
-p |
yes | Profile to bake in | |
--name |
-n |
yes | Package/CLI name | |
--output |
-o |
no | . |
Directory to create package in |
--cli-version |
no | 1.0.0 |
Package version | |
--source |
-s |
no | Source dir for enrichment | |
--export-strings |
no | Export strings to JSON | ||
--import-strings |
no | Import strings from JSON | ||
--generate-skill |
no | Generate skill files to dir | ||
--no-build |
no | false |
Run pipeline only, skip generation |
The generated CLI has API commands at the top level (no api sub-group), an auth test command, and all global flags.
Skill Plugin
Generates Claude Code skill files from the active profile's OpenAPI spec.
specli skill generate
| Flag | Short | Default | Description |
|---|---|---|---|
--output |
-o |
./skill-output |
Output directory |
--profile |
-p |
default profile | Profile name |
Generated files:
skill-output/
SKILL.md # Main skill: grouped commands, examples, quick start
references/
api-reference.md # Full endpoint docs: params, bodies, responses
auth-setup.md # Auth configuration guide from security schemes
Completion Plugin
Installs shell tab-completion scripts.
specli completion install [SHELL]
Auto-detects shell if omitted. Supported: bash, zsh, fish, powershell.
specli completion show SHELL
Prints the completion script to stdout for manual installation.
Source Code Enrichment
specli can scan your Python source code at build time and pull documentation into the CLI's help text. This means your FastAPI docstrings, Pydantic field descriptions, and module docs automatically become --help output.
How it works
OpenAPI spec ──────────────────────────┐
▼
Python source ── AST scan ── match ── enrich ── build
(docstrings, routes by │
Field(desc)) + params method ▼
+ prefix + path Enriched spec
│
▼
CLI with rich help text
The scanner:
- Walks Python files matching glob patterns
- Finds route handler functions (
@app.get(...),@router.post(...)) - Resolves
APIRouter(prefix=...)to build full paths - Extracts function docstrings (first line as summary, rest as description)
- Parses
Args:/Parameters:sections for parameter descriptions - Reads Pydantic
Field(description=...)for request body fields
Configuration
Add source_enrichment to your profile:
{
"name": "uspto-data-set-api",
"spec": "examples/uspto-openapi.json",
"source_enrichment": {
"source_dir": "/path/to/your/app",
"include": ["**/*.py"],
"exclude": ["**/test_*", "**/__pycache__/**"]
}
}
Or pass it at build time:
specli build compile -p uspto-data-set-api -n uspto --source /path/to/your/app
Enrichment rules
- Source data fills gaps only -- it never overwrites substantive spec content
- A summary is considered "thin" if it's missing, very short, or auto-derived from operationId
- Source descriptions replace spec descriptions only when they're longer
String Export / Import
For full control over every user-facing string in the generated CLI, specli supports a JSON-based export/import workflow. This is the highest-priority layer -- imported strings override both the spec and source enrichment.
Export
specli build compile -p uspto-data-set-api -n uspto \
--export-strings ./strings.json \
--no-build
This produces a structured JSON file:
{
"info": {
"title": "USPTO Data Set API",
"description": "The Data Set API (DSAPI) allows the public users to discover and search USPTO exported data sets..."
},
"tags": {
"metadata": "Find out about the data sets",
"search": "Search a data set"
},
"operations": {
"GET /": {
"summary": "List available data sets",
"description": "",
"parameters": {}
},
"GET /{dataset}/{version}/fields": {
"summary": "Provides the general information about the API and the list of fields that can be used to query the dataset.",
"description": "This GET API returns the list of all the searchable field names...",
"parameters": {
"dataset": "Name of the dataset.",
"version": "Version of the dataset."
}
},
"POST /{dataset}/{version}/records": {
"summary": "Provides search capability for the data set with the given search criteria.",
"description": "This API is based on Solr/Lucene Search...",
"parameters": {
"version": "Version of the dataset.",
"dataset": "Name of the dataset. In this case, the default value is oa_citations"
}
}
}
}
Edit
Edit the JSON manually, or pass it to an LLM:
"Rewrite every summary to be concise and action-oriented. Rewrite every description to be clear for a developer who hasn't read the source code."
Import
specli build compile -p uspto-data-set-api -n uspto \
--import-strings ./strings.json
The priority chain (lowest to highest):
Raw OpenAPI spec → Source enrichment → Imported strings (wins)
Using Generated Skills
specli generates Claude Code skill files that teach AI assistants how to use your API.
Generate
# Standalone
specli skill generate -p uspto-data-set-api --output ./skills/uspto
# Or as part of a build
specli build compile -p uspto-data-set-api -n uspto --generate-skill ./skills/uspto
What you get
| File | Purpose |
|---|---|
SKILL.md |
Main skill file. Grouped commands, usage examples, quick start guide. |
references/api-reference.md |
Every endpoint with parameters, request bodies, and response schemas. |
references/auth-setup.md |
Step-by-step auth setup derived from the spec's security schemes. |
Use with Claude Code
Copy the generated skill directory into your project's .claude/skills/ or register it as a Claude Code skill:
cp -r ./skills/uspto ~/.claude/skills/uspto-skill
Claude Code will discover the skill and use it as context when working with your API -- it knows every endpoint, every parameter, and how to authenticate.
Path Rules
Path rules transform API URL paths into clean CLI command hierarchies.
Configure in your profile's path_rules section:
{
"path_rules": {
"auto_strip_prefix": true,
"strip_prefix": "/api/v1",
"keep": ["v2"],
"skip_segments": ["internal"],
"collapse": {
"/api/v1/users/me": "profile"
}
}
}
| Field | Default | Description |
|---|---|---|
auto_strip_prefix |
true |
Auto-detect and strip the common path prefix |
strip_prefix |
Explicit prefix to strip (overrides auto) | |
keep |
[] |
Segments to preserve even if auto-stripped |
skip_segments |
[] |
Segments to remove from all paths |
collapse |
{} |
Map specific paths to flat command names |
Example transformations (USPTO spec):
Spec paths: CLI commands:
/ → root list
/{dataset}/{version}/fields → fields list <dataset> <version>
/{dataset}/{version}/records (POST) → records create <version> <dataset>
Configuration
Config locations
| Platform | Config | Cache | Data |
|---|---|---|---|
| Linux/BSD | ~/.config/specli/ |
~/.cache/specli/ |
~/.local/share/specli/ |
| macOS/Windows | ~/.specli/ |
~/.specli/ |
~/.specli/ |
Overridable via XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME.
Config files
Project config (./specli.json): Created by specli init. Sets the default profile for the current directory.
Profile (~/.config/specli/profiles/<name>.json): Per-API configuration.
{
"name": "uspto-data-set-api",
"spec": "examples/uspto-openapi.json",
"base_url": "https://developer.uspto.gov/ds-api",
"path_rules": {
"auto_strip_prefix": true
},
"request": {
"timeout": 30,
"verify_ssl": true,
"max_retries": 3
}
}
Environment variables
| Variable | Description |
|---|---|
SPECLI_PROFILE |
Override default profile |
SPECLI_BASE_URL |
Override profile's base URL |
Precedence (lowest to highest)
- Global config defaults
- Project config (
./specli.json) - Environment variables (
SPECLI_*) - CLI flags (
--profile, etc.)
Development Setup
# Clone
git clone https://github.com/nicholasgasior/specli.git
cd specli
# Create virtualenv and install with dev dependencies
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,build]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=specli
# Type checking
mypy src/specli
# Linting
ruff check src/ tests/
Project structure
src/specli/
app.py # Typer app factory and entry point
config.py # XDG config, profiles, precedence
models.py # Pydantic models (profiles, specs, operations)
output.py # Rich/JSON/plain output formatting
exceptions.py # Exception hierarchy with exit codes
parser/ # OpenAPI spec loading, $ref resolution, extraction
generator/ # Typer command tree builder, param mapper, path rules
client/ # Sync + async httpx clients with auth, retry, dry-run
auth/ # Auth plugin base, manager, credential store
enrichment/ # Source code scanner, spec enricher, string I/O
commands/ # Built-in commands: init, auth, config, inspect
plugins/ # Plugin system + built-in plugins
build/ # Binary compilation and package generation
skill/ # Claude Code skill file generation
completion/ # Shell tab-completion
api_key/ # API key auth plugin
bearer/ # Bearer token auth plugin
basic/ # HTTP basic auth plugin
oauth2_auth_code/ # OAuth2 authorization code + PKCE
oauth2_client_credentials/ # OAuth2 client credentials
openid_connect/ # OpenID Connect discovery + auth
browser_login/ # Browser-based login (OAuth + simple modes)
device_code/ # OAuth2 device code flow
manual_token/ # Interactive token prompt
api_key_gen/ # Remote API key provisioning
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file specli-0.1.0.tar.gz.
File metadata
- Download URL: specli-0.1.0.tar.gz
- Upload date:
- Size: 207.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c75595a6459d58ecf0dcb994d244b2900531431b857bcf2a4a9e2bdd62927ef0
|
|
| MD5 |
b934530f956459ba6b36320e458f49c2
|
|
| BLAKE2b-256 |
5b355b51a9064dcced4363130b608077a7b6e3ca7e744d6f0450a1c5c05fc476
|
File details
Details for the file specli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: specli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 148.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1c263b55b9d704f9def7d5cd735f01b571aab2651887c54323e91e342d34ae1
|
|
| MD5 |
f10cfb8dc20d9af0b63824107aa50de6
|
|
| BLAKE2b-256 |
d8b78c8dfc1297210d0c6b37e1d436d98bb2b94982425cf7be1fb344434de70e
|