Skip to content
FeedbackDashboard
Codegen targets

CLI

Generate production-ready command line tools from your OpenAPI specification

The Stainless CLI generator creates command line tools from your OpenAPI specification. Generated CLIs include automatic pagination, interactive TUI explorer, and man pages. The CLI wraps your Go SDK with command line argument parsing.

Example repositories:

A CLI tool lets your users interact with your API from the terminal and integrate it into shell scripts and automation workflows. CLI tools are particularly valuable for developer-focused APIs and CI/CD integrations.

Before generating a CLI tool, be aware of the following requirements:

Go SDK dependency

The CLI generator creates a wrapper around your Go SDK, so Go must be included as one of your SDK targets and you must have a public-facing repository for your Go SDK.

Argument structure limitations

Command line interfaces have limitations when passing deeply nested structures as arguments. The generator creates ergonomic flags, but you may need to provide deeply nested parameters in JSON or YAML format.

To generate a CLI tool, add the cli target to your Stainless configuration file.

The CLI generator creates a wrapper around your Go SDK, so you’ll need to enable both targets:

targets:
go:
package_name: github.com/my-company/my-sdk-go
production_repo: my-org/my-sdk-go
cli:
binary_name: my-tool
production_repo: my-org/my-tool
edition: cli.2025-10-08

For a complete list of configuration options, see the CLI target reference.

To test or install the CLI locally, you need Go version 1.22 or later installed.

Verify your installation:

Terminal window
go version
# go version go1.22.0 darwin/arm64

Understanding where Go installs binaries

When you run go install, the binary is placed in your Go bin directory:

  • Default location: $HOME/go/bin (or $GOPATH/bin if GOPATH is set)
  • Check your path: Run go env GOPATH to see the base directory

If commands aren’t found after installation, add the Go bin directory to your PATH:

Terminal window
# Add to your shell profile (.zshrc, .bashrc, etc.)
export PATH="$PATH:$(go env GOPATH)/bin"

To test your CLI tool before release:

  1. Clone your CLI staging repository
  2. Navigate to the repository directory
  3. Run the CLI using the provided script:
Terminal window
./scripts/run [resource] [command] [flags]

For example, to test an endpoint:

Terminal window
./scripts/run people retrieve --id 123

Installing locally

To build and install the CLI to your Go bin directory:

Terminal window
go install ./cmd/my-tool

After installation, run the CLI directly:

Terminal window
my-tool --version

The basic structure of your command line tool follows this format:

Terminal window
my-tool [resource [sub-resource...]] method-name --method-arg value

For example, if your Stainless configuration has the following resources:

resources:
$client:
methods:
current_status: get /status
people:
methods:
retrieve: get /person/{id}
create: post /person
list: get /people

Then your generated CLI tool can be used like this:

Terminal window
my-tool current-status
# Output: {"status": "Up and running!"}
my-tool people create --job "President" \
--name.full-name "Abraham Lincoln" \
--name.nickname "Abe Lincoln"
my-tool people retrieve --id 123
my-tool people list

Note that method names like current-status and flags like --full-name use kebab-case, which is conventional for command line tools.

Get help for any command using the --help flag:

Terminal window
# General help
my-tool --help
# Help for a specific endpoint
my-tool people create --help

You can also pipe JSON or YAML data as body parameters:

Terminal window
my-tool people create <<YAML
name:
full_name: Abraham Lincoln
nickname: Honest Abe
job: President
YAML
# Or from a file:
cat person.json | my-tool people create

To pass files to your API, you can use the @myfile.ext syntax:

Terminal window
my-tool people update --photo @abe.jpg

Files can also be passed inside JSON or YAML blobs:

Terminal window
my-tool people update --profile '{pic: "@abe.jpg"}'
# Equivalent:
my-tool people update <<YAML
profile:
pic: "@abe.jpg"
YAML

If you need to pass a string literal that begins with an @ sign, you can escape the @ sign to avoid accidentally passing a file.

Terminal window
my-tool people update --username '\@abe'

For JSON endpoints, the CLI tool does filetype sniffing to determine whether the file contents should be sent as a string literal (for plain text files) or as a base64-encoded string literal (for binary files). If you need to explicitly send the file as either plain text or base64-encoded data, you can use @file://myfile.txt (for string encoding) or @data://myfile.dat (for base64-encoding). Note that absolute paths will begin with @file:// or @data://, followed by a third / (for example, @file:///tmp/file.txt).

Terminal window
my-tool upload-arbitrary-file --file @data://file.txt

Built-in top-level flags:

  • --help, -h: Show help message and exit
  • --version, -v: Print version and exit
  • --base-url: Provide a base URL for the API backend
  • --format=...: Change output formatting (see below)
  • --debug: Show debug information for HTTP requests and responses

Generated CLIs support configuration through environment variables. The environment variable names are defined in your Stainless configuration under client_settings.opts.

For example, if your config includes:

client_settings:
opts:
api_key:
type: string
read_env: MY_TOOL_KEY

Then the CLI reads authentication from the MY_TOOL_KEY environment variable.

