All articles
platform engineering·intermediate··Updated

Turn an OpenAPI spec into an agent-facing CLI

A spec-driven CLI gives coding agents a discoverable, executable API surface without hand-maintaining one command per endpoint.

api-explorationclicoding-agentsdeveloper-experienceopenapi
Resources
Resource Link
Implementation vercel-labs/specli
Package specli
Contract OpenAPI Specification

API documentation is not an execution surface

An OpenAPI document can describe thousands of operations and still be awkward for a coding agent. The agent needs to discover the relevant resource, inspect the accepted inputs, construct a valid request, and receive output it can parse.

Handwritten commands solve that problem only while they stay synchronized with the specification. Every renamed parameter or added operation creates another place for the interface to drift.

specli takes the OpenAPI document as the command registry. It derives resources, actions, arguments, request bodies, and validation from the current schema.

Start with schema discovery

The built-in __schema resource lists the interface before any API operation runs. Human-readable output works for terminal inspection; --json --min produces a compact representation for an agent.

terminalbash
npx specli exec ./openapi.json __schema
npx specli exec ./openapi.json __schema --json --min

This discovery command comes from specli, not from an operation in the supplied API. It gives the caller a bounded way to answer “what can this interface do?” before choosing a resource and action.

Execute resource and action pairs

The public command shape is:

terminalbash
npx specli exec <spec> <resource> <action> [arguments] [options]

For a specification that defines a users.list operation with a limit query parameter, the generated command becomes:

terminalbash
npx specli exec ./openapi.json users list --limit 20

The resource and action names come from the specification. Flags such as --limit come from path, query, header, or body parameters on that operation; they are not a fixed list baked into the CLI.

Request bodies can use generated field flags, --data for an inline payload, or --file for a payload on disk. Schema validation runs before the HTTP request.

Inspect a request before sending it

Agents need a way to verify a generated request without mutating the target system. --dry-run returns the resolved method, URL, headers, and body without sending the request. --curl renders the same request as a cURL command.

terminalbash
npx specli exec ./openapi.json users list --limit 20 --dry-run
npx specli exec ./openapi.json users list --limit 20 --curl

These modes keep request construction and request execution on the same code path. The preview therefore checks the interface the caller is about to use instead of a separately maintained example.

Keep machine output explicit

Default output is designed for a person at a terminal. --json changes the contract to one JSON value. An HTTP response contains status and body; CLI-level failures use an error field.

response.jsonjson
{
  "status": 200,
  "body": {
    "items": []
  }
}

The stable envelope lets an agent branch on HTTP status without scraping colors, tables, or explanatory prose. Non-zero exits distinguish command failures from successful execution.

Put access policy outside generation

Generating commands from a specification does not decide which operations a caller should see. specli exposes the OpenAPI document it receives and executes with the credentials supplied to it.

A company-facing wrapper therefore owns three additional decisions:

  • Select or derive the specification available to the caller.
  • Resolve credentials without printing secrets into prompts or logs.
  • Gate high-impact operations before execution.

This boundary keeps the generated layer mechanical. Policy remains explicit in the platform that chooses the schema, credentials, and execution rules.

Compile a stable executable

Development can run directly through npx. A deployed integration can compile the specification and runtime into a standalone executable with Bun.

terminalbash
npx specli compile ./openapi.json --name company-api

The compiled binary keeps the same resource-action interface. A programmatic client exposes the same model through list(), help(resource, action), and exec(...), while the AI SDK integration maps list, help, and execution to tools.

The important design choice is not the transport. It is the single source of truth: discovery, validation, previews, CLI execution, and programmatic execution all derive from the same OpenAPI operations.

Takeaways

Discovery belongs in the executable interface

The same OpenAPI document can list resources, explain an action, validate arguments, and execute the request.

Generated flags keep the contract current

Path, query, and body inputs come from the operation schema instead of a parallel command registry.

Machine mode needs a stable envelope

A single JSON value with HTTP status and body is easier for an agent to inspect than decorated terminal output.

Authorization remains a separate boundary

Command generation exposes the supplied specification. A platform wrapper must still decide which specification, credentials, and operations a caller may use.