--- summary: "CLI backends: local AI CLI fallback with optional MCP tool bridge" read_when: - You want a reliable fallback when API providers fail - You are running Codex CLI or other local AI CLIs and want to reuse them - You want to understand the MCP loopback bridge for CLI backend tool access title: "CLI Backends" --- # CLI backends (fallback runtime) OpenClaw can run **local AI CLIs** as a **text-only fallback** when API providers are down, rate-limited, or temporarily misbehaving. This is intentionally conservative: - **OpenClaw tools are not injected directly**, but backends with `bundleMcp: true` can receive gateway tools via a loopback MCP bridge. - **JSONL streaming** for CLIs that support it. - **Sessions are supported** (so follow-up turns stay coherent). - **Images can be passed through** if the CLI accepts image paths. This is designed as a **safety net** rather than a primary path. Use it when you want “always works” text responses without relying on external APIs. If you want a full harness runtime with ACP session controls, background tasks, thread/conversation binding, and persistent external coding sessions, use [ACP Agents](/tools/acp-agents) instead. CLI backends are not ACP. ## Beginner-friendly quick start You can use Codex CLI **without any config** (the bundled OpenAI plugin registers a default backend): ```bash openclaw agent --message "hi" --model codex-cli/gpt-5.4 ``` If your gateway runs under launchd/systemd and PATH is minimal, add just the command path: ```json5 { agents: { defaults: { cliBackends: { "codex-cli": { command: "/opt/homebrew/bin/codex", }, }, }, }, } ``` That’s it. No keys, no extra auth config needed beyond the CLI itself. If you use a bundled CLI backend as the **primary message provider** on a gateway host, OpenClaw now auto-loads the owning bundled plugin when your config explicitly references that backend in a model ref or under `agents.defaults.cliBackends`. ## Using it as a fallback Add a CLI backend to your fallback list so it only runs when primary models fail: ```json5 { agents: { defaults: { model: { primary: "anthropic/claude-opus-4-6", fallbacks: ["codex-cli/gpt-5.4"], }, models: { "anthropic/claude-opus-4-6": { alias: "Opus" }, "codex-cli/gpt-5.4": {}, }, }, }, } ``` Notes: - If you use `agents.defaults.models` (allowlist), you must include your CLI backend models there too. - If the primary provider fails (auth, rate limits, timeouts), OpenClaw will try the CLI backend next. ## Configuration overview All CLI backends live under: ``` agents.defaults.cliBackends ``` Each entry is keyed by a **provider id** (e.g. `codex-cli`, `my-cli`). The provider id becomes the left side of your model ref: ``` / ``` ### Example configuration ```json5 { agents: { defaults: { cliBackends: { "codex-cli": { command: "/opt/homebrew/bin/codex", }, "my-cli": { command: "my-cli", args: ["--json"], output: "json", input: "arg", modelArg: "--model", modelAliases: { "claude-opus-4-6": "opus", "claude-sonnet-4-6": "sonnet", }, sessionArg: "--session", sessionMode: "existing", sessionIdFields: ["session_id", "conversation_id"], systemPromptArg: "--system", systemPromptWhen: "first", imageArg: "--image", imageMode: "repeat", serialize: true, }, }, }, }, } ``` ## How it works 1. **Selects a backend** based on the provider prefix (`codex-cli/...`). 2. **Builds a system prompt** using the same OpenClaw prompt + workspace context. 3. **Executes the CLI** with a session id (if supported) so history stays consistent. 4. **Parses output** (JSON or plain text) and returns the final text. 5. **Persists session ids** per backend, so follow-ups reuse the same CLI session. The bundled Anthropic `claude-cli` backend is supported again. Anthropic staff told us OpenClaw-style Claude CLI usage is allowed again, so OpenClaw treats `claude -p` usage as sanctioned for this integration unless Anthropic publishes a new policy. ## Sessions - If the CLI supports sessions, set `sessionArg` (e.g. `--session-id`) or `sessionArgs` (placeholder `{sessionId}`) when the ID needs to be inserted into multiple flags. - If the CLI uses a **resume subcommand** with different flags, set `resumeArgs` (replaces `args` when resuming) and optionally `resumeOutput` (for non-JSON resumes). - `sessionMode`: - `always`: always send a session id (new UUID if none stored). - `existing`: only send a session id if one was stored before. - `none`: never send a session id. Serialization notes: - `serialize: true` keeps same-lane runs ordered. - Most CLIs serialize on one provider lane. - OpenClaw drops stored CLI session reuse when the backend auth state changes, including relogin, token rotation, or a changed auth profile credential. ## Images (pass-through) If your CLI accepts image paths, set `imageArg`: ```json5 imageArg: "--image", imageMode: "repeat" ``` OpenClaw will write base64 images to temp files. If `imageArg` is set, those paths are passed as CLI args. If `imageArg` is missing, OpenClaw appends the file paths to the prompt (path injection), which is enough for CLIs that auto- load local files from plain paths. ## Inputs / outputs - `output: "json"` (default) tries to parse JSON and extract text + session id. - For Gemini CLI JSON output, OpenClaw reads reply text from `response` and usage from `stats` when `usage` is missing or empty. - `output: "jsonl"` parses JSONL streams (for example Codex CLI `--json`) and extracts the final agent message plus session identifiers when present. - `output: "text"` treats stdout as the final response. Input modes: - `input: "arg"` (default) passes the prompt as the last CLI arg. - `input: "stdin"` sends the prompt via stdin. - If the prompt is very long and `maxPromptArgChars` is set, stdin is used. ## Defaults (plugin-owned) The bundled OpenAI plugin also registers a default for `codex-cli`: - `command: "codex"` - `args: ["exec","--json","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]` - `resumeArgs: ["exec","resume","{sessionId}","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]` - `output: "jsonl"` - `resumeOutput: "text"` - `modelArg: "--model"` - `imageArg: "--image"` - `sessionMode: "existing"` The bundled Google plugin also registers a default for `google-gemini-cli`: - `command: "gemini"` - `args: ["--prompt", "--output-format", "json"]` - `resumeArgs: ["--resume", "{sessionId}", "--prompt", "--output-format", "json"]` - `modelArg: "--model"` - `sessionMode: "existing"` - `sessionIdFields: ["session_id", "sessionId"]` Prerequisite: the local Gemini CLI must be installed and available as `gemini` on `PATH` (`brew install gemini-cli` or `npm install -g @google/gemini-cli`). Gemini CLI JSON notes: - Reply text is read from the JSON `response` field. - Usage falls back to `stats` when `usage` is absent or empty. - `stats.cached` is normalized into OpenClaw `cacheRead`. - If `stats.input` is missing, OpenClaw derives input tokens from `stats.input_tokens - stats.cached`. Override only if needed (common: absolute `command` path). ## Plugin-owned defaults CLI backend defaults are now part of the plugin surface: - Plugins register them with `api.registerCliBackend(...)`. - The backend `id` becomes the provider prefix in model refs. - User config in `agents.defaults.cliBackends.` still overrides the plugin default. - Backend-specific config cleanup stays plugin-owned through the optional `normalizeConfig` hook. ## Bundle MCP overlays CLI backends do **not** receive OpenClaw tool calls directly, but a backend can opt into a generated MCP config overlay with `bundleMcp: true`. Current bundled behavior: - `codex-cli`: no bundle MCP overlay - `google-gemini-cli`: no bundle MCP overlay When bundle MCP is enabled, OpenClaw: - spawns a loopback HTTP MCP server that exposes gateway tools to the CLI process - authenticates the bridge with a per-session token (`OPENCLAW_MCP_TOKEN`) - scopes tool access to the current session, account, and channel context - loads enabled bundle-MCP servers for the current workspace - merges them with any existing backend `--mcp-config` - rewrites the CLI args to pass `--strict-mcp-config --mcp-config ` If no MCP servers are enabled, OpenClaw still injects a strict config when a backend opts into bundle MCP so background runs stay isolated. ## Limitations - **No direct OpenClaw tool calls.** OpenClaw does not inject tool calls into the CLI backend protocol. Backends only see gateway tools when they opt into `bundleMcp: true`. - **Streaming is backend-specific.** Some backends stream JSONL; others buffer until exit. - **Structured outputs** depend on the CLI’s JSON format. - **Codex CLI sessions** resume via text output (no JSONL), which is less structured than the initial `--json` run. OpenClaw sessions still work normally. ## Troubleshooting - **CLI not found**: set `command` to a full path. - **Wrong model name**: use `modelAliases` to map `provider/model` → CLI model. - **No session continuity**: ensure `sessionArg` is set and `sessionMode` is not `none` (Codex CLI currently cannot resume with JSON output). - **Images ignored**: set `imageArg` (and verify CLI supports file paths).