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:
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:
#!/bin/bash
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:
#!/bin/bash
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.