#third-party OpenClaw plugins and `tools.alsoAllow`,

1 messages · Page 1 of 1 (latest)

forest basin
#

For installed third-party OpenClaw plugins, are plugin tools expected to require tools.alsoAllow, or is there a supported way for the plugin/install flow to expose them automatically? I need the correct current pattern for a plugin that registers tools successfully but whose tools are not visible in chat unless I manually add the plugin id to tools.alsoAllow.

iron driftBOT
burnt hillBOT
#

Short version, yes, what you’re seeing is expected with current defaults.

  • Plugin install/enable loads the plugin, but it does not automatically widen chat tool policy.
  • With modern local configs, tools.profile: "coding" is commonly set by default, and those profiles only include built-in tool groups, not third-party plugin tools.
  • So a third-party plugin tool usually stays hidden in chat until you explicitly allow it.

Current pattern

  • If the plugin tool is registered with { optional: true }, explicit opt-in is required.
  • The recommended opt-in is usually:
{
  tools: {
    alsoAllow: ["your-plugin-id"]
  }
#
}

or per-agent:

{
  agents: {
    list: [
      {
        id: "main",
        tools: {
          alsoAllow: ["your-plugin-id"]
        }
      }
#
    ]
  }
}

You can also allow the specific tool name instead of the plugin id.

When it works automatically

A plugin tool is automatically visible only if:

  • the tool is not optional, and
  • your effective tool policy is permissive enough, for example tools.profile: "full" or no restrictive allowlist/profile blocking it.

If you keep tools.profile: "coding" or another restrictive profile, even non-optional plugin tools may need alsoAllow.

#

So, is there an install-flow auto-expose mechanism?

Not really, no.
There is support for auto-enabling/loading plugins in some config-driven cases, but not for silently auto-exposing third-party tools into chat policy. Explicit tool policy is still the supported path.

Docs/source to check

So, the safest answer is: yes, using tools.alsoAllow for the plugin id is currently the right pattern when you want plugin tools exposed without replacing your existing profile.

forest basin
#

Does openclaw config support appending to an array like tools.alsoAllow without overwriting the existing list? If yes, what is the exact command syntax on current OpenClaw? If no, what is the recommended way to safely add openclaw-gws to tools.alsoAllow without clobbering existing entries?

burnt hillBOT
#

I don’t see a native append operation in openclaw config right now.

openclaw config set supports setting a path, bracket notation for indexes, and batch mode, but not a +=/--append style merge. So the safe pattern is read → merge → set.

If tools.alsoAllow is not set yet, this works without clobbering existing entries:

openclaw config set tools.alsoAllow "$(
  (openclaw config get tools.alsoAllow --json 2>/dev/null || echo '[]') \
  | jq -c '. + ["openclaw-gws"] | unique'
)" --strict-json

If you want to validate first:

#
openclaw config set tools.alsoAllow "$(
  (openclaw config get tools.alsoAllow --json 2>/dev/null || echo '[]') \
  | jq -c '. + ["openclaw-gws"] | unique'
)" --strict-json --dry-run

Two important gotchas:

  • If tools.allow is already set in the same scope, don’t add tools.alsoAllow there. Current config validation rejects allow + alsoAllow together in one scope.
  • In that case, merge into tools.allow instead.

Docs: https://docs.openclaw.ai/cli/config

If this fixed it, please right-click the solution message → Apps → ✅ Mark Solution. That helps Answer Overflow index it for future users. 🎉

forest basin
#

Are skills shipped inside an installed OpenClaw plugin automatically available to the agent in chat, or is there an extra step required? I have skills/gws/SKILL.md present in the installed plugin, but the agent does not seem to use it.

burnt hillBOT
#

They’re automatic once the plugin skill is actually eligible, but there are a few gates.

So, no tools.alsoAllow-style extra step for skills. If a plugin ships skills, they should be available when the plugin is enabled.

