Custom commands
The CLI generator is in early access. Reach out to get started.
Custom commands are subcommands you write in Rust and compile into the generated CLI alongside the commands derived from your API spec. Use them for anything that doesn’t map to a single endpoint: a workflow that chains several API calls, a shortcut for a common task, or a local utility such as verifying a webhook signature.
Custom commands call your API through a typed SDK client that Fern generates with the CLI. That client uses the CLI’s own auth, retries, base URL, TLS settings, and global headers, so a handler doesn’t need to set any of these up.
Generated files
Custom command support is on by default. Each fern generate run produces these files in the CLI repository:
The generated repository also includes an agent skill in .agents/skills/custom-commands/, a guide written for coding agents that uses your API’s real endpoint and client names. Point an agent such as Claude Code at it to have it write custom commands for you.
Add a command
Register the command in custom.rs
Open cli/<binaryName>/custom.rs. The generated file contains a register function with a commented-out example. Add each command with app.command(...): a clap Command that defines the name, help text, and arguments, plus a handler wrapped in OpenApiBinding::handler(...).
The handler must be wrapped in OpenApiBinding::handler(...). Passing a bare closure doesn’t compile.
Call your API from a handler
Two helpers in the generated sdk.rs cover most handlers:
super::sdk::client(ctx)returns the typed SDK client, with one field per resource in your API (for example,client.plants). Requests go through the CLI’s own HTTP stack, so the user’s credentials,--base-url, retries, and global headers all apply.super::sdk::block_on(future)runs an async SDK call from inside the handler and converts SDK errors into CLI errors, so?works as expected.
Typed models are available from <binary_name>_sdk::api::* (for a CLI named plant, use plant_sdk::api::*;). To chain calls, use the result of one SDK call as the input to the next inside the same handler.
Choose where the command appears
To keep your own commands at the top level and the API commands under a single group, set rootGroup in generators.yml. With the following config, plant plants list becomes plant api plants list:
Things to know
- Regeneration doesn’t touch
custom.rs, but everything it calls is regenerated from your spec. If a spec change renames an endpoint or resource, updatecustom.rsto match.cargo buildreports every call that no longer exists. - Handlers control their own output. Response transforms and error-recovery hooks configured on the CLI only apply to spec-derived commands, so print results however suits the command.
- Extra Rust crates go in
generators.yml. If a handler imports a crate the CLI doesn’t already depend on, add it withextraDependenciesinstead of editingCargo.toml, which is regenerated. - Wire tests don’t cover custom commands. The optional
generateWireTestssuite only tests spec-derived commands, so add your own tests for custom ones. - You can turn custom commands off. Set
customCommands: falseto generate a spec-only CLI withoutcustom.rsor the SDK and types crates.