Skip to main content
Expose your MCP tools as commands for people, scripts and coding agents. Tool commands need no model key: they call the server directly. The optional chat command talks to a messaging agent through a separate terminal endpoint.

Try the starter

Start npm run mcp in one terminal. In another terminal in the same project:
Check it worked: menu --json prints the café’s menu as JSON. order --help lists the commands for placing and tracking orders.

Define your commands

Install the optional peers when adding a CLI to an existing project:
src/cli.ts defines your product’s command names, groups, arguments and output:
Tools without an explicit command use their tool name with underscores changed to dashes. Commands support --json and --fields; writes support --dry-run, and actions that can delete or overwrite require --yes without an interactive terminal. Add your own Stricli commands alongside the generated tool commands. Handwritten commands execute locally without discovery, including their help and argument parsing; an explicit this.cli.call(...) calls MCP. A handwritten command takes precedence over a derived command with the same name. flow check checks that your CLI configuration still matches the tools. sections groups top-level commands and groups under headings in the order declared; remaining commands appear under Other. chat, when configured, comes first; built-in commands appear under This CLI. flow check rejects unknown names and a name listed in more than one section. Run cafe help order to find commands whose names or descriptions contain every supplied word.

Format results for people

Add output and format to a tool to share readable Markdown across MCP and the CLI. A command without columns or view prints that text by default; --json and --fields keep using the structured result. For terminal rendering, install these optional peers:
For a tool returning { order_id: "BL-1040", ready_by: "10:30" }, the example formatter prints Order BL-1040 is ready by 10:30. Piped output, NO_COLOR, and installations without these peers print the Markdown as written. A formatter error falls back to JSON and records formatError on tool.end; it does not turn a successful operation into a failure. For an account-protected server, run the CLI’s auth login command. It signs the person in with OAuth; automation can use the configured token environment variable instead. The account and permissions are verified by the same MCP server. Run npx @flow-engineer/flow cli --server <url> --help for a generic CLI over an existing MCP server. Always use the full package name with npx; npx flow fetches a different package.

Common commands

The order status result depends on whether that order exists in the running café server. --dry-run previews the operation without invoking the write tool.

List and read skills

Use skill as a catalog and reader, without loading every skill body:
Expected result: list shows names and descriptions, including the generated cafe-cli guide and the café’s allergens workflow. get allergens prints that skill’s full instructions. The last command returns two named records, each with name, description and content; no other skill bodies are read. Replace cafe-cli with <your-cli-name>-cli in your own project. Bare skill is the same as skill list. skill list --json returns only names and descriptions; skill get <name> [name...] --json returns an array even for one selected skill. The generated CLI guide includes handwritten commands and maps MCP tool names to configured CLI command paths. Workflow instructions come from the same agent/skills/ source used by MCP and messaging. To install selected skills:
This writes <dir>/<skill-name>/SKILL.md for each selected skill. Additional source assets are not copied; use native plugin packaging to distribute those. The existing skill --write <dir> command continues to export only the generated CLI guide. A handwritten workflow with the same name takes precedence in the catalog; the legacy export still produces the generated guide. Use the standalone MCP endpoint (/mcp) for workflow skills. Its existing read_skill descriptor advertises names and descriptions in _meta["flow.engineer/skills"]; bodies are fetched only by get. Older MCP servers without this metadata still expose names through the reader’s schema, with empty descriptions. Unknown names trigger one discovery refresh before the CLI reports an error. All requested names are checked before a batch is read or exported.

Cached discovery and server updates