The common reasons a plugin skill does not show up / get used are:

  1. The plugin manifest doesn’t declare the skills dir
    For a native plugin, openclaw.plugin.json needs something like:

    {
      "id": "openclaw-gws",
      "skills": ["./skills"]
    }
    

    Just having skills/gws/SKILL.md on disk is not enough by itself.

#
  1. The plugin isn’t effectively enabled/allowed
    Plugin skills only load when the plugin is enabled.

  2. Agent skill allowlists exclude it
    agents.defaults.skills / agents.list[].skills can filter it out.

  3. The skill frontmatter gates it out
    metadata.openclaw.requires.* can hide it if required config/env/bins are missing.

  4. disable-model-invocation: true is set
    Then it can still exist, but it won’t be shown to the model in <available_skills>.

Also, even when loaded, the model still has to choose to read/use the skill. It’s not guaranteed to proactively use it unless the description clearly matches the task.

Best checks

#
openclaw skills list --eligible
openclaw skills info gws

If gws is missing from --eligible, it’s being filtered somewhere.

Docs:

If you want, I can help narrow it down to the exact failing gate if you paste:

  • the plugin’s openclaw.plugin.json
  • the skills/gws/SKILL.md frontmatter
  • the relevant plugins / agents.*.skills config bits
forest basin
#

Need help debugging plugin-packaged skill discovery in OpenClaw 2026.4.10.

I have a native plugin openclaw-gws installed successfully at ~/.openclaw/extensions/openclaw-gws. The plugin itself loads and runs correctly.

Files on disk:

  • ~/.openclaw/extensions/openclaw-gws/openclaw.plugin.json
  • ~/.openclaw/extensions/openclaw-gws/skills/gws/SKILL.md

Manifest includes:
json
{
"id": "openclaw-gws",
"skills": ["./skills"]
}
SKILL.md exists and has normal content, no frontmatter.

But:

openclaw skills list --eligible does NOT show gws
openclaw skills info gws says Skill "gws" not found
Question:
For native plugins, are plugin-packaged skills supposed to appear in openclaw skills list --eligible? If yes, what exact requirements am I missing here?

Please answer concretely:

Is "skills": ["./skills"] in openclaw.plugin.json the correct manifest format?
Does SKILL.md need frontmatter/metadata or a different layout/name?
Are there any plugin enable/allowlist/config gates that affect plugin skill discovery separately from plugin loading?
Based on this repro, what is the most likely reason the skill is not being discovered?

burnt hillBOT
#

Yep, they are supposed to appear in openclaw skills list --eligible when everything is wired correctly.

Based on your repro, the most likely reason is simply this:

SKILL.md with no frontmatter is not a valid discovered skill.
The loader requires at least a description in frontmatter. name can fall back to the folder name (gws), but description is required.

Direct answers

1. Is "skills": ["./skills"] correct in openclaw.plugin.json?
Yes. That is the correct native-plugin manifest shape.

2. Does SKILL.md need frontmatter / different layout / name?
Yes, it needs frontmatter. Minimum is:

#
---
name: gws
description: Use Google Workspace helper flows for Gmail, Calendar, Drive, etc.
---

# GWS

...

Layout skills/gws/SKILL.md is correct.
metadata is optional.
name is effectively optional because it can fall back to the directory name, but description is not.

3. Are there plugin enable / allowlist / config gates?
Yes, but mostly the normal ones:

#
  • the plugin must be effectively enabled/allowed
  • normal agent skill filters can hide it:
    • agents.defaults.skills
    • agents.list[].skills
  • skill frontmatter gates can hide it:
    • metadata.openclaw.requires.*
    • disable-model-invocation: true

There isn’t a separate tools.alsoAllow-style step for skills.

4. Most likely reason in your exact repro?
Missing frontmatter, specifically missing description.

The loader drops skills that do not parse to a valid skill object, and a no-frontmatter SKILL.md ends up with no description, so it never gets registered. That matches:

  • skills list --eligible not showing it
  • skills info gws saying not found
