If the upstream service exposes a normal HTTP model API, write a
provider plugin instead. If the upstream
runtime owns complete agent sessions, tool events, compaction, or background
task state, use an agent harness.
What the plugin owns
A CLI backend plugin has three contracts:
The manifest is discovery metadata: it does not execute the CLI or register
runtime behavior. Runtime behavior starts when the plugin entry calls
api.registerCliBackend(...).
Minimal backend plugin
1
Create package metadata
package.json
./src/index.ts, add openclaw.runtimeExtensions pointing at the
built JavaScript peer. See Entry points.2
Declare backend ownership
openclaw.plugin.json
cliBackends is the runtime ownership list; it lets OpenClaw auto-load the
plugin when config or model selection mentions acme-cli/....setup.cliBackends is the descriptor-first setup surface. Add it when
model discovery, onboarding, or status should recognize the backend
without loading plugin runtime. Use requiresRuntime: false only when
those static descriptors are enough for setup.3
Register the backend
index.ts
cliBackends entry. The
registered config is only the default; user config under
agents.defaults.cliBackends.acme-cli merges over it at runtime.Config shape
CliBackendConfig describes how OpenClaw should launch and parse the CLI:
Prefer the smallest static config that matches the CLI. Add plugin callbacks
only for behavior that really belongs to the backend.
Advanced backend hooks
CliBackendPlugin can also define:
Keep these hooks provider-owned. Do not add CLI-specific branches to core when
a backend hook can express the behavior.
prepareExecution(ctx) receives ctx.contextTokenBudget, the effective token
limit selected for the run. Backends that own native compaction can map that
budget into their CLI-specific launch contract.
runtimeArtifact is plugin-owned and is not user-overridable. It is consulted
only when a live inference turn mints or revalidates verified setup authority;
normal CLI runs do not require it. A backend without this declaration cannot
mint verified CLI setup authority. A bundled-package-tree declaration names
the exact package.json owner and requires the package entrypoint to be the
command. OpenClaw hashes the bounded complete installed package tree, including
nested dependencies, and fails closed for redirecting symlinks,
launchers outside the declared package, required external dependency
declarations, oversized trees, and unknown scripts. Declare this only when that
tree contains the complete inference implementation; optional tool integrations
do not make an external implementation graph safe.
If the same backend also ships a self-contained native executable, list its
canonical basenames in nativeExecutableNames. Other native commands remain
unverified even when a user overrides the backend command.
ctx.executionMode is "agent" for normal turns and "side-question" for
ephemeral /btw calls. Use it when the CLI needs different one-shot flags,
such as disabling native tools, session persistence, or resume behavior for
BTW. If a backend normally has nativeToolMode: "always-on" but its
side-question argv reliably disables those tools, also set
sideQuestionToolMode: "disabled"; otherwise OpenClaw fails closed when BTW
requires a no-tools CLI run.
Set nativeToolMode: "selectable" only when resolveExecutionArgs can disable
every backend-native tool for an individual run. For those restricted runs,
ctx.toolAvailability.native is an empty tuple and
ctx.toolAvailability.mcp is the exact host-isolated MCP allowlist. The hook
must replace conflicting tool flags and return argv that enforces both values;
OpenClaw calls it once with the final fresh or resume argv and fails closed when
the backend cannot enforce the restriction. MCP names in this context are safe
to auto-approve only because the host has already limited the generated MCP
configuration to those servers and tools.
ownsNativeCompaction: opting out of OpenClaw compaction
If your backend runs an agent that compacts its own transcript, set
ownsNativeCompaction: true so OpenClaw’s safeguard summarizer never runs
against its sessions - the CLI compaction lifecycle returns a no-op and the
turn proceeds. claude-cli declares it because Claude Code compacts
internally with no harness endpoint. Native-harness sessions such as Codex
keep routing to their harness compaction endpoint instead.
Only declare it when all of the following hold, or a deferred
over-budget session can stay over budget or go stale (OpenClaw no longer
rescues it):
- the backend reliably compacts or bounds its own transcript as it nears its window;
- it persists a resumable session so the compacted state survives turns
(for example
--resume/--session-id); - it is not a native-harness compaction session - matching
agentHarnessIdsessions route to the harness endpoint instead.
MCP tool bridge
CLI backends do not receive OpenClaw tools by default. If the CLI can consume an MCP configuration, opt in explicitly:
Only enable the bridge when the CLI can actually consume it. If the CLI has
its own built-in tool layer that cannot be disabled, set
nativeToolMode: "always-on" so OpenClaw can fail closed when a caller requires no native
tools. If it can disable every native tool per run, use "selectable" with the
resolveExecutionArgs contract above.
User configuration
Users can override any backend default:command when the binary is outside PATH.
Verification
For bundled plugins, add a focused test around the builder and setup registration, then run the plugin’s targeted test lane:Checklist
package.json has openclaw.extensions and built runtime entries for published packagesopenclaw.plugin.json declares cliBackends and intentional activation.onStartupsetup.cliBackends is present when setup/model discovery should see the backend coldapi.registerCliBackend(...) uses the same backend id as the manifestUser overrides under
agents.defaults.cliBackends.<id> still winSession, system prompt, image, and output parser settings match the real CLI contract
Targeted tests and at least one live CLI smoke prove the backend path
Related
- CLI backends - user configuration and runtime behavior
- Building plugins - package and manifest basics
- Plugin SDK overview - registration API reference
- Plugin manifest -
cliBackendsand setup descriptors - Agent harness - full external agent runtimes