Connect OpenClaw
Use these steps with your own OpenClaw install — the one you installed from npm, not the Companion OpenClaw app from the Hub app store.
Nothing here requires a Companion build. The packaged app installs stock OpenClaw from npm; the only difference is that it arrives pre-wired.
Before you start
You need two API keys and the addresses of both servers.
<hub-url> and <memory-url> below are your two servers’ base URLs — everything before /api/mcp.
Neither has a port you can assume, so take both from the Hub dashboard if you are unsure:
Hub,
Memory.
OpenClaw can do this itself. Everything on this page — the MCP servers and
first-class memory — is also published as an agent-readable skill. Ask it to
set itself up from https://docs.ci.computer/connect/SKILL.md, or install
the skill so it is there next
time. You still supply the two keys. See Or let your agent do
it.
Add the servers
Add Companion Hub
Prefer the CLI. openclaw mcp add probes the server before saving, so a wrong URL or key fails
immediately instead of at first use:
openclaw mcp add ci-hub --url "<hub-url>/api/mcp" --transport streamable-http --header "Authorization=Bearer <hub-key>"Add Companion Memory
openclaw mcp add ci-memory --url "<memory-url>/api/mcp" --transport streamable-http --header "Authorization=Bearer <memory-key>"One line each, deliberately: a trailing \ continues a command in bash, but
in PowerShell and cmd it is just another argument — pasted there, the first
line runs without its --url and every later line fails as an unknown
command.
Confirm both registered
openclaw mcp probeThis reports each server with the number of tools it advertises. Read the counts, not just the
names — a server listed with 0 tools is connected and useless, and that is the failure this step
exists to catch.
The same underlying problem can also surface as a line beginning ! rather than a tool count, when
the server answers tools/list with -32601 Method not found instead of an empty list. Treat either
shape as the same finding.
Editing the config by hand
openclaw mcp add writes ~/.openclaw/openclaw.json. You can edit it directly instead:
{
"mcp": {
"servers": {
"ci-hub": {
"url": "<hub-url>/api/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer ${HUB_MCP_API_KEY}" }
},
"ci-memory": {
"url": "<memory-url>/api/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer ${CI_MEMORY_API_KEY}" }
}
}
}
}Prefer the ${ENV_VAR} form over pasting keys directly, so the credential
never lands in a config file that might be backed up or committed. The name
must be upper-case (A-Z, 0-9, _, not starting with a digit).
Export it everywhere OpenClaw runs, not just in your shell. Substitution
happens when the config is loaded, so a variable set in your terminal but not
in the service environment is unset at load time. OpenClaw does not fail
loudly: it logs missing env var "…" - feature using this value will be unavailable, leaves the literal ${VAR} in place, and starts normally. The
server then sends Authorization: Bearer ${CI_MEMORY_API_KEY} verbatim and
gets a 401 — so this arrives looking like a bad key rather than a missing
one. Check the startup log before you go replacing credentials.
Setting the variable where the gateway can see it
The ${VAR} syntax itself is platform-neutral — OpenClaw substitutes it when the config loads. What
differs is how you put the variable somewhere the gateway process inherits it.
macOS / Linux / WSL2
# This shell only:
export CI_MEMORY_API_KEY='<memory-key>'
# Persist it: add that line to ~/.zshrc, ~/.bashrc, or the systemd unit's
# Environment= / EnvironmentFile= if the gateway runs as a service.The transport field must be streamable-http. OpenClaw is the client that names this field
transport; Hermes omits it and Claude Code calls it type. Copying a working entry between clients
without translating that field is a common failure.
After a hand edit, a running OpenClaw has to be told to pick it up:
openclaw mcp reloadreload only affects the process it runs in. It disposes the cached MCP
runtimes held by that CLI invocation — which is the right command when
OpenClaw is embedded in the same process, and a no-op when it is not. If you
run OpenClaw as a service or a separate gateway, that process keeps its
old config and you must restart it — openclaw daemon restart. The command
reports success either way, because a fresh CLI process genuinely has nothing
cached to dispose.
openclaw mcp probe also runs as a separate process that reads the file and dials the server itself.
It reports success even when the running agent still uses the old config.
Verify
Ask your agent to list what it can do, or check directly:
openclaw mcp probeExpect both servers listed with a non-zero tool count — with a Hub key of any capability, tools such
as hub_list_installed_apps, and with a Memory key that has MCP and Intents, tools such as
memory_recall. If ci-memory shows zero, the key is missing the Intents scope — see
the scope trap.
openclaw mcp doctor checks the saved definitions for local setup problems without connecting, and
warns when a header holds a literal bearer token instead of an ${ENV_VAR} reference. Add --probe
to run the live connection check as well.
If both servers probe cleanly and the running agent still has no Companion tools, check its tool
profile. Embedded OpenClaw exposes configured MCP tools in the coding and messaging profiles, the
minimal profile hides them, and tools.deny: ["bundle-mcp"] turns them off explicitly.
Read the ! lines too. A server that errors during the probe — including one answering
-32601 Method not found, the other symptom of a missing Intents scope — is reported as a
diagnostic rather than listed with a count, so scanning only for 0 tools misses it.
Removing a server
Revoking a key does not remove the entry that used it, and a stale entry fails on every start:
openclaw mcp unset ci-memoryDo this whenever you rotate or revoke a key, or the client keeps trying the old credential.
OPENCLAW_DATA_DIR does not isolate openclaw mcp add — OpenClaw does
not read that variable at all, so the command writes to the real
~/.openclaw/openclaw.json. To isolate a scripted test, use one of the
mechanisms OpenClaw does support: the --profile <name> flag, or the
OPENCLAW_CONFIG_PATH / OPENCLAW_STATE_DIR environment variables.
First-class memory
Everything above gives your agent memory it can call. What it does not give you is memory that
accumulates on its own — passive turn capture and bootstrap context injected at the start of a
session. That comes from a memory-slot provider, OpenClaw’s plugins.slots.memory, which is a
different mechanism from MCP entirely.
The provider is published as @companionintelligence/openclaw-memory. You do not need MCP
configured to use it — the two are independent, and you can do this instead of the sections above
if passive memory is all you want.
There are two ways to install it. They produce the same configuration, and both end with a gateway
restart — the config is read at startup. To turn parts of the integration off without uninstalling,
set any of CI_MEMORY_CONTEXT_ENABLED, CI_MEMORY_RECALL_ENABLED, CI_MEMORY_TURN_ENABLED,
CI_MEMORY_WRITE_ENABLED, CI_INTENTS_ENABLED, or CI_INTENT_TOOLS_ENABLED to false in the
environment of the gateway process. All are on by default. Plugin releases after the pinned
2026.8.3 also read CI_DISPATCH_ENABLED. The
Companion OpenClaw app page
says what each one controls.
flowchart TB
subgraph official["The official way — on the machine running OpenClaw"]
A["openclaw plugins install<br/>npm:@companionintelligence/openclaw-memory"] --> B["Edit openclaw.json:<br/>slots.memory, entries, hooks,<br/>tools.alsoAllow, session-memory off"]
end
subgraph hub["The cihub connect way — on the Hub host"]
X["cihub connect openclaw<br/>probes the key, installs the pinned plugin,<br/>writes the config block below"]
end
B --> C["openclaw daemon restart"]
X --> C
C --> D["openclaw plugins inspect<br/>status: loaded, selected memory slot"]
D --> E["Have a conversation, then look for<br/>the turn in the Companion Memory dashboard"]The official way
Install the plugin
openclaw plugins install npm:@companionintelligence/openclaw-memory@2026.8.3If this fails, check your Node version before anything else. OpenClaw
refuses to run at all on a Node outside its supported range and says so —
OpenClaw 2026.9.1 prints Node.js >=22.22.3 <23, >=24.15.0 <25, or >=25.9.0 is required, and later releases raise the floor — which reads like a plugin
problem when it appears here. It is not; upgrade Node, or if you use a version
manager check that the version on your PATH is the one you think it is.
Verified working on native Windows against the pinned OpenClaw release once
Node is in range.
OpenClaw treats a package that is not in its official catalog as an arbitrary npm source. On
current releases the installer therefore asks you to confirm the source and to accept the plugin’s
capabilities before it installs anything. Answer at the prompts. In a script with no terminal, add
--force --accept-capabilities, which is what those two prompts stand for — review the plugin first,
because both flags skip the review.
Fill in what the installer does not
plugins install registers the plugin and does not write the rest of this block for you. Depending
on your OpenClaw version it may also select the plugin as your memory provider — the plugin’s README
says 2026.9.1 and later do — so treat the slots.memory line below as the pin that keeps that choice
explicit either way. Edit ~/.openclaw/openclaw.json:
Install before you edit, not after. slots.memory naming a plugin that is
not installed yet makes the whole config invalid — plugin not found: companionintelligence — and OpenClaw then refuses to run plugins install,
so you cannot fix it by carrying on. If you have already done it in that
order, remove the plugins block, install, then put it back.
{
"plugins": {
"slots": { "memory": "companionintelligence" },
"entries": {
"companionintelligence": {
"enabled": true,
"hooks": { "allowConversationAccess": true },
"config": {
"url": "<memory-url>",
"token": "<memory-key>"
}
}
}
},
"tools": { "alsoAllow": ["memory_store"] },
"hooks": {
"internal": { "entries": { "session-memory": { "enabled": false } } }
}
}Every line earns its place:
| Key | Why |
|---|---|
slots.memory | Selects the provider. Without it the plugin is installed and inert |
hooks.allowConversationAccess | OpenClaw only delivers conversation hooks to a plugin that opts in. Without it you get the tools and nothing accumulates — the entire point of this section |
tools.alsoAllow | If a tools profile or allowlist narrows your tool set, the built-in group:memory group covers search and get only, so memory_store has to be named. On an unrestricted config it is harmless |
session-memory disabled | OpenClaw’s bundled hook keeps a parallel local memory — session summaries written to workspace files on /new and /reset — that this provider does not manage and expects not to compete with |
Add to tools.alsoAllow, and never introduce tools.allow. alsoAllow
extends the default set; allow replaces it with an exclusive allowlist,
so creating it denies every tool not named in it and breaks unrelated things
on your machine.
The key for token needs different scopes than an MCP key. This provider
talks REST, not MCP, so the key must carry the Memory scope (plus
Intents for the intent tools) at capability Read & write — the
dialog’s default. A key minted for the MCP sections above (MCP + Intents)
is refused on every capture call, and the plugin never breaks your session
over a failed write, so the result is memory that silently never accumulates
while every check on this page passes. One key with Memory, Intents and
MCP covers both setups. See Get your API keys.
url is the Companion Memory base URL — the same <memory-url> used above,
without /api/mcp. It is written once and never re-resolved, so this agent
is configured for exactly one network position. A laptop that reaches your Hub
over a VPN at home and not at all from a café needs the address that works from
wherever it actually runs. See Find your address
first for the routes
and how to read each one off the dashboard.
Restart and verify
Configuration is read at startup, so the gateway has to come back before any of this takes effect:
openclaw daemon restartThat is the command when the gateway runs as an installed service — launchd on macOS, systemd on
Linux, a Scheduled Task named OpenClaw Gateway on native Windows. openclaw daemon status tells
you whether yours is one, naming the service manager and the unit or task it found. If you instead
start the gateway yourself in a terminal, stop that process and start it again — daemon restart
manages the service and does not touch a gateway you are running by hand.
Then:
openclaw plugins inspect companionintelligence --runtime --jsonLook inside the plugin object for "status": "loaded" and
"activationReason": "selected memory slot". toolNames should include memory_store.
Know what each check proves. plugins inspect is a separate process that
reads the config from disk and loads the plugin itself — the same trap this
page describes for openclaw mcp probe — so it reports loaded whether or
not the running gateway has restarted, and it never dials the server, so a
typo in url or an unreachable address still loads cleanly. The check that
exercises the running gateway and the connection is /ci-memory in a live
session. It calls GET /api/health, which does not check the key, so it
proves the address and not the credential.
The proof that passive capture is actually on: have a short conversation, then look for the turn in the Companion Memory dashboard — or start a fresh session and ask about something from the last one. Nothing in that flow calls a memory tool by hand, which is the point.
The cihub connect way
If you are working on the Hub host itself, one command does the install and the configuration:
cihub connect openclaw --memory-url "<memory-url>" --memory-key "<memory-key>"It probes the server before touching anything, installs the pinned plugin version, writes every key
in the table above, and backs up your config first. Add --dry-run to see exactly what it would do
and write nothing. You still restart the gateway afterwards — openclaw daemon restart — as the
command’s output reminds you.
The pre-flight probe checks the key as well as the address — it calls an endpoint that requires the
Memory scope, so a key that would fail silently at capture time is refused up front, with nothing
written.
It configures the machine it runs on. If your agent lives on your laptop and the Hub is an appliance across the room, use the official way on the laptop — that split is the design, not a gap.
Optionally add --hub-url and --hub-key to register the Hub MCP server too, replacing
the openclaw mcp add ci-hub step near the top of this page.
When memory does not accumulate
The command refused with “the memory slot is held by …”. Another provider is selected, and the
configuration this page writes would take the slot from it. That selection was a deliberate choice
you or a previous setup made, so cihub connect refuses to overwrite it without --force. (Plain
openclaw plugins install does not touch the slot either way — which is why step 2 exists.)
Setup succeeded, tools appear, nothing is captured. Two usual causes, both silent. First, the
key: wrong, or missing the Memory scope, or minted Read-only — the plugin swallows failed
writes rather than break your session, and no check on this page can catch a bad key, so re-check
the key before anything else. Second, hooks.allowConversationAccess: it must be true on the
plugin’s own entry — not at the top level.
Session summaries still pile up in workspace memory/*.md files. The bundled session-memory
hook is still enabled, keeping its parallel local memory alongside this provider.
Changes did nothing. A running gateway keeps its old config. openclaw mcp reload does not help
here — it disposes MCP runtimes in the process that runs it, and this is not MCP. Restart the
gateway with openclaw daemon restart, or by stopping and starting it yourself if you do not run it
as a service.