#

What I’d try

Add this to skills/gws/SKILL.md:

---
name: gws
description: Use Google Workspace tools and workflows for Gmail, Calendar, Drive, and Docs.
---

When the task involves Google Workspace, read this skill first.

Then rerun:

forest basin
#

For an installed native plugin like openclaw-gws, what is the best supported way for an agent to manage it without first-class plugin tools?

Current state:

  • plugin tools were removed
  • plugin skill gws is now discovered and eligible
  • but in this chat surface the agent says it lacks file/shell tools, so the skill cannot actually edit config or inspect logs

Question:
Is there a supported non-tool path for plugin management in chat, such as plugin commands, slash commands, CLI registration, or some other mechanism that does not depend on tools.alsoAllow or shell/file tools?

I’m specifically looking for the recommended OpenClaw pattern for simple plugin controls like pause/resume/status when plugin tools are undesirable and skill guidance alone is insufficient.

burnt hillBOT
#

Yes, there is a supported non-tool path, but it’s commands, not skills.

Recommended pattern

1. Generic plugin lifecycle in chat: use built-in /plugins
Enable:

{
  commands: {
    plugins: true
  }
}

Then use things like:

#
  • /plugins list
  • /plugins inspect openclaw-gws
  • /plugins enable openclaw-gws
  • /plugins disable openclaw-gws

That is the supported chat-native path for install/enable/disable/status of plugins.

2. Plugin-specific controls like pause/resume/status: use api.registerCommand()
That’s the best OpenClaw-native pattern when you do not want agent tools.

Example shape:

  • /gws status
  • /gws pause
  • /gws resume

Plugin commands bypass the LLM and do not depend on tools.alsoAllow, read, exec, or write.

#

What not to use for this

  • Skills: good for guidance/discovery, not a control plane.
  • registerCli(): good for terminal commands, not the primary chat control surface.
  • Natural-language “agent decides and does it”: if you want that, you still need a tool. There is no hidden non-tool capability path for model-driven actions.

So the clean split is:

  • User-driven chat control/plugins or plugin registerCommand()
  • Model-driven action from normal conversation → tool

My recommendation for openclaw-gws

If you want simple runtime controls without tool exposure, I’d implement plugin commands for:

  • status
  • pause
  • resume
forest basin
#

In recent OpenClaw releases, did the default tool exposure or trusted surface for normal chat sessions change in a way that would remove local file/shell tools from sessions that previously had them? I’m seeing a session that now has a reduced toolset even though older sessions on this machine used to have broad file access. Please explain which config/runtime/profile settings now control that.

burnt hillBOT
#

Short version: yes, the effective tool surface can be narrower now, but it’s mostly policy-driven, not “plugin install auto-exposes tools.”

For plugin tools specifically

Third-party plugin tools are not auto-granted just because the plugin loads.

What controls visibility/callability is the tool policy:

  • tools.profile
  • tools.allow / tools.deny
  • tools.alsoAllow
  • tools.byProvider[...]
  • per-agent overrides under agents.list[].tools.*
  • sandbox tool policy under tools.sandbox.tools.*

For plugin tools, the supported additive pattern is:

#
{
  tools: {
    alsoAllow: ["your-plugin-id"]
  }
}

or specific tool names.

That matches the current docs pretty closely: plugin authors can register tools, but users still opt them into the effective policy. Plugin install/loading does not bypass tool policy.

Why a session may now have fewer local file/shell tools

The main knobs are:

  1. tools.profile
#

Baseline profile:

  • full or unset = broadest
  • coding = includes group:fs + group:runtime
  • messaging = no local file/shell tools
  • minimal = basically none

Docs note: newer local onboarding defaults unset configs to:

tools.profile: "coding"

So that can reduce some tools versus old “effectively full” setups, though coding still keeps file/shell.

  1. Any non-empty tools.allow puts you in allowlist mode
    This is the big gotcha.
