# One Brain, Two Wallets: Two Claude Code Accounts on One Machine

> Source: <https://dev.to/daksh-gargas/one-brain-two-wallets-two-claude-code-accounts-on-one-machine-5ejb>
> Published: 2026-07-20 18:56:18+00:00

I hit my Claude Code usage limit mid-week. I have a second subscription, but switching accounts the naive way — `/logout`

, `/login`

, repeat — is miserable, and it turns out "switching accounts" in Claude Code silently means switching *everything*: skills, MCP servers, settings, hooks, memory, session history. All of it.

What I actually wanted: **one brain, two wallets.** Same skills, same MCP servers, same muscle memory — just a different subscription getting billed.

Here's the setup that got me there. ~20 minutes, fully reversible.

Claude Code keeps all its state in a config directory — `~/.claude`

by default — and honors a `CLAUDE_CONFIG_DIR`

env var to point elsewhere. That's the whole trick:

```
# ~/.zshrc
# Claude Code multi-account aliases
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude claude'
alias claude-personal='CLAUDE_CONFIG_DIR=~/.claude-personal claude'
mkdir ~/.claude-personal
source ~/.zshrc
claude-personal   # prompts OAuth login for the second account — one time
```

Credentials land in whichever dir is active. Bare `claude`

still defaults to `~/.claude`

, so nothing about your existing workflow changes.

`.claude.json`

moves when you set the env var
Claude Code's user-level config file (`~/.claude.json`

— MCP servers, folder trust, onboarding state) lives *next to* `~/.claude`

, not inside it. But when `CLAUDE_CONFIG_DIR`

is set, it's read from *inside* the config dir instead.

So `claude-work`

— even though it points at the same `~/.claude`

you've always used — creates a fresh, empty `~/.claude/.claude.json`

and suddenly has zero MCP servers. Fix: symlink it to the master, since it's the same account:

```
ln -s ~/.claude.json ~/.claude/.claude.json
```

Now bare `claude`

and `claude-work`

are byte-for-byte identical.

The second account starts as a blank slate. Since only billing should differ, symlink everything shareable from the main dir:

```
cd ~/.claude-personal
ln -s ~/.claude/skills skills
ln -s ~/.claude/CLAUDE.md CLAUDE.md
ln -s ~/.claude/settings.json settings.json        # hooks, permissions, env
ln -s ~/.claude/settings.local.json settings.local.json
ln -s ~/.claude/plugins plugins
```

One source of truth: add a skill or tweak a hook once, both accounts see it.

MCP servers need more care. They live in `.claude.json`

, which *also* holds the account identity (`oauthAccount`

) — so you can't symlink the whole file across accounts. Merge just the `mcpServers`

block:

```
jq --slurpfile mcp <(jq '.mcpServers' ~/.claude.json) \
   '.mcpServers = $mcp[0]' ~/.claude-personal/.claude.json \
   > /tmp/merged.json && mv /tmp/merged.json ~/.claude-personal/.claude.json
```

`--resume`

doesn't know about accounts
Session transcripts live at `<config-dir>/projects/<project>/<session-id>.jsonl`

. Resume a personal session under the work account and Claude just says the session doesn't exist. Worse, anything that launches bare `claude`

for you — crash-restore scripts, launchd jobs — always resumes against the default dir.

The fix is a tiny shim earlier in `$PATH`

than the real binary. Every session ID exists in exactly one account's tree, so ownership *is* the routing signal:

``` bash
#!/bin/bash
# ~/.local/bin/claude — routes --resume to the account that owns the session
REAL=/opt/homebrew/bin/claude   # wherever `command -v claude` pointed before

if [[ -z "$CLAUDE_CONFIG_DIR" ]]; then
  id="" prev=""
  for arg in "$@"; do
    if [[ $prev == "--resume" || $prev == "-r" ]]; then id=$arg; break; fi
    if [[ $arg == --resume=* ]]; then id=${arg#--resume=}; break; fi
    prev=$arg
  done
  if [[ -n $id ]] && compgen -G "$HOME/.claude-personal/projects/*/$id.jsonl" >/dev/null; then
    export CLAUDE_CONFIG_DIR="$HOME/.claude-personal"
  fi
fi
exec "$REAL" "$@"
```

Now `claude --resume <id>`

just works, from anywhere, for either account. Explicit `CLAUDE_CONFIG_DIR`

(your aliases) passes through untouched.

The whole reason I did this was running out of credits *mid-conversation*. Since a session is just a transcript file, "continue this work conversation on my personal account" is a copy + resume:

``` bash
#!/bin/bash
# ~/.local/bin/claude-pickup <session-id> — continue a work session as personal
set -euo pipefail
id=${1:?usage: claude-pickup <session-id>}
src=$(ls ~/.claude/projects/*/"$id".jsonl 2>/dev/null | head -1)
[[ -n $src ]] || { echo "session $id not found" >&2; exit 1; }
proj=$(basename "$(dirname "$src")")
mkdir -p ~/.claude-personal/projects/"$proj"
cp -n "$src" ~/.claude-personal/projects/"$proj"/
CLAUDE_CONFIG_DIR=~/.claude-personal exec claude --resume "$id"
```

The work account keeps its copy as history; new turns land only in the personal copy. And since the shim checks the personal tree first, future bare `claude --resume`

of that ID follows the conversation to its new wallet automatically.

`/mcp`

once in the new account to re-auth. CLI-based MCP servers that use machine credentials work immediately.

```
claude              # work (default, unchanged)
claude-personal     # second subscription, same brain
claude --resume X   # figures out the account itself
claude-pickup X     # mid-conversation wallet swap
```

Same skills, same MCP servers, same hooks, same memory. The only thing that changes is who gets the bill.
