OpenAPI is usually treated as an input to something else. Generate an SDK. Render Swagger UI. Publish reference docs. Feed a contract test. All of those are good jobs for a spec.
But there is another useful job hiding in the same document: teach the terminal how to work with the API.
That does not have to mean generating a new repository, choosing a language runtime, publishing a package, and keeping a client binary in sync with every operation change. A CLI can load an OpenAPI description at runtime, cache what it needs, and turn repeated API work into commands without making users rebuild a client.
That is the core bet behind Restish. It can still make a one-off HTTP request, but once an API publishes OpenAPI, Restish can turn that API into a small shell-native command group.
api.rest.sh API. Local setup commands
are shown as fenced shell snippets because they write Restish config on your
machine.Start With Plain HTTP
A good API CLI still needs the universal escape hatch: send a request to a URL. Restish does not require setup before it is useful.
That path matters because API work often starts messy. You paste a URL from a bug report, check a response header, inspect an error body, or try one endpoint before you know whether the API deserves local setup.
That is useful, but it is not the whole job. The moment you call the same API repeatedly, the URL, auth, parameters, output shape, and pagination rules start becoming a little client you keep reconstructing by hand.
OpenAPI already knows a lot of that.
Register The API Once
When an API publishes an OpenAPI document, connect it to Restish:
restish api connect example api.rest.sh
restish example --help
Restish discovers the spec, stores the API registration, and caches the
description. The API name becomes a command group. After that, generated
operations are available under restish example ....
The browser preview used in these docs has a built-in example mapping, so you
can try the generated-command shape without writing local config:
That command is not a wrapper around a handwritten curl string. It comes from
the API description. Restish knows the operation, parameters, path, response
shape, and profile context, then runs the request through the same pipeline used
by ordinary URL requests.
What The Spec Becomes
OpenAPI has many fields that look like documentation metadata until a CLI starts using them as interface design.
operationId becomes a command name:
paths:
/items/{item-id}:
get:
operationId: getItem
summary: Get one item
Restish turns that into:
restish myapi get-item alpha
Path parameters become positional arguments. Optional query, header, and cookie parameters become flags. Summaries and descriptions become help text. Schemas describe request bodies and parameter types. Servers influence where operations are sent. Security requirements tell Restish which credentials an operation can use.
Here is the anatomy of a generated command:
restish inventory update-item --dry-run item-123 'name: Travel mug, enabled: true'restish
Restish itself
The universal entry point.inventory
Chosen during api connect
Local operator vocabulary, not an OpenAPI field.update-item
operationId: updateItem
The operation ID becomes the command name.--dry-run
Optional query parameter dry_run
Optional parameters become flags.item-123
Required path parameter item-id
Required path parameters become positional arguments.'name: Travel mug, enabled: true'
JSON request body schema
Body input can come from shorthand, stdin, or a file.The corresponding OpenAPI operation might look like this:
paths:
/items/{item-id}:
patch:
operationId: updateItem
summary: Update one item
parameters:
- name: item-id
in: path
required: true
schema:
type: string
- name: dry_run
in: query
schema:
type: boolean
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
enabled:
type: boolean
That means spec quality becomes CLI quality. Stable operation IDs, clear parameter names, accurate schemas, and honest auth metadata are not only good for docs. They make the generated command easier to discover, complete, and run.
When a spec needs CLI-specific polish, Restish supports targeted extensions:
x-cli-name: list-items
x-cli-aliases: [items]
x-cli-description: List items with optional filtering.
Those extensions should stay small. OpenAPI remains the source of truth for the API. Restish-specific hints are for command vocabulary, hiding confusing operations, auth setup hints, and other terminal UX details that the base spec cannot express cleanly.
Why Runtime Helps
Generated SDKs make sense inside applications. A service written in Go, TypeScript, Python, or Java usually wants typed calls, versioned dependencies, and application-local error handling. That is a different job.
A terminal workflow has different constraints:
- users want the newest operation without waiting for a released client
- commands need to compose with files, pipes,
jq,grep, and CI scripts - auth and environment selection belong in local profiles
- output has to be helpful for humans and boring for scripts
- debugging often starts before anyone knows which language a real integration will use
Runtime generation fits that shape. You connect an API once, then sync the spec when it changes:
restish api sync example
restish example --help
There is still caching. Restish should not fetch and parse a large spec for every shell completion or every request. But the cached artifact is local operational state, not a generated client project that must be reviewed, published, installed, and eventually forgotten.
This also keeps API examples honest. Documentation can say:
restish example get-image jpeg > dragonfly.jpg
That example is both readable to a person and connected to the live API shape after sync. If the API publishes a better command name, updated schema, or new auth metadata, the CLI can pick it up from the spec instead of waiting for a new handwritten wrapper.
Output Is A Contract
Turning OpenAPI into commands only helps if the result behaves like a good CLI. The generated command should not dump surprising diagnostics into a script, add color to piped output, or corrupt a downloaded file because the tool tried to be helpful.
Restish v2 treats output as a product contract:
stdoutcarries the selected response datastderrcarries diagnostics, warnings, progress, and verbose traces- terminal output can be readable by default
- redirected unfiltered responses preserve body bytes
- explicit filters and formats produce structured output for the next program
For example, ask for one field from every paginated item:
The same rule applies to generated OpenAPI commands and plain URL requests. The API-aware layer should add names, help, auth, and request shaping. It should not break the shell contract underneath.
Auth Belongs With Profiles
OpenAPI command generation becomes much more useful when credentials are not pasted into every command.
Restish keeps repeated auth in profiles. A profile can hold API keys, bearer tokens, OAuth configuration, mTLS settings, external tool auth, base URLs, headers, query defaults, and OpenAPI credential bindings.
The practical difference is that this:
restish github list-issues --state open
can carry the right base URL, token source, TLS config, and operation-specific credential choice without turning every docs example into a wall of headers.
OpenAPI security matters here. Some operations are public. Some allow several auth alternatives. Some require a credential that should only be used for a subset of operations. Restish v2 treats operation security as request execution metadata, not as one global header pasted onto every generated call.
That is where a runtime CLI can feel more like a local tool than a copied HTTP example. Profiles remember the environment. The spec explains the operation. The command stays small enough to type.
The Same Source Can Feed Agents
OpenAPI-to-CLI is also a useful bridge to agent tools.
For humans, Restish turns OpenAPI operations into terminal commands. For MCP
clients, the restish-mcp plugin can expose registered operations as tools:
restish mcp serve example
That does not mean every endpoint should be exposed to every model, user, or profile. Restish keeps MCP conservative by default: read-oriented tools are shown first, write tools require explicit opt-in, and operations can be allowlisted or hidden.
The important part is that both interfaces use the same source of truth. The OpenAPI document describes the API. Restish profiles carry local auth and environment choices. The request pipeline owns TLS, retries, timeouts, normalization, filtering, and output behavior.
Humans use the CLI. Agents use MCP. The API should not need a separate pile of handwritten glue for each one.
There is also a lighter-weight path when an agent is calling Restish through an
ordinary command instead of MCP: render the response as TOON.
TOON is a token-dense text encoding for
JSON-shaped data, and Restish now supports it with -o toon:
That is not a replacement for filtering. The biggest savings still come from projecting the response down to the records and fields the agent needs, then using TOON when the remaining shape is a uniform list. Restish treats TOON as output-only, so JSON remains the format to use for request bodies and for workflows where the consumer expects standard JSON. The output formats reference covers the tradeoffs.
CLI-Friendly Specs
If you publish an API and want it to work well in tools like Restish, start with the ordinary OpenAPI basics:
- publish the spec at a predictable URL such as
/openapi.json - use stable, human-readable
operationIdvalues - write summaries that make sense as command help
- give parameters clear names and accurate schemas
- describe request and response bodies honestly
- model auth per operation, including public operations
- provide examples for tricky request shapes
- expose pagination links or enough metadata for clients to proceed safely
Then test the terminal shape:
restish api connect myapi https://api.example.test
restish myapi --help
restish doctor api myapi
If the generated commands are awkward, the fix is often useful beyond Restish. Better operation IDs improve docs anchors. Better schemas improve SDKs. Better security metadata improves Swagger UI, contract tests, and agent tools.
That is the quiet advantage of OpenAPI as a shared interface contract. A spec that can teach a terminal usually teaches other tools better too.
Try It Locally
Install Restish:
brew install restish
restish --version
Or with Go:
go install github.com/rest-sh/restish/v2/cmd/restish@latest
restish --help
Connect the public example API:
restish api connect example api.rest.sh
restish example --help
restish example list-images
Useful next stops:
- Connect to an API covers setup, explicit spec URLs, sync, and project config.
- OpenAPI Reference explains command naming, parameters, extensions, and generated help.
- Output Guide covers formats, filters, redirects, and script-friendly output.
- Authentication Guide covers profiles and repeated credentials.
- Serve APIs Over MCP shows how registered APIs become MCP tools.
OpenAPI is not only for generated SDKs and browser docs. If the spec already knows the API, the terminal should be able to learn from it too.
Try Restish
Make any REST-ish API feel shell-native.
Run the browser tour for live examples, or install Restish locally to turn OpenAPI-described REST APIs into command-line workflows.