Terminal window
MY_TOOL_KEY=sk_my_api_key_abc123xyz my-tool people retrieve --id 123
# Or export to reuse
export MY_TOOL_KEY=sk_my_api_key_abc123xyz
my-tool people create --job "President" \
--name.full-name "Abraham Lincoln" \
--name.nickname "Abe Lincoln"
my-tool people retrieve --id 123
my-tool people list

The default output format is formatted and syntax-highlighted JSON. You can select different formats using the --format flag:

  • --format=auto: Automatically chosen format (currently defaults to json)
  • --format=json: JSON with autoformatting and syntax highlighting
  • --format=jsonl: JSON formatted to fit on a single line
  • --format=raw: Exact raw JSON response from server
  • --format=yaml: Response in YAML format
  • --format=pretty: Human-readable format similar to YAML with a box
  • --format=explore: Interactive TUI explorer for browsing nested data (see below)

The --format=explore option opens an interactive terminal UI for navigating complex JSON responses. This is useful for exploring deeply nested data structures.

Terminal window
my-tool people retrieve --id 123 --format=explore

Keyboard shortcuts:

KeyAction
or kMove up
or jMove down
or lExpand nested object/array
or hCollapse or go back
EnterExpand selected item
pPrint the current value
rShow raw JSON format
qQuit explorer

The explorer uses your system pager (configured via the PAGER environment variable, defaulting to less) for scrolling through large datasets.

Use the --transform flag to filter or reshape JSON responses using GJSON query syntax. This is useful for extracting specific fields or navigating nested structures.

Terminal window
# Extract just the name field
my-tool people retrieve --id 123 --transform "name"
# Get the first item from an array
my-tool people list --transform "items.0"
# Extract multiple fields
my-tool people retrieve --id 123 --transform "{name,job}"
# Filter array items
my-tool people list --transform 'items.#(job=="President")#'

Common GJSON patterns:

PatternDescription
fieldGet a top-level field
parent.childGet a nested field
items.0Get the first array element
items.#Get the array length
items.#.nameGet the name field from all array items
{field1,field2}Extract multiple fields into an object
items.#(status=="active")#Filter array items by condition

Use --transform-error to apply the same transformation to error responses.

For paginated endpoints, your CLI supports automatic pagination. The CLI lazily streams items to your user’s terminal pager (e.g., $PAGER or less). New pages load automatically as the user scrolls.

The CLI respects HTTP 429 (too many requests) responses and throttles according to your response headers.

Published CLI tools include automatically generated man pages. Users can run man my-tool to see the full usage manual.

When running locally, generate man pages by running ./scripts/run @manpages, which creates a compressed file in ./man/man1/, viewable with man ./man/man1/my-tool.1.gz.

Generated CLIs include tab completion support for Bash, Zsh, and Fish shells. Completions are included in Homebrew installations automatically.

Editions allow Stainless to make improvements to SDKs that aren’t backwards-compatible. You can explicitly opt in to new editions when you’re ready. See the SDK and config editions reference for more information.

  • Initial edition for CLI (used by default if no edition is specified)

Once published, users can install your CLI without having Go installed on their machine. Stainless uses GoReleaser to build cross-platform binaries and publish releases.

For details on how Stainless opens Release PRs and manages versioning, see Versioning and releases.

After publishing a release, your users have several installation options:

Homebrew (macOS/Linux) — Recommended for most users:

Terminal window
brew install your-org/tools/your-tool

Pre-built binaries — Download from GitHub releases for any platform.

Go users — Install directly with Go:

Terminal window
go install github.com/your-org/your-tool/cmd/your-tool@latest

Stainless handles formula creation and management when you publish to Homebrew.

Create a Homebrew tap repository
  1. Create a public GitHub repository named homebrew-tools under your organization (or any name starting with homebrew-).

The repository name must start with homebrew- according to Homebrew conventions.

Generate a GitHub Personal Access Token
  1. Click on your account profile picture > Settings > Developer Settings > Personal Access Tokens > Fine-grained tokens > Generate new token.
  2. Choose your organization as the resource owner.
  3. Set “No expiration” to avoid regular renewal (recommended), or choose an expiration date.
  4. Choose “Only select repositories” and select the homebrew repository you created.
  5. Add permissions for “Contents” and “Pull requests” with read and write access.
  6. Generate the token and save it securely.
Add the token to your production repo
  1. In your CLI tool’s production repository, navigate to Secrets and variables > Actions > New repository secret. The URL should look like https://github.com/<org>/<repo>/settings/secrets/actions/new.
  2. Add a new secret named HOMEBREW_TAP_GITHUB_TOKEN with your token.
Update your Stainless config

Update the Stainless config and save:

targets:
cli:
publish:
homebrew:
tap_repo: your-org/homebrew-tools
homepage: https://example.com
description: The official CLI for YourOrg.
Install and use

Once published, users can install your CLI using:

Terminal window
brew tap your-org/tools
brew install your-tool
# or more concise:
brew install your-org/tools/your-tool