Multi-client ops · field-tested
How we run one machine, one agent setup, and many clients' Clay workspaces without ever letting a command land in the wrong one. A short guide for humans, a copyable instruction block for agents, and a skill that deploys it into any harness.
Clay's agent tooling is a CLI (clay), and clay login stores one global session — a single Clay workspace at a time, shared by every project on the machine. If you serve multiple clients, each with their own Clay workspace, that global session is a loaded gun: switch clients without switching the login and every table read, workflow edit, and credit spend silently lands in the previous client's workspace. Nothing errors. It just quietly bleeds one client's account into another's.
Agents make this worse, not better — they'll happily run clay workflows list for Client B against Client A's session and report success.
The Clay CLI keeps its session under $XDG_CONFIG_HOME/clay. So we give each client its own config directory holding its own login session, and prefix every command with the matching environment variable. That's the entire trick — no wrapper scripts, no re-logins when switching clients, and multiple workspaces stay signed in concurrently.
mkdir -p ~/.clay-isolated/<client> then XDG_CONFIG_HOME=~/.clay-isolated/<client> clay login — and pick that client's workspace in the browser OAuth flow.
One directory per client. Or run the isolated-clay-instances skill (below): it does the terminal steps, pauses for you at the browser moment, then verifies and records the workspace id.
Prefix every clay call with XDG_CONFIG_HOME=~/.clay-isolated/<client>, where <client> comes from the folder being worked in.
The bare global session is never used for client work.
Before the first Clay call in a session, run clay whoami under the client's config dir and check workspace.id against the client's recorded id.
Check the id, never a display name — names are ambiguous, ids aren't.
CLAY_API_KEY. Running CLAY_API_KEY=<key> clay whoami to check which workspace a key belongs to will report the session's workspace, not the key's — and look like it passed. This bit us: a key for one client appeared to resolve to a different client's workspace. Verify keys in an empty config dir: XDG_CONFIG_HOME=$(mktemp -d) CLAY_API_KEY=<key> clay whoami.clay workflows …) requires a login session; API keys return auth_forbidden. So the per-client login sessions aren't optional — they're the only credential that drives the full surface.One config dir per client · prefix every call · verify by workspace.id · API keys in the client's own .env, created while viewing that client's workspace.
Use the global session for client work · borrow a key across clients · trust a bare whoami to verify a key · configure Clay as an MCP server (removed in CLI 0.6.0+).
Paste this into your agent's instruction file verbatim. It's harness-agnostic — plain markdown, no tool-specific syntax. Replace ~/.clay-isolated if you prefer a different location, and adapt the "client folder" convention to however your repo names clients.
## Clay — per-client workspace isolation (CLI only)
Clay is driven through the `clay` CLI. Do NOT configure a Clay MCP server —
`clay mcp` was removed (CLI 0.6.0+); an entry in MCP config can never start.
The default `clay login` session is ONE global credential: a single Clay
workspace at a time, shared machine-wide. NEVER use it for client work —
switching clients would silently switch workspaces and bleed one client's
Clay account into another's.
Each client has its own config dir holding its own login session:
1. One-time setup per client is done via the `isolated-clay-instances` skill
(or by a human, since it ends in a browser OAuth flow):
mkdir -p ~/.clay-isolated/<client>
XDG_CONFIG_HOME=~/.clay-isolated/<client> clay login # picking THAT client's workspace
If the dir for the current client does not exist, STOP and ask the human
to run the skill. Never fall back to the global session or another
client's dir.
2. Prefix EVERY `clay` command run for a client with its config dir:
XDG_CONFIG_HOME=~/.clay-isolated/<client> clay workflows list
`<client>` is derived from the client folder currently being worked in.
3. Before the first Clay call of a session, confirm the pinned workspace:
XDG_CONFIG_HOME=~/.clay-isolated/<client> clay whoami
Check `workspace.id` against the client's recorded workspace id. Verify
the id, never a display name. If it doesn't match, stop and report.
Rules:
- A LOGIN SESSION is required for workflow work (`clay workflows …`).
API keys cannot drive it (auth_forbidden). When both a session and
CLAY_API_KEY exist in one config context, the session wins.
- Any Clay API key a client needs (for scripts hitting Clay's REST API)
lives in that client's own secrets file (client-root `.env`) — never in
shared config, never borrowed across clients. It must be a Clay PUBLIC
API key (UUID-shaped), created at Clay → Settings → Account → API keys
while VIEWING that client's workspace; the key is scoped to whatever
workspace was active at creation.
- To verify which workspace a key belongs to, do NOT run a bare
`CLAY_API_KEY=<key> clay whoami` — a saved login session silently
shadows the key and reports the session's workspace instead. Use an
empty config dir so nothing can shadow it:
XDG_CONFIG_HOME=$(mktemp -d) CLAY_API_KEY=<key> clay whoami
Sanity-check first: the same command with an all-zeros key must fail
with auth_invalid.
- The CLI contract moves between versions. Before trusting any `clay`
line copied from a runbook, run `clay <command> --help`. An
`upgrade_required` error on every command means update the CLI
(`clay update`), not re-login.
- General credential isolation applies: if no Clay credential exists for
the current client context, stop and ask rather than borrowing one.
Primary path: use the skill. We packaged the whole setup as an agent skill, isolated-clay-instances — a folder containing a SKILL.md (the procedure) and references/agent-instructions.md (the canonical copy of the block above). Drop the folder into your repo's skills directory (.claude/skills/isolated-clay-instances/ for Claude Code; for other harnesses, point the agent at its SKILL.md as instructions — it's written to work either way), then tell the agent: "set up Clay for <client>". It runs the terminal steps, pauses for you at the browser OAuth step, verifies and records the workspace id, and installs the agent-instruction block into the right instruction file for your harness — idempotently, so re-running it just verifies.
One thing no instruction file — or skill — can fully do: the browser OAuth click-through in clay login. The skill runs the command and pauses for you at that moment; picking the right workspace in the picker is the one decision that stays human. Everything after — prefixing, verifying, refusing to fall back — is the agent's job.
No skill support, or you'd rather wire it by hand: every mainstream coding-agent harness reads a plain-text instruction file from the repo. Drop the block above into whichever file your harness loads; the content is identical everywhere.
| Harness | Where the block goes |
|---|---|
| Claude Code | CLAUDE.md at the repo root — or, if clients live under one folder (e.g. clients/), a CLAUDE.md inside that folder so it loads exactly when client work is happening. ~/.claude/CLAUDE.md makes it machine-global. |
| Codex | AGENTS.md at the repo root. Nested AGENTS.md files are also read, so a copy in the clients folder scopes it the same way. |
| Cursor | AGENTS.md at the repo root, or a rule file under .cursor/rules/ set to always apply. |
| Gemini CLI | GEMINI.md at the repo root (or set contextFileName to reuse your existing AGENTS.md). |
| Anything else | Paste the block into the system prompt / custom instructions. It has no tool-specific syntax, so it survives any harness. |
mkdir -p ~/.clay-isolated/<client> && XDG_CONFIG_HOME=~/.clay-isolated/<client> clay login, choosing that client's workspace in the browser.XDG_CONFIG_HOME=~/.clay-isolated/<client> clay whoami, and note workspace.id in the client's context docs so the agent has something to verify against..env (gitignored), and verify it with the empty-config-dir whoami shown above.workspace.id gives the agent a cheap, deterministic pre-flight check. Isolation you can't verify is isolation you'll eventually discover was broken.