
ccswitch
Switch between multiple Claude Code accounts with one command — MCP servers, plugins, skills and session history stay shared.
Install with your AI
Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.
Install and set up ccswitch (git-clone project) into my current project. Found on https://claudeers.com/ccswitch Repo: https://github.com/2hmad/ccswitch Homepage/docs: — Detected install method: git-clone → git clone https://github.com/2hmad/ccswitch Category: devtools. Platforms: cli, api, desktop, web. Read the repo's README for exact setup and env vars, then install it and wire it into my project. Claudeers Health Verdict: active; community-verified: false. Confirm the source before running anything.
git clone https://github.com/2hmad/ccswitch
// compatibility
| Platforms | cli, api, desktop, web |
|---|---|
| Operating systems | — |
| AI compatibility | claude |
| License | MIT |
| Pricing | open-source |
| Language | Shell |
ccswitch
Switch between multiple Claude Code accounts with one command — without re-running /login every time, and without splitting your setup into isolated copies.
$ ccswitch list
ACCOUNT EMAIL STATUS RELOGIN
* work [email protected] ready 24d
personal [email protected] ready 19d
client [email protected] BROKEN - re-login -
$ ccswitch personal
switched to personal ([email protected]) valid 6h 40m
$ claude # runs as personal
Why
Claude Code stores one signed-in account at a time. The usual workaround is to give each account its own CLAUDE_CONFIG_DIR, but that isolates everything — your MCP servers, plugins, skills, agents, slash commands, session history and CLAUDE.md all get duplicated per account, and each copy drifts.
ccswitch takes the opposite approach. One ~/.claude, shared by every account. Only the credential and the identity it belongs to are swapped.
Separate CLAUDE_CONFIG_DIR | ccswitch | |
|---|---|---|
| MCP servers | duplicated per account | shared |
| Plugins & marketplaces | duplicated per account | shared |
| Skills, agents, commands | duplicated per account | shared |
| Session history | duplicated per account | shared |
settings.json, CLAUDE.md | duplicated per account | shared |
| Accounts active at once | many | one |
That last row is the trade-off. If you need two accounts running simultaneously in different terminals, use CLAUDE_CONFIG_DIR instead — ccswitch changes which account claude runs as, globally.
Install
With npm, if you already have Node around:
npm install -g @2hmad/ccswitch
Or with no Node at all:
curl -fsSL https://raw.githubusercontent.com/2hmad/ccswitch/main/install.sh | bash
Or from a checkout:
git clone https://github.com/2hmad/ccswitch
cd ccswitch && ./install.sh
The npm package is scoped because the unscoped ccswitch name belongs to an unrelated project. The installed command is still ccswitch.
Requires bash, python3, and Claude Code. Node is only a delivery mechanism — nothing at runtime uses it. Linux is supported; macOS is experimental — see Platform support.
Update
ccswitch update # fetch and install the latest release
ccswitch update --check # just report what is available
It knows how it was installed and won't fight your package manager — an npm copy is told to run npm update -g @2hmad/ccswitch rather than overwriting itself. Before replacing anything it checks the download is really ccswitch, of the expected version, and parses as valid bash, then swaps it in by rename so the running script is never written through.
ccswitch list mentions a newer version when one exists. That line is printed from a cache written by the scheduled refresh run, so list itself never touches the network. Silence it with CCSWITCH_NO_UPDATE_NOTICE=1.
Usage
Sign each account in once:
ccswitch login work # opens Claude Code; run /login, then /exit
ccswitch login personal
ccswitch login client
Already signed in as one of them? Capture it without a fresh login:
ccswitch add work
Signed in with /login inside Claude Code instead? That changes the live account without telling ccswitch, so point it at the right slot afterwards:
ccswitch save work # adopt the live credential into 'work'
A bare ccswitch save writes into the active account, and refuses if the live credential belongs to a different one — it names the account it actually belongs to.
Then switch whenever you like:
ccswitch personal # shorthand
ccswitch use personal # same thing
claude # runs as personal
All commands
| Command | Description |
|---|---|
ccswitch <name> | Switch to an account |
ccswitch use <name> [--force] | Same, explicit form |
ccswitch login <name> | Sign a new account in and store it |
ccswitch add <name> | Store the account you're already signed in as |
ccswitch list | Accounts, emails, token status |
ccswitch current | Print the active account name |
ccswitch save [name] | Write the live token back to its account |
ccswitch sync | Capture the live token into the account it belongs to |
ccswitch autosync install | Capture every rotation automatically |
ccswitch refresh [name|--all] [--force] | Renew a token as its refresh token nears expiry |
ccswitch rm <name> | Forget an account |
ccswitch rename <old> <new> | Rename an account |
ccswitch backup [file] | Archive the vault |
ccswitch restore <file> | Restore a vault archive |
ccswitch doctor | Diagnose the setup |
ccswitch update [--check] | Update ccswitch itself |
ccswitch completion bash|zsh | Print a completion script |
Keeping parked accounts alive
Claude Code holds two tokens: a short-lived access token (~8h) and a refresh token used to mint new ones. The refresh token rotates — every renewal consumes the old one and issues a replacement. Present a consumed token and the server answers invalid_grant, at which point Claude Code marks it dead and blanks the credential on disk, so the next thing you type asks you to sign in again.
Each rotation also pushes the refresh token's expiry back out to roughly 28 days from that moment, so an account you actually use never lapses. The 28-day clock is not what costs you a login. Losing a rotation is. If the vault holds a snapshot taken before a rotation, restoring it hands the server a token that has already been spent.
That is what ccswitch autosync install is for: a systemd path unit watches the credential file and captures every rotation as it is written, so the vault can never fall behind. Without it the vault only catches up when you happen to run a ccswitch command, which leaves a window.
The access token expiring is a non-event — Claude Code renews it on use — so ccswitch list does not report it. ccswitch doctor shows it if you want the detail.
That matters for a tool that snapshots and restores credentials. ccswitch keeps the vault in step by syncing the live credential into the active account's slot on every run, not only when you switch — a token Claude Code rotated mid-session, or a /login you typed inside Claude Code, would otherwise never reach the vault, and restoring that stale snapshot later would hand the server a consumed token. ccswitch list reports a slot whose tokens have been cleared as BROKEN - re-login, and its RELOGIN column counts down the refresh token's real remaining life.
An account you haven't switched to in a few weeks can lose its refresh token, which forces a full ccswitch login <name> with a browser round-trip. ccswitch refresh --all prevents that: it restores each stored account in turn, makes one tiny API call to exercise its token, saves the renewed credential back, and returns you to the account you started on.
Run it by hand whenever it occurs to you:
ccswitch refresh --all
Or schedule it daily. Because each renewal rotates the token, refresh does nothing until an account is within CCSWITCH_REFRESH_WINDOW_DAYS (default 7) of its refresh token expiring — so a typical run makes no API calls at all:
$ ccswitch refresh --all
skip work ([email protected]) re-login in 24d - not due yet
refreshed personal ([email protected]) valid 8h 0m
systemd user timer (recommended)
Prefer this on anything that isn't a server running 24/7. Persistent=true catches up a run missed while the machine was off; cron silently skips it. A desktop that sleeps overnight will never fire a 4am cron job — the run is lost every night, without a single log line to say so. Pick a time the machine is usually awake anyway.
~/.config/systemd/user/ccswitch-refresh.service:
[Unit]
Description=Refresh parked ccswitch account tokens
[Service]
Type=oneshot
Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=%h/.local/bin/ccswitch refresh --all
~/.config/systemd/user/ccswitch-refresh.timer:
[Unit]
Description=Daily ccswitch token refresh
[Timer]
OnCalendar=*-*-* 15:30:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
Enable it:
systemctl --user daemon-reload
systemctl --user enable --now ccswitch-refresh.timer
systemctl --user list-timers ccswitch-refresh.timer # when it next fires
journalctl --user -u ccswitch-refresh.service # what happened last time
If you want it to run while you're logged out, also sudo loginctl enable-linger $USER.
cron
Only if the machine is genuinely always on. crontab -e, then:
PATH=/home/you/.local/bin:/usr/local/bin:/usr/bin:/bin
30 4 * * * ccswitch refresh --all >> "$HOME/.cache/ccswitch-refresh.log" 2>&1
Two things that trip people up:
- Set
PATH. cron runs with a bare/usr/bin:/bin, andrefreshneeds bothccswitchand theclaudebinary. Runcommand -v claudeand put that directory first — it's usually~/.local/bin. Without this the job dies withthe 'claude' command was not found on PATH. - Pick an hour the machine is actually on, and you are not working.
refreshrefuses to run while aclaudeprocess is alive. A job scheduled for a time you are always shut down never runs at all, and cron will not tell you.
How it works
Claude Code keeps two pieces of account state:
~/.claude/.credentials.json— the OAuth access and refresh tokens~/.claude.json— a mixed file holding your sign-in identity and your MCP servers, per-project trust decisions, and other config
ccswitch stores per account, under ~/.config/ccswitch/accounts/<name>/:
credentials.json copy of .credentials.json
identity.json only the oauthAccount and userID keys from .claude.json
Switching writes those two back into place and leaves every other key in ~/.claude.json untouched — which is why mcpServers, projects and the rest stay shared.
Before switching away, ccswitch saves the live credential back into the account you're leaving. Claude Code refreshes tokens during a session, so without this you'd restore a stale token the next time around.
Notes and caveats
One account is active at a time. This is by design — it's the price of sharing everything. ccswitch use refuses to run while a claude process is detected; pass --force to override.
The vault holds live session tokens. ~/.config/ccswitch is created mode 700 and files mode 600. ccswitch backup produces an archive containing those tokens — encrypt it if you keep it anywhere but your own disk.
Tokens rotate, and a lost rotation costs you a login. Renewing consumes the old refresh token, so a snapshot taken before a rotation is worthless afterwards — the server answers invalid_grant and Claude Code blanks the credential on disk, which is why an account can suddenly demand /login mid-prompt. ccswitch syncs the live credential into the active slot on every run to stay ahead of this. An account it could not keep up with shows as BROKEN - re-login in ccswitch list, and needs one ccswitch login <name>.
Parked accounts still lapse. The access token (~8h) refreshes itself whenever the active account is used — Claude Code does that on its own. Accounts sitting in the vault aren't touched by anything, so their refresh token (~28 days) can expire outright. Run ccswitch refresh --all occasionally, or schedule it — see Keeping parked accounts alive. Because each renewal is itself a rotation, it acts only within CCSWITCH_REFRESH_WINDOW_DAYS (default 7) of the refresh token's expiry, so most runs make no API call at all. The RELOGIN column in ccswitch list is the number that matters.
CLAUDE_CONFIG_DIR takes priority. If it's set, ccswitch operates on that directory instead of ~/.claude. ccswitch doctor will warn you. If you're migrating from per-account config dirs, unset it first.
Project config is unaffected. .mcp.json, .claude/settings.json and CLAUDE.md inside a repo load from the repo regardless of which account is active.
Migrating from per-account CLAUDE_CONFIG_DIR
If you already have isolated config dirs, pick the one with the setup you want to keep, make it your ~/.claude, then capture each account's credential:
unset CLAUDE_CONFIG_DIR # and remove it from ~/.zshrc
# keep the best-configured directory as the shared one
mv ~/.claude ~/.claude.old
cp -r ~/.claude-accounts/work ~/.claude
cp ~/.claude-accounts/work/.claude.json ~/.claude.json
ccswitch add work # captures what's now live
# for each remaining account, drop its credential in and capture it
cp ~/.claude-accounts/personal/.credentials.json ~/.claude/.credentials.json
python3 - <<'EOF'
import json, os
h = os.path.expanduser("~")
src = json.load(open(f"{h}/.claude-accounts/personal/.claude.json"))
dst = json.load(open(f"{h}/.claude.json"))
for k in ("oauthAccount", "userID"):
dst[k] = src.get(k)
json.dump(dst, open(f"{h}/.claude.json", "w"), indent=2)
EOF
ccswitch add personal
Then ccswitch list to confirm all of them are there.
Moving to another machine
The vault is portable, but an account cannot be live on two machines at once. Refresh tokens rotate: whichever machine renews first invalidates the other's copy, and the loser gets a forced browser sign-in. So this is a move, not a copy — decide which machine owns the accounts.
On the machine you are leaving:
ccswitch sync # capture the newest rotation first
ccswitch backup ~/ccswitch-move.tar.gz # the whole vault
ccswitch sync matters. The backup is only as fresh as the vault, and a rotation that has not been captured yet would be left behind — you would carry an already-spent token to the new machine.
Copy it over a channel you trust, because it contains live session tokens:
scp ~/ccswitch-move.tar.gz you@laptop:~/
On the new machine:
npm install -g @2hmad/ccswitch # or the curl installer
ccswitch restore ~/ccswitch-move.tar.gz
ccswitch autosync install
ccswitch list # every account should read 'ready'
shred -u ~/ccswitch-move.tar.gz # it holds live tokens
Then on the old machine, stop using those accounts — ccswitch rm <name> for each, or delete ~/.config/ccswitch outright. Leaving them behind is what causes the two machines to fight over rotations.
What the vault does not carry
ccswitch stores only the credential and the identity it belongs to. Your MCP servers, plugins, skills, agents, commands, history and CLAUDE.md live in ~/.claude and ~/.claude.json, and are the same for every account — copy them separately if you want the same setup:
tar czf ~/claude-config.tar.gz -C ~ .claude .claude.json
Restore that before ccswitch restore, since it overwrites ~/.claude.json — which is also where the active account's identity lives.
On WSL
WSL behaves like Linux, and the file backend applies. One catch: ccswitch autosync and the scheduled refresh both need systemd, which WSL does not run as init unless you switch it on:
printf '[boot]\nsystemd=true\n' | sudo tee -a /etc/wsl.conf
Then wsl --shutdown from Windows and reopen. Without it, ccswitch autosync install will tell you so, and you can run ccswitch sync by hand after signing in instead.
Keep everything inside the WSL filesystem (~), not under /mnt/c. Windows drives do not carry Unix permissions, so the vault could not be locked down to mode 700.
Platform support
| Platform | Status |
|---|---|
| Linux | Supported |
| macOS | Experimental — works via the login keychain, but not yet confirmed on real hardware (#1) |
| Windows / WSL | WSL behaves like Linux and should work; untested |
On macOS, Claude Code keeps the OAuth credential in your login keychain rather than in ~/.claude/.credentials.json, so ccswitch reads and writes it there with security(1). It derives the same item Claude Code does:
| Value | |
|---|---|
| Service | Claude Code-credentials, plus -<sha256(config dir)[:8]> when CLAUDE_CONFIG_DIR is set |
| Account | $USER, or claude-code-user if that is unset or contains anything outside [a-zA-Z0-9._-] |
This path has been exercised against a security(1) stand-in but not yet against a real Keychain, so treat it as experimental and report anything odd on #1.
ccswitch doctor prints what it resolved to. The first switch may raise a keychain prompt — macOS asks before letting a new binary read an item it did not create. Choose Always Allow if you would rather not be asked again.
The keychain is used only when there is no ~/.claude/.credentials.json; a file left by an older Claude Code still wins. Force it either way with CCSWITCH_BACKEND=keychain or CCSWITCH_BACKEND=file.
Contributing
Issues and pull requests welcome. Please run shellcheck bin/ccswitch before submitting; CI runs it on every push.
Changelog
See CHANGELOG.md.
License
MIT — see LICENSE.
// faq
What is ccswitch?
Switch between multiple Claude Code accounts with one command — MCP servers, plugins, skills and session history stay shared.. It is open-source on GitHub.
Is ccswitch free to use?
ccswitch is open-source under the MIT license, so it is free to use.
What category does ccswitch belong to?
ccswitch is listed under devtools in the Claudeers registry of Claude-compatible tools.
// embed badge
[](https://claudeers.com/ccswitch)
// retro hit counter
[](https://claudeers.com/ccswitch)
// reviews
// guestbook
// related in Developer Tools
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Curs…
Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote.
Use Garry Tan's exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA
AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more). Turn any folder of code, SQL schemas, R scripts, shell scripts, docs,…