incurs
The CLI framework for humans and agents.
Define a command once. incurs derives the argument parsing, help, validation, output formatting, JSON Schemas, and every transport from that single definition — so a command you wrote for a terminal is already an MCP tool, an HTTP route, an OpenAPI operation, a skill file an agent can read, a shell completion, and a window.
[]
= "0.6"
= "1"
= { = "1", = ["derive"] }
= { = "1", = ["full"] }
Requires Rust 1.88 or newer.
Quick start
use Cli;
use ;
use JsonSchema;
use ;
async
$ greet greet Ada --excited --json
{
"message": "Hello, Ada!"
}
Doc comments become descriptions everywhere. CommandDef::typed derives the input
schemas from GreetArgs and GreetOptions, and the output schema from GreetOutput.
The handler receives validated values whether the call arrived from a terminal, HTTP,
or an agent.
What that one definition already gives you
Nothing below needs extra code.
| Run this | You get |
|---|---|
greet --help |
Help for the CLI and every command |
greet greet --schema |
The command's JSON Schema |
greet --llms / --llms-full |
A manifest written for an agent to read |
greet --format json|yaml|toon|jsonl|md |
The same result in any output format |
greet --mcp |
An MCP server over stdio |
greet mcp add |
Registration with detected MCP clients |
greet skills add |
Agent Skills installed for detected agents |
greet completions bash|zsh|fish |
A shell completion script |
greet plugin build --output ./dist |
A portable Agent Plugins 1.0 package |
greet --config-schema |
A schema for the config file format |
Output
A command returns TypedResult::ok(value), and the serialized value is the output.
Pass --full-output for the {ok, data, meta} envelope when a caller wants timing and
command metadata. TypedResult::error(code, message) produces the structured error
envelope and a non-zero exit; ok_with_exit_code passes a wrapped subprocess's status
through without turning success into a failure; ok_with_cta attaches follow-up
commands.
The default format is TOON. Table and CSV are deliberately opt-in, through a separate crate:
= "0.6"
use ;
let cli = cli.default_extra_format;
Feature flags
= { = "0.6", = ["http", "mcp", "openapi"] }
| Feature | Adds |
|---|---|
cli (default) |
Process adapters, signals, and terminal output |
toon, tokens (default) |
TOON output and --token-count / --token-limit |
http |
Axum routes for root and nested commands, plus fetch gateways |
mcp |
MCP server, all five published standards at once |
openapi |
Import an OpenAPI 3.x document as commands, and emit one |
yaml |
YAML output |
agent-plugins, agent-plugins-mcp |
Load Agent Plugin packages, with or without MCP transports |
MCP clients that support discovery negotiate with server/discover; older clients
initialize with their exact standard. Fallback happens only on protocol evidence, never
on an authentication, transport, or server failure.
The incurs command-line tool
Code generation
Point it at a project whose binary exposes --llms-full, and it writes typed helpers
for calling that CLI from Rust:
src/incurs_generated.rs— typed command modules, argument and option types, CTA renderers, and the embedded manifestincurs.manifest.json— the canonical command manifestconfig.schema.json— the config file schema, with--config-schema
--output and --json-output override the first two paths. --entry takes a Cargo
binary name or a path to an executable.
Agent Plugin packages
Any incurs CLI can package itself:
The package keeps its layers separate: skills/<name>/SKILL.md for agents to read,
mcp.json declaring the tool surface, and bin/my-cli as the executable, all declared
by a root plugin.json. Use --plugin-no-mcp for a skills-only package. Regeneration
will not overwrite existing plugin artifacts without --force.
The incurs binary is also a client for those packages:
Installation validates the package, checks its operating system and architecture, and
installs the command into your executable directory. It tells you when that directory
is not on PATH, and never edits a shell profile. Each MCP server connects
independently, so one failure does not hide the tools from the others. The data
directory you name is created before launch, persists across runs, and is never removed
for you.
See Agent Plugins compatibility for the full behavior and failure-boundary matrix.
Calling commands without a CLI
Cli::tool_catalog() exposes every MCP-visible command as a transport-neutral Rust
API. Calls made through it use the same schemas, middleware, environment fields, config
defaults, streaming results, and structured errors as the CLI — it is the boundary MCP,
Code Mode, and the desktop application all go through. try_tool_catalog() reports
name collisions instead of silently replacing a command.
Code Mode
incurs-codemode lets an agent write a small JavaScript program that calls your tools,
instead of making one tool call per step. It provides connector discovery, approval
policy, immutable capability snapshots, deterministic replay, cancellation, ordered
events, artifact-backed large values, rollback hooks, and bounded durable history. It
can wrap an incurs catalog, a remote MCP client, or an authenticated OpenAPI client.
Your command's own annotations decide policy: a read-only tool skips approval only when it is neither destructive nor open-world. Remote tools require approval unless the host installs its own policy resolver.
incurs-codemode-local runs those programs in a resource-limited QuickJS runtime, and
incurs-codemode-mcp exposes the lifecycle to MCP clients:
| Tool | Purpose |
|---|---|
codemode_search |
Search tools and snippets, with the declarations needed to call each match |
codemode_execute |
Start a JavaScript execution |
codemode_execution |
Read execution state or an owned artifact |
codemode_decide |
Approve or reject one pending action |
codemode_cancel |
Cancel a running or paused execution |
Terminal applications
incurs-app-ratatui runs a command graph full-screen in a terminal: commands on
the left, the selected command's inputs collected from its schema, results below.
use TerminalApp;
from_cli?.title.run
The installed incurs tool does this for its own commands:
See its README for the key map and the controls each schema type gets.
Native desktop applications
incurs-app-gpui ships the same command graph as a double-clickable application, for
people who will never open a terminal. Commands are listed in a window, each command's
inputs are collected from its schema, and every call goes through the tool catalog — so
validation, middleware, config defaults, streaming, and cancellation behave exactly as
they do on the CLI.
use DesktopApp;
from_cli?.title.run
MacBundle wraps the built executable in a macOS .app that installs by dragging.
See its README for the full guide and current limits.
How it fits together
typed command definitions
|
v
shared command graph + schemas
______________|____________________________
| | | | |
CLI HTTP MCP tool catalog generated artifacts
| |-- OpenAPI
| |-- Agent Skills
| |-- shell completions
| `-- Rust and JSON codegen
|
|-- Code Mode (local QuickJS, remote executors)
|
`-- native desktop window
Learn more
incurs explain— the authoring reference, also compiled intoSKILL.mdfor agents- A worked example covering commands, streaming, middleware, CTAs, discovery, and output formats
- MIGRATION.md — upgrading between releases
- CHANGELOG.md — what changed
- CONTRIBUTING.md — working on incurs itself
License
MIT. incurs began as a Rust port of wevm/incur and keeps its command model; the two are now independent.