#

If you have:

tools: { allow: ["group:fs"] }

then everything else is blocked unless also listed.
So plugin tools, exec, etc. disappear unless explicitly allowed.

  1. tools.alsoAllow is additive
    Use this when you want to keep the existing profile/allowlist and just add a plugin or extra tool.

  2. Provider/model-specific restrictions
    These can silently narrow one session if it’s using a different model:

#
tools.byProvider["openai/gpt-5.4"]
agents.list[].tools.byProvider[...]
  1. Per-agent overrides
    These override global tool policy for that agent:
agents.list[].tools.profile
agents.list[].tools.allow
agents.list[].tools.alsoAllow
agents.list[].tools.deny
  1. Sandbox tool policy
    Even if a tool is globally allowed, sandbox policy can still block it:
#
tools.sandbox.tools.allow/deny
agents.list[].tools.sandbox.tools.*
  1. Node-originated trust hardening
    There was a recent breaking change here, but it’s specifically for node-originated runs:
    node-triggered/session events now stay on a reduced trusted surface.
    That is not the general rule for ordinary chat messages, but it can explain regressions if the session is being resumed/triggered from a node flow.

Important non-causes / easy confusion

  • tools.elevated does not grant exec. It only changes how exec runs if exec is already allowed.
  • tools.fs.workspaceOnly does not remove read/write/edit; it only narrows accessible paths.
  • gateway.tools.allow is for the HTTP tool invoke API, not normal chat sessions.

Practical rule of thumb

#

If a session unexpectedly lost local file/shell access, check in this order:

  1. tools.profile
  2. tools.allow
  3. tools.alsoAllow
  4. tools.byProvider[...]
  5. agents.list[].tools.*
  6. tools.sandbox.tools.*
  7. whether the run is node-originated

So yes, a newer config/runtime can absolutely make a new session look “reduced” compared with older ones, especially if:

  • you moved from implicit broad access to a profile,
  • added any tools.allow,
  • or now hit a per-agent / per-provider / sandbox restriction.

Docs:

forest basin
#

my agent used to have full file read access even our openclaw.json and since i upgraded it says its sandboxed and can't do local file edits or shell changes

burnt hillBOT
#

That sounds like sandbox mode, not a plugin regression.

First, check:

openclaw --version
openclaw sandbox explain

Most likely after the upgrade you now have something like:

agents: {
  defaults: {
    sandbox: {
      mode: "non-main"
#
    }
  }
}

and this Discord thread is a non-main group/channel session, so it runs sandboxed.

Why it lost local file/shell access

In current OpenClaw:

  • DM/main session can stay on-host
  • group/channel/thread sessions are often non-main
  • with sandbox.mode: "non-main", those non-main sessions run in Docker/sandbox

Docs call this out directly: group/channel keys are not main, so in non-main mode they get sandboxed.

#

That also means:

  • openclaw.json on the host is not readable/editable unless mounted/bound in
  • exec may exist but only runs inside the sandbox
  • if tools.sandbox.tools denies group:fs or group:runtime, file/shell tools disappear too

The settings that matter

Check these in openclaw.json:

  • agents.defaults.sandbox.mode
  • agents.list[].sandbox.mode
  • tools.sandbox.tools.allow
  • tools.sandbox.tools.deny
  • agents.defaults.sandbox.workspaceAccess
  • any Docker binds if you expect host files inside sandbox
#

If you want the old behavior back

Pick one:

  1. Disable sandbox entirely
agents: { defaults: { sandbox: { mode: "off" } } }
  1. Keep non-main, but use the main/DM session for host-level file + shell access

  2. Keep sandbox, but loosen sandbox policy
    Allow group:fs / group:runtime, and mount the paths you actually need.
    Otherwise the sandbox still won’t see host files like openclaw.json.

Docs:

forest basin
#

