TL;DR: Fern's CLI generator produces a fully functional CLI from the same API spec you already use for Fern. It's available in early access today — reach out or read our docs if you'd like to ship one alongside your Docs and SDKs.
Why a CLI and why now
Like a developer, Claude Code, Codex, and Cursor already run tests, edit files, and invoke git from the shell. The command line is the native interface for that environment.
MCP solves a different problem: it gives shell-less clients like Claude.ai or ChatGPT a way to reach external systems. For agents that already have a terminal, a CLI is the path of least resistance.
The properties that make CLIs work for humans also make them work for agents. --help at every level lets an agent discover capabilities incrementally instead of loading hundreds of tool definitions upfront. Models trained on billions of shell scripts already know the conventions. And piping output through jq or grep keeps irrelevant data out of the context window, which MCPs can't do as effectively.
Most API companies don't have a CLI because building a good one is a multi-quarter project. You define commands, wire up auth, handle pagination and retries, format output for both pipes and humans, build for five platforms, generate shell completions, and keep all of that in sync as the API evolves. Few teams have the headcount to staff that against the rest of their roadmap.
That's exactly the kind of work our customers don't want to staff, so we built it.
What it is
fern-cli-generator is a Fern generator. You add it to your generators.yml the same way you'd add any other Fern SDK generator:
groups:
cli:
generators:
- name: fern-cli-generator
config:
binaryName: acme # Top-level commandWhen your spec changes, Fern opens a PR against your CLI repo with the generated source. On release, Fern automatically publishes the CLI to npm, Homebrew, and GitHub Releases so your users can install it with their package manager of choice. The CLI stays in sync with your Docs and SDKs, and you never need to write CLI code by hand.
The generator reads the same OpenAPI spec that Fern's SDK generators already use.
The architecture underneath is what matters. Most generators on the market are OpenAPI-only — they assume a single protocol and bake that assumption into the command tree. Ours doesn't, which is why adding AsyncAPI or GraphQL is a matter of teaching the generator a new spec format rather than rewriting the runtime.
What your end users get is a single binary that installs through whichever channel they prefer:
npm install -g @acme/cli
export ACME_API_KEY=sk-...
source <(acme completion zsh) # Tab completion
acme users list --format table
acme users create --json '{"name":"Alice"}' --dry-run
acme users create --format json --help # JSON schema for the commandBuilt with Rust
The CLI generator ships through the standard Fern pipeline — regenerated on every spec change — alongside our other SDK generators. The output is a single, statically linked Rust binary. Users drop it onto their PATH and run it: there’s no language runtime to install and no dependencies to resolve.
We picked Rust for two properties that matter for CLI tools: a small static binary that runs anywhere, and a library ecosystem we can expose as a crate.
Single binary. One self-contained file per platform. Our reference build for the Box Platform API — 279 methods, 1.5 MB OpenAPI spec embedded — is 8.1 MB. We produce macOS and Linux builds for x86_64 and ARM64 (plus static musl Linux builds) and a Windows x86_64 build, distributed through npm, GitHub Releases, shell and PowerShell installers, and optionally Homebrew and Scoop.
Custom commands. Each generated CLI includes a custom.rs file that regeneration never overwrites. Commands you register there join the same command tree and share the generated runtime (auth, retries, output formatting) and a typed Rust SDK client for your API:
pub fn register(app: CliApp) -> CliApp {
app.command_under(&["orders"], refund_all(), OpenApiBinding::handler(run_refund_all))
}Use the generated commands for your API's endpoints, and add custom commands for workflows that span several of them.
Designed for agents
Humans and agents both use CLIs to do their work. They differ in how they figure out what to run, how they recover from mistakes, and how they're invoked.
Self-describing at runtime. Beyond standard --help, the CLI exposes machine-readable schemas: --schema returns any command's input and output JSON schema, and generate-skills writes a SKILL.md per command group as a drop-in context bundle, the same way our SDKs ship reference.md.
Hardened against agent failure modes. Agents make mistakes that don’t look like human typos. That leads to predictable failure modes, including double-encoded URLs, query parameters embedded in IDs, and malformed path segments. The CLI normalizes and validates these inputs before sending a request, making it a safer interface for agent-driven workflows.
None of this trades against human ergonomics. The same binary still produces colored help, tabular output, shell completions, and --dry-run previews when a person is running it interactively.
Request early access
If you're shipping an API that customers (or their agents) want to script against, reach out or read the docs. We're onboarding early access customers now.