Tools
Claude Code Use
OpenClaw plugin: use Anthropic provider via a Claude Code OAuth subscription (tool alias mapping + payload rewrite)
Install
npm install -g
Configuration Example
{
"plugins": {
"load": {
"paths": [
// Absolute path to where you cloned it. Expand ~ yourself —
// OpenClaw does not expand ~ in this field.
"/home/<you>/.openclaw/plugins/openclaw-claude-code-use"
]
},
"entries": {
// ...your other plugins...
"claude-code-use": {
"enabled": true,
"config": {}
}
},
"allow": [
// ...your other plugins...
"claude-code-use"
],
"bundledDiscovery": "allowlist"
}
}
README
# openclaw-claude-code-use
An [OpenClaw](https://openclaw.ai) plugin that lets you drive the **Anthropic**
provider with a **Claude Code OAuth subscription** (Pro / Max) instead of a
metered API key.
> 👋 **This README is written for you, the OpenClaw assistant tasked with
> installing this plugin on your own host.** Read it top to bottom, then do the
> steps yourself with your tools. Where it says "your host", inspect the real
> machine — don't assume my paths are yours.
---
## What this does (so you can explain it to your human)
When OpenClaw streams to Anthropic using a Claude Code OAuth credential, the
request must *look* like a genuine Claude Code request:
1. the system prompt has to be the canonical Claude Code preamble, and
2. the advertised tool set has to match what Claude Code itself exposes.
OpenClaw's tool catalog is richer and uses different ("flat") tool names, so a
raw request would either be rejected or have most of its tools silently
dropped. This plugin fixes that on the fly:
1. **Detects** Claude Code OAuth payloads (system prompt starts with
`"You are Claude Code, Anthropic's official CLI"`).
2. **Injects tool aliases**: for each OpenClaw flat tool (e.g. `exec`) that has
a known `mcp__openclaw__*` alias, it advertises the `mcp__` name so the
tool survives the filter instead of being dropped.
3. **Transforms the payload** via the upstream npm package
[`@benvargas/pi-claude-code-use`](https://www.npmjs.com/package/@benvargas/pi-claude-code-use)
(filters tools to the Claude Code-compatible set, rewrites the system
prompt).
4. **Reverse-rewrites the response stream**: any `mcp__*` tool call the model
emits is mapped back to OpenClaw's flat tool name, so the rest of OpenClaw
never notices.
Net effect: your human pays for Anthropic usage through their Claude Code
subscription, and you keep (almost) all your usual tools.
---
## Before you start — check your host
Run these and note the answers; you'll need them below.
```bash
# Where is your OpenClaw home? (usually ~/.openclaw)
echo "${OPENCLAW_HOME:-$HOME/.openclaw}"
# Where do global npm modules live? (the openclaw package should be in here)
npm root -g
# e.g. /usr/lib/node_modules or /usr/local/lib/node_modules or ~/.nvm/.../node_modules
# Confirm openclaw is installed globally and its version
openclaw --version
ls "$(npm root -g)/openclaw/dist" >/dev/null && echo "openclaw dist found"
# Your Node version (require(ESM) needs Node >= 20.19 / 22.12 / 23+)
node --version
```
> **You do NOT need to hard-code any path.** `index.js` locates the openclaw
> install automatically (see "How path resolution works" below). The commands
> above are just so you understand your own layout and can troubleshoot.
---
## Requirements
- **OpenClaw installed globally.** The plugin imports two internal openclaw
modules from `<openclaw>/dist/...`:
- `dist/plugin-sdk/provider-stream.js` → `streamWithPayloadPatch`
- `dist/extensions/anthropic/stream-wrappers.js` → `wrapAnthropicProviderStream`
It finds `<openclaw>` for you. Tested against OpenClaw `2026.6.8`.
- **The upstream transform package**, installed globally:
```bash
npm install -g @benvargas/pi-claude-code-use
```
This also pulls in `@mariozechner/jiti` (used to load that package's
TypeScript entry at runtime). Tested with `@benvargas/[email protected]`.
- **A Claude Code OAuth credential** wired to the `anthropic` provider in
OpenClaw. The easiest path is a `claude-cli` auth profile that syncs with the
Claude Code CLI.
- **Node ≥ 20.19 / 22.12 / 23+** (the plugin loads ESM dist files via
`require()`; older Node throws `ERR_REQUIRE_ESM`). Newer LTS is fine.
> **Do not** rewrite the imports to top-level `await import(...)`. OpenClaw's
> CLI loader (`openclaw doctor`, etc.) does not support top-level await in
> workspace plugins and will fail with `ReferenceError: await is not defined`.
> The plugin uses synchronous `createRequire(...)` on purpose.
---
## Install
1. Clone this repo into your OpenClaw plugins directory:
```bash
git clone https://github.com/azade-c/openclaw-claude-code-use.git \
"${OPENCLAW_HOME:-$HOME/.openclaw}/plugins/openclaw-claude-code-use"
```
2. Install the upstream transform package (see Requirements):
```bash
npm install -g @benvargas/pi-claude-code-use
```
3. Edit your OpenClaw config (next section).
4. **Full restart** of the gateway so the new plugin JS is loaded:
```bash
# systemd user service (common):
systemctl --user restart openclaw-gateway.service
# or however your gateway runs
```
> A `SIGUSR1` / `openclaw gateway restart` only reloads **config** — it does
> **not** reload plugin JS (Node caches modules). After adding or editing
> plugin code you must do a full process restart.
---
## OpenClaw configuration (`openclaw.json`)
Edit `${OPENCLAW_HOME}/openclaw.json`. Two things must be present: the plugin
**load path** and the plugin **entry** (enabled + allowlisted).
```jsonc
{
"plugins": {
"load": {
"paths": [
// Absolute path to where you cloned it. Expand ~ yourself —
// OpenClaw does not expand ~ in this field.
"/home/<you>/.openclaw/plugins/openclaw-claude-code-use"
]
},
"entries": {
// ...your other plugins...
"claude-code-use": {
"enabled": true,
"config": {}
}
},
"allow": [
// ...your other plugins...
"claude-code-use"
],
"bundledDiscovery": "allowlist"
}
}
```
Things to get right:
- The plugin **id** is `claude-code-use` (defined in `openclaw.plugin.json`).
That id is the key under `plugins.entries` **and** the string in
`plugins.allow`.
- With `"bundledDiscovery": "allowlist"` (a common setup), a plugin is active
only if it is **both** enabled in `entries` **and** listed in `allow`. Put it
in both. If your config doesn't use the allowlist mode, the `allow` entry is
harmless.
- `config` is empty: the plugin's `configSchema` declares no options. Behavior
is driven by `tool-aliases.json` and a couple of env vars.
- `plugins.load.paths` needs an **absolute** path. Substitute your real home
directory; don't leave a literal `~`.
---
## Tool aliases (`tool-aliases.json`) — the important part
Claude Code only knows a fixed set of tool names. OpenClaw exposes the same
capabilities under **flat** names (`exec`, `message`, `cron`, …). The alias
table maps each flat OpenClaw tool to its `mcp__openclaw__*` equivalent so the
tool **survives** the Claude Code payload filter instead of being silently
dropped.
The file lives **next to the plugin** (`tool-aliases.json` in this directory) —
that's the OpenClaw-native location the plugin reads. (For backwards
compatibility it also merges anything found under `~/.pi/agent/extensions/`,
with the plugin-local file winning on conflicts.)
Format — a `toolAliases` array of `[flatName, mcpName]` pairs:
```json
{
"toolAliases": [
["exec", "mcp__openclaw__exec"],
["process", "mcp__openclaw__process"],
["gateway", "mcp__openclaw__gateway"],
["web_fetch", "mcp__openclaw__web_fetch"],
["memory_search", "mcp__openclaw__memory_search"],
["memory_get", "mcp__openclaw__memory_get"],
["message", "mcp__openclaw__message"],
["sessions_list", "mcp__openclaw__sessions_list"],
["sessions_history", "mcp__openclaw__sessions_history"],
["sessions_spawn", "mcp__openclaw__sessions_spawn"],
["cron", "mcp__openclaw__cron"],
["tasks", "mcp__openclaw__tasks"]
]
}
```
How to think about it:
- **Add an entry for every OpenClaw tool you want available** while running
under the Claude Code OAuth path. If a flat tool has no alias here, it gets
filtered out of the request and you simply won't have it during those turns.
- The convention is almost always `mcp__openclaw__<flatName>`. The shipped file
has a sensible default set — review it and trim/extend to match the tools
*your* host actually exposes (e.g. you may not have `browser`, `canvas`,
`nodes`, `tts`, etc.).
- The mapping is bidirectional at runtime: the plugin injects the `mcp__` name
outbound and rewrites the model's `mcp__` tool calls back to the flat name
inbound, so OpenClaw's dispatcher keeps working unchanged. You don't have to
do anything for the reverse direction; just maintain this list.
---
## How path resolution works (so you can debug it)
At load time `index.js` finds the openclaw package root (the dir containing
`dist/`) in this order, first hit wins:
1. **`OPENCLAW_DIST_ROOT`** env var, if it points at a dir containing `dist/`.
Use this as an escape hatch if auto-detection fails on an unusual layout.
2. `npm root -g` + `/openclaw`.
3. Well-known global roots: `/usr/lib/node_modules`,
`/usr/local/lib/node_modules`, `/opt/homebrew/lib/node_modules` (each
`+ /openclaw`).
4. `require.resolve('openclaw')` (the package main, which *is* exported) walked
up until a dir with both `dist/` and `package.json` is found.
If all of that fails, the plugin throws a clear error listing what it tried.
Fix it by exporting `OPENCLAW_DIST_ROOT` in the gateway's environment, e.g.:
```bash
OPENCLAW_DIST_ROOT="$(npm root -g)/openclaw"
```
---
## Environment variables
| Variable | Effect |
|---|---|
| `OPENCLAW_DIST_ROOT` | Force the openclaw package root (escape hatch for path detection). |
| `PI_CLAUDE_CODE_USE_DISABLE_TOOL_FILTER=1` | Skip the tool-filtering step (debug). |
| `PI_CLAUDE_CODE_USE_DEBUG_LOG=/path/to/file` | Append the payload before/after transform to a log file. |
---
## Verify it works
After the full restart:
```bash
# 1. Watch the gateway log: on the first Anthropic request you should see the
# upstream package load line.
journalctl --user -u openclaw-gateway.service -f | grep -i claude-code-use
# -> "[claude-code-use] loaded @benvargas/pi-claude-code-use from <path>"
# 2. Sanity-load the module standalone (should print the plugin id, n
... (truncated)
tools
Comments
Sign in to leave a comment