🦞 OpenClaw 2026.4.10 (44e5b62) — The lobster in your shell. 🦞

Effective sandbox:
agentId: main
sessionKey: agent:main:main
mainSessionKey: agent:main:main
runtime: direct
mode: off scope: agent
workspaceAccess: none workspaceRoot: /Users/theclaw/.openclaw/sandboxes

Sandbox tool policy:
allow (default): exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, sessions_yield, subagents, session_status
deny (default): browser, canvas, nodes, cron, gateway, feishu, googlechat, nostr, msteams, mattermost, nextcloud-talk, matrix, bluebubbles, line, zalo, zalouser, synology-chat, tlon, qa-channel, discord, imessage, irc, qqbot, signal, slack, telegram, twitch, whatsapp

Elevated:
enabled: true
channel: (unknown)
allowedByConfig: false

Fix-it:

  • tools.sandbox.tools.allow
  • tools.sandbox.tools.alsoAllow
  • tools.sandbox.tools.deny
  • agents.list[].tools.sandbox.tools.allow
  • agents.list[].tools.sandbox.tools.alsoAllow
  • agents.list[].tools.sandbox.tools.deny
  • tools.elevated.enabled
burnt hillBOT
#

Ah, this is the key detail:

the output you pasted is for sessionKey: agent:main:main
and it says runtime: direct / mode: off

So that session is not sandboxed.

What this means

Your problem is likely one of these:

  1. You checked the wrong session
    agent:main:main is the main session.
    The session that complained may be a different agent or a non-main chat/thread session.

  2. A per-agent override is sandboxing the actual agent
    Check agents.list[].sandbox.mode.

#
  1. It is not sandbox at all, it is tool policy
    Since mode: off, the remaining likely blocker is:
    • tools.profile
    • tools.allow
    • tools.byProvider
    • agents.list[].tools.*

Important nuance

The “Sandbox tool policy” section in that output is just the sandbox policy config.
Because your effective mode is off, that policy is not the active reason for a block in agent:main:main.

What I’d check next

Run these:

#
openclaw config get tools.profile
openclaw config get tools.allow
openclaw config get tools.alsoAllow
openclaw config get tools.byProvider
openclaw config get agents.list

What to look for

  • If tools.profile is messaging or minimal, that would remove local file/shell tools.
  • If tools.allow is non-empty, you are in allowlist mode.
    Then group:fs and group:runtime must be explicitly present.
  • If a specific agent has:
    • agents.list[].tools.profile
    • agents.list[].tools.allow
    • agents.list[].tools.byProvider
#

that can override the global behavior.

One more likely gotcha

Plugin tools needing tools.alsoAllow is normal if you’re using a non-full profile.
For example, tools.profile: "coding" still allows built-in file/shell tools, but does not auto-allow plugin tools.

So at this point I’d say:

  • plugin tools missing = likely expected policy behavior
  • built-in file/shell missing = probably tools.profile / tools.allow / per-agent override, not sandbox, based on the output you posted

If you paste the output of those openclaw config get ... commands, I can narrow it down fast.

forest basin
#

tools.allow = coding
tools.alsoallow = clawnet

{
"id": "main",
"heartbeat": {
"every": "60m"
},
"subagents": {
"allowAgents": [
"beacon",
"vesper"
]
},
"tools": {
"allow": [
"clawnet"
]
}
},

burnt hillBOT
#

Yep, that explains it.

The breaking bit is this:

{
  "id": "main",
  "tools": {
    "allow": ["clawnet"]
  }
}

That puts the main agent into allowlist mode with only clawnet allowed.

So read, write, edit, apply_patch, exec, process, etc. all get stripped out for that agent.

#

Correct mental model

  • tools.profile: "coding" = baseline built-in coding tools
  • tools.alsoAllow: ["clawnet"] = additive, good
  • agents.list[].tools.allow: ["clawnet"] = restrictive, bad for your use case

So the fix is