MCP-backed commands execute on your deployed MCP server; ship that server alongside the CLI. The CLI caches tool definitions and the server’s name/version in ~/.config/<name>/discovery/ (XDG_CONFIG_HOME or CliConfig.directory overrides the location). Entries are scoped to the endpoint, client and credentials. Discovery files have mode 600 and contain no credentials or tool results. Cached definitions load immediately, including older snapshots. Help, tools, skill and dry runs do not wait for a server check. Once a snapshot is five minutes old, a background refresh checks the server and fetches current definitions. Installed CLIs use a separate process that survives the command exiting, with a 15-second limit. Concurrent commands share one refresh; background failures keep the previous definitions and do not add output. Tool calls still connect and authenticate. If the server’s name or version differs, the CLI fetches definitions and rebuilds the generated command’s parser and approval flags before sending the call. Bump the MCP server’s version when changing tool contracts or safety annotations. Periodic discovery also picks up changes when a server does not bump its version. If an unknown command or invalid argument could be caused by an old cache, the CLI fetches current definitions and reparses once before execution. If the server rejects arguments after a request has been sent, the CLI refreshes definitions and reports the error with a help hint. It never silently resubmits that request or reruns a handwritten command. To force current discovery:
Expected result: the CLI fetches and prints the server’s current tool definitions and updates the cache. A new tool can also be invoked directly; an unknown command triggers foreground discovery. Missing or malformed cache files require discovery. An unwritable cache directory does not stop commands working, but each invocation must discover again. Set discoveryCache: false in defineCli to disable disk caching, or discoveryCache: { maxAgeMs: 60_000 } for a one-minute background interval. Handwritten local commands need no MCP server unless they explicitly call it; --refresh-tools opts into discovery. If a handwritten command declares its own --refresh-tools flag, that flag keeps its local meaning; use tools --refresh-tools for framework discovery.

Local playground CLI

A local playground can run the team’s actual CLI, including custom commands, OAuth, interactive chat, confirmations, stdout/stderr and exit codes. Opt in on the server that runs on your computer:
The named channel must be declared on the agent. Its tab opens the runner instead of simulated chat. Each browser gets an independent temporary sign-in and conversation directory; the test person picker does not grant it an account. Run auth login, open the sign-in link, then return to the runner. chat keeps reading input until /exit; reload reconnects to that session. Clear output clears the viewport; New CLI session ends the session and removes its local credentials, without clearing the account’s agent history. Sessions expire after 30 minutes without browser activity, and a server restart starts fresh. Ending a session does not undo work already accepted by the agent.This runner accepts only direct loopback connections with a loopback Host and same-origin API requests, even if explicitly configured on a mounted handler. It is absent by default and is not enabled in the hosted test cloud. It invokes runCli in-process: no shell, arbitrary program execution, expansion or pipelines. Quoted arguments work. Its configured endpoints stay fixed; use the installed CLI for --server, --token or skill --write.The browser CLI runner is available locally only. Hosted browser execution is not supported.Embedded clients can set CliConfig.directory to isolate credential/conversation/discovery files and CliIO.env to avoid inheriting process tokens; CliIO.signal cancels sign-in and terminal HTTP requests. CliIO.background(work) lets an embedded host schedule discovery refreshes without delaying the response; invoke work while the session is alive. The local playground supplies this scheduler and cancels refreshes when the session ends. Defaults retain the installed CLI’s existing environment and config directory.

Terminal conversations

A terminal conversation needs the optional agent terminal handler described below. Serving the MCP endpoint alone enables tool commands; it does not add conversational chat.
chat talks directly to the agent, using the same bearer as MCP; it is not an MCP tool. defineCli({ name: "acme", server, agent: "https://agent.example.com/agent/terminal" }) overrides the terminal URL; the default is the MCP server’s origin plus /agent/terminal. acme chat "Which orders are ready?" --json prints one response; acme chat reads a TTY until Ctrl-D or /exit. Without a TTY pass a message to chat (an interactive chat exits 2); sign-in failures exit 3. chat.json is mode 600 and scoped by terminal endpoint. --new forgets the terminal’s saved reference; the agent keeps its one-person history across channels. It does not erase a person’s WhatsApp history. Teams can replace chat with their own Stricli command in commands. Mount the optional terminal handler beside MCP (no model or extra dependency in the CLI):
terminalServer accepts path and channel overrides. It verifies auth on every POST, serializes concurrent requests per person, and returns { conversationId, replies } after the turn settles. The authenticated key becomes the same person that resolve returns on other channels. A body cannot choose its own person; a conversation reference from another person is refused. Its custom UI uses Markdown and exports turns as channel cli. For your own HTTP adapters, agent.request({ channel, key, text, conversationId? }) exposes this without the handler. Supply a verified key and serialize calls for that channel and person.

Add another tool

New tools become commands automatically.

Inspect calls in production

The server records calls from the CLI, MCP and plugins.