Configuration: Tools and Custom Providers
Learn about tools configuration, including profiles, experimental toggles, and provider-backed tools, plus how to set up custom providers and base URLs for your gateway.
Read this when
- Configuring `tools.*` policy, allowlists, or experimental features
- Registering custom providers or overriding base URLs
- Setting up OpenAI-compatible self-hosted endpoints
tools.* configuration keys and custom provider / base-URL setup. For agents, channels, and other top-level configuration keys, refer to the Configuration reference.
Tools
Tool profiles
tools.profile establishes a base allowlist before tools.allow/tools.deny:
Note
During local onboarding, new local configs default to
tools.profile: "coding"when not specified (existing explicit profiles remain unchanged).
| Profile | Includes |
|---|---|
minimal | session_status only |
coding | group:fs, group:runtime, group:web, group:sessions, group:memory, cron, get_goal, create_goal, update_goal, progress_card, ask_user, skill_workshop, image, image_generate, music_generate, video_generate |
messaging | group:messaging, sessions, sessions_list, sessions_history, sessions_search, conversations_list, conversations_send, conversations_turn, sessions_send, sessions_spawn, sessions_yield, subagents, session_status, ask_user |
full | No restriction (same as unset) |
coding and messaging also implicitly allow bundle-mcp (configured MCP servers).
Tool groups
| Group | Tools |
|---|---|
group:runtime | exec, process, code_execution (bash is accepted as an alias for exec) |
group:fs | read, write, edit, apply_patch |
group:sessions | sessions, sessions_list, sessions_history, sessions_search, conversations_list, conversations_send, conversations_turn, sessions_send, sessions_spawn, sessions_yield, subagents, session_status, suggest_task, dismiss_task |
group:memory | memory_search, memory_get |
group:web | web_search, x_search, web_fetch |
group:ui | browser, screen, dashboard, terminal, portal, canvas, show_widget |
group:automation | heartbeat_respond, cron, gateway |
group:messaging | message |
group:nodes | nodes, computer |
group:agents | agents_list, get_goal, create_goal, update_goal, progress_card, ask_user, skill_workshop |
group:media | image, image_generate, music_generate, video_generate, tts |
group:openclaw | Every built-in tool listed above except read/write/edit/apply_patch/exec/process/canvas (plugin tools are not part of this set) |
group:plugins | Tools that loaded plugins own, which includes configured MCP servers surfaced via bundle-mcp |
With suggest_task, a coding agent can propose follow-up work that is confirmed but not yet executed. The suggested project directory has to be a git checkout; when the tool records the suggestion, it rejects anything invalid, such as a blank prompt or a directory that is not a git repo. In the Control UI, the title and summary appear as a chip that can be acted on, while a Gateway-backed TUI shows an equivalent interactive prompt instead. Accepting a suggestion can launch it in a fresh managed worktree (the default behavior), run it locally in a new session inside the suggested checkout, hand it to a configured cloud worker profile, or pass it into the source session. OpenClaw forwards the complete prompt to whichever destination was chosen while the current turn keeps running. dismiss_task retracts a suggestion that is still pending, using the temporary task_id that suggest_task returned.
These tools are exposed only when the originating operator surface can receive and respond to Gateway task-suggestion events. Channel sessions and local or embedded TUI sessions do not get those events; channel transports need a portable typed task action before this flow can be safely enabled. Suggestions live only in the current process and vanish when the Gateway restarts. Both tools stay in the coding profile and group:sessions, so the usual tools.allow and tools.deny policy picks them up automatically whenever the surface supports them.
MCP and plugin tools inside sandbox tool policy
Configured MCP servers show up as plugin-owned tools under the bundle-mcp plugin id. Regular tool profiles can grant access to them, but tools.sandbox.tools acts as a second gate for sandboxed sessions. With sandbox mode set to "all" or "non-main", add one of these entries to the sandbox tool allowlist when MCP or plugin tools should be visible:
bundle-mcpfor OpenClaw-managed MCP servers originating frommcp.servers- the plugin id for a particular native plugin
group:pluginsto cover every tool owned by loaded plugins- exact MCP server tool names or server globs such as
outlook__send_mailoroutlook__*when a single server is all you need
Server globs rely on the provider-safe MCP server prefix, not necessarily the raw mcp.servers key. Any character outside the [A-Za-z0-9_-] set becomes -, names that do not begin with a letter receive an mcp- prefix, and prefixes that are long or collide may be truncated or suffixed; as an example, mcp.servers["Outlook Graph"] matches a glob like outlook-graph__*.
{
agents: { defaults: { sandbox: { mode: "all" } } },
mcp: {
servers: {
outlook: { command: "node", args: ["./outlook-mcp.js"] },
},
},
tools: {
sandbox: {
tools: {
alsoAllow: ["web_search", "web_fetch", "memory_search", "memory_get", "bundle-mcp"],
},
},
},
}
If that sandbox-layer entry is missing, the MCP server can still load without error while its tools get filtered out before the provider request. Use openclaw doctor to detect this situation for OpenClaw-managed servers in mcp.servers. MCP servers loaded from bundled plugin manifests or Claude .mcp.json go through the same sandbox gate, but this diagnostic does not yet list those sources; if their tools disappear during sandboxed turns, apply the same allowlist entries.
tools.codeMode
The generic OpenClaw code-mode surface is controlled by tools.codeMode. When it is active for a run that has tools, the standard OpenClaw tools move behind the in-sandbox tools.* catalog bridge, and MCP tools become reachable through the generated MCP namespace. The model normally sees exec and wait; tools such as computer, whose structured results cannot pass through the JSON-only bridge, remain direct.
enabled comes set to "auto", which turns on code mode only for models whose catalog entry marks compat.codeMode: "preferred". See
Code Mode - automatic per-model activation.
To disable it for every run:
{
tools: {
codeMode: {
enabled: false,
},
},
}
The shorthand form works as well:
{
tools: { codeMode: false },
}
enabled: true switches code mode on for every run that has tools, no matter which model is used.
MCP declarations are surfaced in code mode through the read-only virtual API file interface. Before invoking MCP.<server>.<tool>(), guest code may call API.list("mcp") and API.read("mcp/<server>.d.ts") to examine TypeScript-style signatures. Refer to Code Mode for the runtime contract, limits, and debugging instructions.
tools.allow / tools.deny
Global tool allow/deny policy (deny takes precedence). Matching is case-insensitive and supports * wildcards. This applies even when the Docker sandbox is disabled.
{
tools: { deny: ["browser", "canvas"] },
}
write and apply_patch count as distinct tool identifiers. For compatible models, allow: ["write"] additionally activates apply_patch, yet deny: ["write"] does not block apply_patch. To prevent all file modification, deny group:fs or enumerate every mutating tool individually:
{
tools: { deny: ["write", "edit", "apply_patch"] },
}
Note
Setting both
allowandalsoAllowin the same scope (tools,tools.byProvider.<id>,agents.entries.*.tools) is rejected by config validation. Combine thealsoAllowentries intoallow, or removeallowand switch toprofileplusalsoAllow.
The image inspection tool goes by view_image. If an older config still lists image in an allow, alsoAllow, or deny list, execute openclaw doctor --fix to rewrite supported global, per-agent, provider, sandbox, sender, channel, and Gateway policy surfaces. Doctor keeps patterns like image* that could still match other tools and inserts view_image when the pattern no longer covers inspection. Patterns already matching both names, such as * or *image*, stay untouched.
tools.byProvider
Apply tighter restrictions per provider or model. Precedence runs: base profile, then provider profile, then allow/deny.
{
tools: {
profile: "coding",
byProvider: {
anthropic: { profile: "minimal" },
"openai/gpt-5.4": { allow: ["group:fs", "sessions_list"] },
},
},
}
tools.toolsBySender
Limits tools for whoever initiated the current turn. This acts as defense-in-depth layered over channel access control; sender values must originate from the channel adapter, never from message text. It does not authenticate other prompt content; see Requester-scoped controls and prompt context.
{
tools: {
toolsBySender: {
"channel:discord:1234567890123": { alsoAllow: ["group:fs"] },
"id:guest-user-id": { deny: ["group:runtime", "group:fs"] },
"*": { deny: ["exec", "process", "write", "edit", "apply_patch"] },
},
},
}
Keys require explicit prefixes: channel:<channelId>:<senderId>, id:<senderId>, e164:<phone>, username:<handle>, name:<displayName>, or "*". Channel ids are canonical OpenClaw ids; aliases like teams normalize to msteams. Legacy keys without prefixes are interpreted as id: only. Matching follows channel+id, id, e164, username, name, then wildcard.
When it matches, per-agent agents.entries.*.tools.toolsBySender supersedes the global sender match, even with an empty {} policy.
tools.elevated
Governs elevated exec access outside the sandbox:
{
tools: {
elevated: {
enabled: true,
allowFrom: {
whatsapp: ["+15555550123"],
discord: ["1234567890123", "987654321098765432"],
},
},
},
}
- Per-agent override (
agents.entries.*.tools.elevated) may only tighten restrictions. /elevated on|off|ask|fullkeeps state per session; inline directives apply to a single message.- Elevated
execskips sandboxing and relies on the configured escape path (gatewayby default, ornodewhen the exec target isnode).
tools.github
GitHub CLI identity is the default behavior. When tools.github is not provided, local agent tools, the Codex harness, and Agent Settings follow standard gh resolution: GH_TOKEN or GITHUB_TOKEN from the Gateway process takes precedence, followed by the runtime user's gh keyring/config. The Git author is derived from the selected agent's workspace.
The recommended path is Agents → Tools → GitHub Identity → Connect GitHub. OpenClaw shows a one-time user code plus a fixed link to https://github.com/login/device; you visit GitHub directly and grant repo, workflow, read:org, and gist. The last two belong to GitHub CLI's minimum classic-token contract. The Gateway manages the device code, token exchange, account verification, private managed gh profile, and rotating refresh token. None of those credentials enter browser responses, config, logs, command arguments, transcripts, or model environments.
OAuth access tokens last roughly eight hours. The Gateway refreshes them prior to expiry, validates the durable GitHub account ID, and atomically swaps the credential within the same private profile so already-running local tools keep using that identity. An expired or rejected refresh token surfaces as Reconnect required. Refresh never blocks Gateway startup.
Use a PAT instead keeps the fine-grained personal access token setup as an explicit fallback. The browser places the pasted token in the secret store as a one-use handoff. The Gateway hard-deletes that handoff before feeding its value to gh auth login on stdin. Both setup paths verify /user, publish an account-owned managed profile, default Git authorship to the account's canonical GitHub noreply identity, and store only secret-free config:
{
tools: {
github: {
profileId: "ghp_0123456789abcdef0123456789abcdef",
kind: "oauth",
gitAuthor: { name: "Automation User", email: "automation@example.com" },
},
},
agents: {
entries: {
reviewer: {
tools: {
github: {
profileId: "ghp_fedcba9876543210fedcba9876543210",
gitAuthor: { name: "Review Agent" },
},
},
},
},
},
}
Omitting agents.entries.<id>.tools.github inherits the system identity. An agent object is a complete managed override. Settings displays the effective identity and the selected configuration scope separately, so editing System never masquerades as an agent override. If a configured managed profile is missing or unusable, GitHub status reports configured_unavailable; it never falls back to the native profile.
Managed identity applies to the gh CLI/API account and optional Git author/committer metadata in local OpenClaw exec and the local Codex harness. OpenClaw provides a private GH_CONFIG_DIR, clears ambient GH_TOKEN and GITHUB_TOKEN precedence, and applies configured author fields through process-local environment and Git config overlays. It does not install a credential helper, rewrite SSH remotes, add HTTP authorization headers, or otherwise override an existing repository's Git network credentials. OAuth refresh keeps the same profile path and atomically replaces only its credential after verifying the durable account ID, so admitted local processes see the refreshed token on their next gh command. Choosing a different identity or inheritance target creates or selects another profile for new runs; existing processes keep their prior selected profile until they close. Retired profile files are cleaned on the next Gateway restart, so changing this setting is not immediate credential revocation.
Managed profiles supply execution and coordination identity; they are not an OS-user security sandbox. A process with unrestricted host execution under the same OS account can access account-owned files, including managed gh profiles. Use an OpenClaw sandbox, a dedicated host, or a dedicated OS user when adversarial isolation is required.
The profile is not forwarded to node hosts, OpenClaw sandboxes, remote-exec placements, or cloud workers; those environments remain credential-free. The github_publish tool instead records a bounded publication request. For cloud and remote-exec turns, the Gateway waits until the exact workspace result is reconciled and accepted, then commits remaining changes as the verified effective GitHub user, pushes the authoritative session branch through a one-shot HTTPS credential helper, and creates or reuses a draft pull request. The tool and worker payload contain no repository authority or credential.
Local session-owned worktrees can use the same Publish PR action in the Control UI. The Gateway derives the managed worktree, repository, branch, base, and head from current session ownership. It never accepts those authority facts from the browser or model. Publication retries use a durable request ID, an exact commit marker, remote branch observation, and pull-request lookup by head branch so a Gateway restart or lost response does not create duplicate commits, pushes, or pull requests.
Verification proves which account answered the GitHub API request. Status reports the credential kind, access expiry, refresh availability, OAuth scopes, and Git author while distinguishing missing credentials, unverified transport failures, and GitHub rate limiting without returning gh diagnostics. Repository-specific grants remain unknown until an exact repository operation succeeds; /user does not prove write access.
Removing an agent override or choosing native credentials deletes the associated local refresh record after the config change. Already-running local processes may retain the old profile and its current access token until they exit, restart, or the token expires, while new runs use the updated identity immediately. This local change does not revoke the authorization at GitHub; revoke it separately from the OAuth application's GitHub settings when required.
Control UI repository previews and project discovery use the separate optional gateway.controlUi.github.token service credential. They never consume an agent tool identity. When this SecretRef is explicit, OpenClaw excludes its exact environment or store name from agent execution. A custom name does not clear unrelated GH_TOKEN or GITHUB_TOKEN values used by native identity; a ref named GH_TOKEN or GITHUB_TOKEN excludes that exact variable.
tools.exec
{
tools: {
exec: {
backgroundMs: 10000,
timeoutSeconds: 1800,
cleanupMs: 1800000,
approvalRunningNoticeMs: 10000,
notifyOnExit: true,
notifyOnExitEmptySuccess: false,
commandHighlighting: false,
applyPatch: {
enabled: true,
allowModels: ["gpt-5.6-sol"],
},
},
},
}
Values shown are defaults except applyPatch.allowModels (empty/unset by default, meaning any compatible model may use apply_patch). approvalRunningNoticeMs emits a running notice when approval-backed exec runs long; 0 disables it.
tools.loopDetection
Tool-loop safety checks are disabled by default. Set enabled: true to activate detection. Settings can be defined globally in tools.loopDetection and overridden per-agent at agents.entries.*.tools.loopDetection.
{
tools: {
loopDetection: {
enabled: true,
},
},
}
tools.web
{
tools: {
web: {
search: {
enabled: true,
apiKey: "brave_api_key", // or BRAVE_API_KEY env (Brave provider)
maxResults: 5,
timeoutSeconds: 30,
cacheTtlMinutes: 15,
},
fetch: {
enabled: true,
provider: "firecrawl", // optional; omit for auto-detect
maxChars: 20000,
maxCharsCap: 20000,
maxResponseBytes: 750000,
timeoutSeconds: 30,
cacheTtlMinutes: 15,
maxRedirects: 3,
readability: true,
userAgent: "custom-ua",
},
},
},
}
Values shown are defaults except provider and userAgent. maxResponseBytes clamps to 32000, 10000000; maxChars clamps to maxCharsCap (raise maxCharsCap to allow larger responses).
tools.media
Configures inbound media understanding (image/audio/video):
{
tools: {
media: {
concurrency: 2,
models: [
{ provider: "openai", model: "gpt-4o-mini-transcribe", capabilities: ["audio"] },
{
type: "cli",
command: "whisper",
args: ["--model", "base", "{{AttachmentPath}}"],
capabilities: ["audio"],
},
{ provider: "ollama", model: "gemma4:26b", capabilities: ["image"] },
{ provider: "google", model: "gemini-3-flash-preview", capabilities: ["video"] },
],
audio: { enabled: true, preferredModel: "openai/gpt-4o-mini-transcribe" },
image: { enabled: true, preferredModel: "ollama/gemma4:26b" },
video: { enabled: true },
},
},
}
tools.media.models is the only configured model list. Every entry declares the capabilities it handles. The optional preferredModel selector accepts provider/model, a model id, provider:<id> for provider-default entries, or cli:command; matching entries move to the front of that capability's fallback order. Per-capability prompts, limits, request settings, scope, attachment policy, and audio transcript echo remain defaults for configured and auto-detected models; a model entry can override model-specific fields.
Media model entry fields
Provider entry (type: "provider" or omitted):
provider: the identifier for the API provider (openai,anthropic,google/gemini,groq, and others)model: overrides the model idprofile/preferredProfile: picks theauth-profiles.jsonprofile
CLI entry (type: "cli"):
command: which executable gets launchedargs: templated arguments (supports{{AttachmentPath}},{{AttachmentUrl}},{{AttachmentContentType}},{{AttachmentDir}},{{AttachmentIndex}},{{Prompt}},{{MaxChars}}, and more; deprecated{input}placeholders are converted to{{AttachmentPath}}byopenclaw doctor --fix). During the compatibility period, the older aliases{{MediaPath}},{{MediaUrl}},{{MediaType}}, and{{MediaDir}}still work but are marked as deprecated.
Common fields:
capabilities: a list holding one or more ofimage,audio, andvideo.prompt,maxChars,maxBytes,timeoutSeconds,language: overrides applied per entry.- When the agent invokes the explicit
view_imagetool, matching image modeltimeoutSecondsentries are also in effect. For image understanding, this timeout covers the request itself and is not shortened by any prior preparation. - On failure, the next entry is tried.
Provider authentication follows the standard sequence: auth-profiles.json → environment variables → models.providers.*.apiKey.
tools.agentToAgent
{
tools: {
agentToAgent: {
enabled: false,
allow: ["home", "work"],
},
},
}
tools.sessions
Determines which sessions the session tools (sessions_list, sessions_history, sessions_send) are allowed to act on.
Default: tree (the current session plus any it spawns, such as subagents; the main session can access every session belonging to the same agent).
{
tools: {
sessions: {
// "self" | "tree" | "agent" | "all"
visibility: "tree",
},
},
}
Visibility scopes
self: restricts visibility to the key of the current session alone.tree: covers the current session plus any sessions it has spawned, meaning subagents. If the caller happens to be the canonical main session, then list, history, search, send, and status all see every same-agent session.agent: includes any session tied to the current agent id, which could span other users when per-sender sessions share that agent id.all: applies to every session. Reaching across agents still depends ontools.agentToAgent.selfstays strict for main. Incognito refusal is never relaxed, and cross-agent access continues to demandallalong with thetools.agentToAgentpolicy.- Sandbox clamp: when the active session runs sandboxed and
agents.defaults.sandbox.sessionToolsVisibility="spawned"is in effect, which is the default, access stays confined to spawned sessions even if the caller is main ortools.sessions.visibility="all". - Without
all,sessions_listadds a shortvisibilityfield that spells out the effective mode and cautions that some sessions may fall outside the current scope.
Ambient group watches keep queuing activity notices and reporting to the main session where an event took place. They confer no access rights: the main session's same-agent reach is inherent to tree. In a multi-user deployment, session.dmScope: "main" shares that main session across all users; for isolation, pick a per-peer DM scope, or set tools.sessions.visibility: "self" to lock down access to the current session only.
tools.sessions_spawn
Controls whether inline attachments are supported for sessions_spawn.
{
tools: {
sessions_spawn: {
attachments: {
enabled: false, // opt-in: set true to allow inline file attachments
maxTotalBytes: 5242880, // 5 MB total across all files
maxFiles: 50,
maxFileBytes: 1048576, // 1 MB per file
retainOnSessionKeep: false, // keep attachments when cleanup="keep"
},
},
},
}
Attachment notes
- Attachments depend on
enabled: truebeing enabled. - Subagent attachments get materialized into the child workspace at
.openclaw/attachments/<uuid>/using a.manifest.json. - ACP attachments arrive as images only and pass inline to the ACP runtime once the same file count, per-file byte, and total byte limits are satisfied.
- Transcript persistence automatically strips attachment content.
- Base64 inputs go through strict alphabet and padding validation, plus a size check before decoding.
- Subagent attachment permissions are
0700for directories and0600for files. - Subagent cleanup follows the
cleanuppolicy:deletealways deletes attachments, whilekeepkeeps them only whenretainOnSessionKeep: trueholds.
tools.updatePlan
Kill switch for progress_card, the durable plan and status note that tracks multi-step work of any real complexity.
{
tools: {
updatePlan: false, // hide progress_card from every run
},
}
- Defaults to
trueacross every provider and model. To disable the tool, setfalse; no model-specific auto-enable rule exists. - The tool's description instructs the model to keep the plan current, limit itself to a single
in_progressstep, and add Markdown only when it carries information beyond the steps themselves. - Use
progress_cardin newtools.allowandtools.denypolicies. Existing policies that referenceupdate_plannow map toprogress_card, so shipped allowlists and denylists retain their original meaning.
Older configurations relied on tools.experimental.planTool. Running openclaw doctor --fix migrates the value over to tools.updatePlan.
agents.defaults.subagents
{
agents: {
defaults: {
subagents: {
allowAgents: ["research"],
model: "minimax/MiniMax-M2.7",
maxConcurrent: 8,
runTimeoutSeconds: 900,
announceTimeoutMs: 120000,
archiveAfterMinutes: 60,
},
},
},
}
model: the model used by default for sub-agents that get spawned. When it is absent, sub-agents take on the model of whoever called them.allowAgents: the default allowlist of configured target agent ids forsessions_spawnin cases where the requesting agent does not provide its ownsubagents.allowAgents(["*"]means any configured target; by default, only the same agent). Entries that are stale because their agent config was removed get rejected bysessions_spawnand are left out ofagents_list; runopenclaw doctor --fixto clear them.maxConcurrent: the maximum number of sub-agent runs happening at once. Default:8.runTimeoutSeconds: the timeout, in seconds, forsessions_spawnwhen the caller supplies no override of its own. Default:0(meaning no timeout); the900shown earlier is a common opt-in value, not the default that ships with the product.announceTimeoutMs: the per-call timeout, in milliseconds, for gatewayagentannounce delivery attempts. Default:120000. Because of transient retries, the total wait for an announce can stretch beyond any single configured timeout.archiveAfterMinutes: how many minutes after a sub-agent session finishes before it gets archived automatically. Default:60; setting0turns auto-archive off.- Per-subagent tool policy:
tools.subagents.tools.allow/tools.subagents.tools.deny.
Custom providers and base URLs
Provider plugins publish their own rows in the model catalog. To add custom providers, use models.providers in config or go through ~/.openclaw/agents/<agentId>/agent/models.json.
Setting up a custom or local provider baseUrl is also the narrow network trust decision for model HTTP requests: OpenClaw lets that exact scheme://host:port origin through the guarded fetch path, with no separate config option and no trust extended to other private origins.
{
models: {
mode: "merge", // merge (default) | replace
providers: {
"custom-proxy": {
baseUrl: "http://localhost:4000/v1",
apiKey: "LITELLM_KEY",
api: "openai-completions", // openai-completions | openai-responses | anthropic-messages | google-generative-ai | etc.
models: [
{
id: "llama-3.1-8b",
name: "Llama 3.1 8B",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
contextTokens: 96000,
maxTokens: 32000,
},
],
},
},
},
}
Auth and merge precedence
- For custom auth needs, go with
authHeader: true+headers. - Override the agent config root with
OPENCLAW_AGENT_DIR. - Merge precedence when provider IDs match:
- Non-empty agent
models.jsonbaseUrlvalues take precedence. - Non-empty agent
apiKeyvalues win only when that provider is not managed by SecretRef in the current config or auth-profile context. - Provider
apiKeyvalues managed by SecretRef get refreshed from source markers (ENV_VAR_NAMEfor env refs,secretref-managedfor file/exec/store refs) instead of having resolved secrets persisted. - Provider header values managed by SecretRef get refreshed from source markers (
secretref-env:ENV_VAR_NAMEfor env refs,secretref-managedfor file/exec/store refs). - When agent
apiKey/baseUrlare empty or missing, the fallback ismodels.providersin config. - For matching model
contextWindow/maxTokens: the explicit config value wins if it is present and valid (a positive finite number); otherwise the implicit or generated catalog value is used. - Matching model
contextTokensfollows the same explicit-wins-else-implicit rule; use it to cap effective context without touching native model metadata. - Provider-plugin catalogs get stored as generated, plugin-owned catalog shards under the agent's plugin state.
- Use
models.mode: "replace"when you want config to completely rewritemodels.jsonand skip merging in plugin-owned catalog shards. - Marker persistence is source-authoritative: markers are written from the active source config snapshot (pre-resolution), never from resolved runtime secret values.
- Non-empty agent
Provider field details
Top-level catalog
models.mode: provider catalog behavior (mergeorreplace).models.providers: a custom provider map keyed by provider id.- Safe edits: use
openclaw config set models.providers.<id> '<json>' --strict-json --mergeoropenclaw config set models.providers.<id>.models '<json-array>' --strict-json --mergefor additive updates.config setblocks destructive replacements unless you pass--replace.
- Safe edits: use
Provider connection and auth
models.providers.*.api: request adapter (openai-completions,openai-responses,openai-chatgpt-responses,anthropic-messages,google-generative-ai,google-vertex,github-copilot,bedrock-converse-stream,ollama,azure-openai-responses). For self-hosted/v1/chat/completionsbackends such as MLX, vLLM, SGLang, and most OpenAI-compatible local servers, useopenai-completions. A custom provider withbaseUrlbut noapidefaults toopenai-completions; setopenai-responsesonly when the backend supports/v1/responses.models.providers.*.apiKey: provider credential (prefer SecretRef/env substitution).models.providers.*.auth: auth strategy (api-key,token,oauth,aws-sdk).models.providers.*.maxTokens: default output-token cap for models under this provider when the model entry does not setmaxTokens.models.providers.*.timeoutSeconds: optional per-provider model HTTP request timeout in seconds, including connect, headers, body, and total request abort handling.models.providers.*.injectNumCtxForOpenAICompat: for Ollama +openai-completions, injectoptions.num_ctxinto requests (default:true).models.providers.*.authHeader: force credential transport in theAuthorizationheader when required.models.providers.*.baseUrl: upstream API base URL.models.providers.*.headers: extra static headers for proxy/tenant routing.
Request transport overrides
models.providers.*.request: transport overrides for model-provider HTTP requests.
request.headers: supplementary headers, merged with the provider's default set. Values can reference a SecretRef.request.auth: override for the authentication strategy. Available modes:"provider-default"(rely on the provider's built-in auth),"authorization-bearer"(combined withtoken), and"header"(usingheaderName,value, plus optionalprefix).request.proxy: override for the HTTP proxy. Modes:"env-proxy"(read fromHTTP_PROXY/HTTPS_PROXYenvironment variables) or"explicit-proxy"(withurl). An optionaltlssub-object is accepted in both modes.request.tls: TLS override for direct connections. Fields:ca,cert,key,passphrase(each supports SecretRef),serverName, andinsecureSkipVerify.request.allowPrivateNetwork: when set totrue, lets model-provider HTTP requests reach private, CGNAT, or comparable ranges despite the provider HTTP fetch guard. Custom or local provider base URLs already trust their exact configured origin, except metadata, link-local, and local-use NAT64 (64:ff9b:1::/48) origins, which stay blocked unless explicitly opted in. Usefalseto disable exact-origin trust. WebSocket applies the samerequestfor headers and TLS but does not use that fetch SSRF gate. Defaults tofalse.
Model catalog entries
models.providers.*.models: explicit entries for the provider's model catalog.models.providers.*.models.*.input: input modalities for the model. Choose["text"]for text-only models and["text", "image"]for native image or vision models. Image attachments enter agent turns only when the selected model is flagged as image-capable.models.providers.*.models.*.contextWindow: native context-window metadata for that model.models.providers.*.models.*.contextTokens: optional cap on active input for that model; use it when you need an effective budget different from the model's nativecontextWindow;openclaw models listdisplays both when they differ.
Custom provider capability declarations
Provider catalogs manage compat for bundled and catalog-known model routes. Do not duplicate those flags in config: OpenClaw uses the catalog row when the configured api and baseUrl still point to that route. openclaw doctor --fix removes matching legacy overrides and flags divergent values for review.
A compat block remains available for a truly custom provider, a custom model, or a catalog model sent to a different endpoint. Only set capabilities you have verified against that endpoint:
| Custom-route key | Runtime contract |
|---|---|
supportsStore | Handles the OpenAI store request field. |
supportsPromptCacheKey | Recognizes OpenAI prompt-cache and session-affinity keys. |
supportsDeveloperRole | Processes developer messages without mandating system. |
supportsReasoningEffort | Supports a reasoning-effort control. |
supportsTemperature | Accepts temperature for this model and adapter. |
supportsUsageInStreaming | Includes usage metadata in streaming responses. |
supportsTools | Enables structured tool/function calling. Tools can be disabled via false. |
supportsStrictMode | Handles strict tool schemas. |
requiresStringContent | Demands plain-string message content in Chat Completions. |
strictMessageKeys | Requires outgoing messages to carry only permitted keys. |
visibleReasoningDetailTypes | Identifies reasoning detail block types safe for transcript display. |
supportedReasoningEfforts | Enumerates the endpoint's accepted reasoning labels. |
reasoningEffortMap | Translates OpenClaw thinking labels into endpoint-specific ones. |
maxTokensField | Chooses between max_tokens and max_completion_tokens. |
thinkingFormat | Picks the endpoint's reasoning payload format. |
requiresToolResultName | Needs a tool name on tool-result messages. |
requiresAssistantAfterToolResult | Requires an assistant message following tool results. |
requiresThinkingAsText | Outputs reasoning as text instead of structured content. |
requiresReasoningContentOnAssistantMessages | Keeps DeepSeek-style reasoning_content intact during replay. |
toolSchemaProfile | Chooses a tool-schema normalization profile. Custom model entries acknowledge llamacpp and gemini. The llamacpp profile strips pattern and maxLength values of 2000 or more; built-in llama-cpp, ollama, and lmstudio providers apply this cleaner automatically. Custom provider IDs targeting llama-server must opt in explicitly. Refer to the llama.cpp example below. |
unsupportedToolSchemaKeywords | Drops named JSON Schema keywords the endpoint rejects before tool schemas are sent. Use it for endpoint-specific gaps beyond a profile's targeted changes. |
toolCallArgumentsEncoding | Chooses the endpoint's tool-call argument encoding. |
requiresOpenAiAnthropicToolPayload | Transforms OpenAI-shaped tool calls into Anthropic-family payloads. |
Amazon Bedrock discovery
plugins.entries.amazon-bedrock.config.discovery: Bedrock auto-discovery settings root.plugins.entries.amazon-bedrock.config.discovery.enabled: toggles implicit discovery.plugins.entries.amazon-bedrock.config.discovery.region: AWS region used for discovery.plugins.entries.amazon-bedrock.config.discovery.providerFilter: optional provider-id filter for focused discovery.plugins.entries.amazon-bedrock.config.discovery.refreshInterval: polling interval for discovery refresh.plugins.entries.amazon-bedrock.config.discovery.defaultContextWindow: fallback context window for discovered models.plugins.entries.amazon-bedrock.config.discovery.defaultMaxTokens: fallback max output tokens for discovered models.
Interactive custom-provider onboarding guesses image input for known vision-model-id patterns, such as GPT-4o/GPT-4.1/GPT-5+, the o1/o3/o4 reasoning families, Claude, Gemini, any id ending in -vl (Qwen-VL and similar), and named families like LLaVA, Pixtral, InternVL, Mllama, MiniCPM-V, and GLM-4V; it omits the extra question for known text-only families (Llama, DeepSeek, Mistral/Mixtral, Kimi/Moonshot, Codestral, Devstral, Phi, QwQ, CodeLlama, and bare Qwen ids without a vl/vision suffix). Unknown model IDs still trigger a prompt for image support. Non-interactive onboarding applies the same inference; use --custom-image-input to force image-capable metadata or --custom-text-input to force text-only metadata.
Provider examples
Cerebras (GLM 4.7 / GPT OSS)
The official external cerebras provider plugin can set this through openclaw onboard --auth-choice cerebras-api-key. Use explicit provider config only when overriding defaults.
{
env: { vars: { CEREBRAS_API_KEY: "sk-..." } },
agents: {
defaults: {
model: {
primary: "cerebras/zai-glm-4.7",
fallbacks: ["cerebras/gpt-oss-120b"],
},
models: {
"cerebras/zai-glm-4.7": { alias: "GLM 4.7 (Cerebras)" },
"cerebras/gpt-oss-120b": { alias: "GPT OSS 120B (Cerebras)" },
},
},
},
models: {
mode: "merge",
providers: {
cerebras: {
baseUrl: "https://api.cerebras.ai/v1",
apiKey: "${CEREBRAS_API_KEY}",
api: "openai-completions",
models: [
{ id: "zai-glm-4.7", name: "GLM 4.7 (Cerebras)" },
{ id: "gpt-oss-120b", name: "GPT OSS 120B (Cerebras)" },
],
},
},
},
}
Use cerebras/zai-glm-4.7 for Cerebras; zai/glm-4.7 for Z.AI direct.
Kimi Coding
{
env: { vars: { KIMI_API_KEY: "sk-..." } },
agents: {
defaults: {
model: { primary: "kimi/kimi-for-coding" },
models: { "kimi/kimi-for-coding": { alias: "Kimi Code" } },
},
},
}
Built-in provider that works with Anthropic's API format. Quick alias: openclaw onboard --auth-choice kimi-code-api-key.
Local models (llama.cpp / llama-server)
The standard llama-cpp provider runs the llama.cpp schema cleaner in both managed and existing-server setups. To use a different provider ID with a remote llama-server (or any OpenAI-compatible llama.cpp endpoint), add compat.toolSchemaProfile: "llamacpp" to each model whose chat template turns tool arguments into GBNF. This profile strips pattern and maxLength values of 2000 or higher, which addresses the cron tool's trigger.script ceiling of 65536. It's a focused fix, not full support for every JSON Schema rule or minLength.
{
agents: {
defaults: {
model: { primary: "my-llamacpp/qwen35" },
},
},
models: {
mode: "merge",
providers: {
"my-llamacpp": {
baseUrl: "http://127.0.0.1:8080/v1",
apiKey: "llamacpp-no-key",
api: "openai-completions",
models: [
{
id: "qwen35",
name: "Qwen3.5 (llama-server)",
contextWindow: 8192,
maxTokens: 2048,
compat: {
supportsTools: true,
toolSchemaProfile: "llamacpp",
},
},
],
},
},
},
}
For older builds lacking toolSchemaProfile, the wider fallback is compat.unsupportedToolSchemaKeywords: ["pattern", "patternProperties", "format", "propertyNames", "uniqueItems", "contains", "minContains", "maxContains", "minLength", "maxLength"]. That approach removes every listed keyword without conditions, unlike the profile.
Local models (LM Studio)
Check Local Models for details. In short: run a big local model through LM Studio Responses API on capable hardware, and keep hosted models merged for backup.
MiniMax M3 (direct)
{
agents: {
defaults: {
model: { primary: "minimax/MiniMax-M3" },
models: {
"minimax/MiniMax-M3": { alias: "Minimax" },
},
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "${MINIMAX_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "MiniMax-M3",
name: "MiniMax M3",
reasoning: true,
input: ["text", "image"],
cost: { input: 0.6, output: 2.4, cacheRead: 0.12, cacheWrite: 0 },
contextWindow: 1000000,
maxTokens: 131072,
},
],
},
},
},
}
Configure MINIMAX_API_KEY. Use openclaw onboard --auth-choice minimax-global-api or openclaw onboard --auth-choice minimax-cn-api as shortcuts. The default model catalog points to M3 and also lists the M2.7 versions. On the Anthropic-compatible streaming route, OpenClaw turns off MiniMax M2.x thinking unless you set thinking manually; MiniMax-M3 (and M3.x) uses the provider's omitted/adaptive thinking path by default. /fast on or params.fastMode: true converts MiniMax-M2.7 into MiniMax-M2.7-highspeed.
Moonshot AI (Kimi)
{
env: { vars: { MOONSHOT_API_KEY: "sk-..." } },
agents: {
defaults: {
model: { primary: "moonshot/kimi-k2.6" },
models: { "moonshot/kimi-k2.6": { alias: "Kimi K2.6" } },
},
},
models: {
mode: "merge",
providers: {
moonshot: {
baseUrl: "https://api.moonshot.ai/v1",
apiKey: "${MOONSHOT_API_KEY}",
api: "openai-completions",
models: [
{
id: "kimi-k2.6",
name: "Kimi K2.6",
reasoning: false,
input: ["text", "image"],
cost: { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0 },
contextWindow: 262144,
maxTokens: 262144,
},
],
},
},
},
}
For the China endpoint: baseUrl: "https://api.moonshot.cn/v1" or openclaw onboard --auth-choice moonshot-api-key-cn.
Native Moonshot endpoints report streaming usage compatibility over the shared openai-completions transport, and OpenClaw decides based on endpoint features rather than the built-in provider ID alone.
OpenCode
{
agents: {
defaults: {
model: { primary: "opencode/claude-opus-4-6" },
models: { "opencode/claude-opus-4-6": { alias: "Opus" } },
},
},
}
Set OPENCODE_API_KEY (or OPENCODE_ZEN_API_KEY). Point to opencode/... refs for the Zen catalog or opencode-go/... refs for the Go catalog. Shortcut: openclaw onboard --auth-choice opencode-zen or openclaw onboard --auth-choice opencode-go.
Synthetic (Anthropic-compatible)
{
env: { vars: { SYNTHETIC_API_KEY: "sk-..." } },
agents: {
defaults: {
model: { primary: "synthetic/hf:MiniMaxAI/MiniMax-M3" },
models: { "synthetic/hf:MiniMaxAI/MiniMax-M3": { alias: "MiniMax M3" } },
},
},
models: {
mode: "merge",
providers: {
synthetic: {
baseUrl: "https://api.synthetic.new/anthropic",
apiKey: "${SYNTHETIC_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "hf:MiniMaxAI/MiniMax-M3",
name: "MiniMax M3",
reasoning: true,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 262144,
maxTokens: 65536,
},
],
},
},
},
}
Leave /v1 out of the base URL (the Anthropic client adds it). Shortcut: openclaw onboard --auth-choice synthetic-api-key.
Z.AI (GLM-4.7)
{
agents: {
defaults: {
model: { primary: "zai/glm-4.7" },
models: { "zai/glm-4.7": {} },
},
},
}
Configure ZAI_API_KEY. Model refs use the standard zai/* provider ID. Shortcut: openclaw onboard --auth-choice zai-api-key.
- General endpoint:
https://api.z.ai/api/paas/v4 - Coding endpoint:
https://api.z.ai/api/coding/paas/v4 - The default
zai-api-keyauth mode tests your key and figures out which endpoint it matches (prompting you, with Global as the fallback, when detection fails). You can also pick CN or Coding-Plan auth explicitly. - For the general endpoint, create a custom provider using the base URL override.
Related
- Configuration, agents
- Configuration, channels
- Configuration reference, other top-level keys
- Tools and plugins