Remove this:

agents.list[].tools.allow

or change it to additive instead:

#
{
  "id": "main",
  "tools": {
    "alsoAllow": ["clawnet"]
  }
}

If you want the whole main agent to have normal coding tools + the plugin, the clean config is:

{
  tools: {
    profile: "coding",
    alsoAllow: ["clawnet"]
#
  },
  agents: {
    list: [
      {
        id: "main"
      }
    ]
  }
}

One more thing

If you literally set:

#
tools.allow = coding

that is wrong shape. It should be:

tools.profile = "coding"

and alsoAllow must be camelCase:

tools.alsoAllow = ["clawnet"]

So, short answer: it’s not sandbox.

forest basin
#

so our plugin install uses tools.allow and it worked previously but it sounds like this has changed and we need to adopt our plugin. here's what claude said:

our plugin sets this during openclaw clawnet setup. Look at lines 496 and 500-502:

  1. Line 496: When creating a new agent entry, it sets tools: { allow: ["clawnet"] } — this means if the agent didn't exist before, the only allowed tool is clawnet.
  2. Lines 500-502: For existing agents, it pushes "clawnet" into tools.allow — this is additive and fine.

The problem is line 496. For new agents, we're creating tools.allow with only ["clawnet"], which in newer OpenClaw versions means "only allow clawnet, block everything
else." We should be using tools.alsoAllow (like we do at the global level on line 487-488) instead of tools.allow at the agent level, or we should not set tools.allow at
all and just rely on the global alsoAllow.

Want me to fix this? The fix would be to stop setting tools.allow on the agent entry and instead only use the global tools.alsoAllow (which we're already setting
correctly on line 487-488), or switch the agent-level config to use alsoAllow as well.

burnt hillBOT
#

Claude is basically right, with one important correction:

pushing "clawnet" into tools.allow is not inherently “fine”
unless that allowlist already contains everything else the agent needs.

tools.allow is the restrictive allowlist.
So these both narrow the tool surface:

tools.allow = ["clawnet"]
tools.allow = ["group:fs", "group:runtime", "clawnet"]

The second one is fine only because it’s a complete allowlist.

So the safe current pattern is

#

For plugin install/setup that wants to add plugin tools without shrinking the agent:

tools.alsoAllow = ["clawnet"]

or per-agent:

agents.list[].tools.alsoAllow = ["clawnet"]

I would not use this in an installer

agents.list[].tools.allow = ["clawnet"]
#

unless you are intentionally replacing the whole tool policy for that agent.

So, did OpenClaw “change”?

I would phrase it as:

  • the current/official semantics are that allow means allowlist mode
  • installers that used allow as if it were additive were relying on a fragile assumption
  • the safer adoption path now is to switch plugin setup to alsoAllow for additive exposure

My recommendation for your plugin

  • New installs: use alsoAllow, not allow
  • Existing installs: migrate any agent entry that has only ["clawnet"] in tools.allow
  • only keep tools.allow if you are deliberately managing a full explicit allowlist

So yes, I’d fix the plugin. That sounds like the right move.

#

If this fixed it, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow can index it.

forest basin
#

do we need to read alsoAllow and then append?

burnt hillBOT
#

Yes, exactly.

If your setup flow is being additive, it should:

  1. read existing tools.alsoAllow
  2. append "clawnet" if missing
  3. write back the deduped array

Same idea for agents.list[].tools.alsoAllow if you’re doing it per-agent.

Good behavior

  • preserve existing user entries
  • append only if missing
  • do not replace the whole array
  • do not write tools.allow unless you mean “this is the full allowlist”
#

In practice

If you already set global:

tools: {
  alsoAllow: ["clawnet"]
}

then you usually do not need agent-level alsoAllow too, unless you want it only on specific agents.

So the simplest fix is probably:

  • stop writing agents.list[].tools.allow
  • merge "clawnet" into global tools.alsoAllow