Repository navigation
mcc v0.4.0
Each project can now have its own profile, which mcc run starts (at a usage limit it asks first, with waiting as the default), and usage now comes by default from the rate limits the opt-in mcc statusline caches instead of from each profile's login. mcc sync is three-way and keeps a profile's own edits, and profiles can share the default's plugins, link its skills and other read-mostly items, keep their own settings in an overlay and share sessions within a named group. mcc doctor checks every profile's setup, mcc config reads and changes mcc's settings without hand-editing TOML, and mcc env and mcc exec point other tools at a profile. mcc list hides account emails by default, and releases now ship prebuilt binaries for macOS and Linux, which the install script uses.
Added
mcc add --email ADDRESSandmcc add --ssopass--emailand--ssotoclaude auth login(each its own argument, no shell), for the new profile and for the default profile.--emailmust look like one address and mcc does not print it (after the login, the signed-in account's email is shown like any profile's); both are refused with--no-login. After the login,mcc addwarns, without stopping, when the profile is signed in with an Anthropic Console account (API usage billing;claude auth statusreportsapiKeySource: "/login managed key"): Claude Code stores a Console sign-in outside the profile's directory, so profiles don't keep it separate. The hint namesALIAS auth login --claudeaito switch to a Claude subscription. (#58)- A status view for bare
mcc. Once mcc is set up,mccwith no command (or with only global flags such as-qor--no-color) printsHere:(the profilemcc runstarts in the current directory, in its color with its label, and why: a project default, or none), thenmcc list's table of every profile with its account, cached 5-hour and weekly usage, last use and sync state. On stderr follow warnings for drift against the default (profiles a sync would change; local edits and never-synced fresh profiles don't count), out-of-date launchers, missing directories and a project default naming no profile, and a pointer tomcc doctorwhen there is any. It is read-only and fast: no lock, no first-run setup, no log line, nothing written; usage comes from themcc statuslinecache only, never looked up live, even with[usage] live = true(so it never shows or records the live-lookup note), and skill counts and sizes on disk are left tomcc list. Emails stay hidden unless[privacy] show_emailis set, colors follow--no-color,NO_COLORand whether stdout is a terminal, and piped it prints the same text without colors. Before setup, baremccstill prints the help on stderr and exits 2 without setting anything up, and global flags alone are still a usage error;mcc helpandmcc --helpare unchanged. (#54) - An opt-in launch banner. With
[launch] banner = true(mcc config set launch.banner true; off by default), starting a profile from its launcher or withmcc runprints one line on stderr: the profile's name in its color, its label and its cached 5-hour and weekly usage, e.g.▶ work · Acme · 5h 42% · week 18%. It is printed only when stderr is a terminal, never on stdout (so<alias> -p "…" | jqis unaffected), and not withmcc run --quiet. It reads onlyconfig.tomland themcc statuslinecache: no network, token or Keychain access at launch, even with[usage] live = true. Launchers include the banner call (the hiddenmcc launch-banner <alias>, run only when stderr is a terminal, with mcc's errors discarded) only while the banner is on: with it off, as by default, launchers are byte-for-byte what they were, nothing extra runs at launch, and no launcher is reported out of date. After turning it on, runmcc refresh-launchers(mcc config set launch.bannersays so) to add the call to existing launchers; profiles added later get it straight away, and runningmcc refresh-launchersafter turning it off takes it out again. While it is on,mcc doctorwarns about a launcher without the call, andmcc list("launcher_current": falsein--json) counts it as out of date. (#59) - A color per profile, next to its label.
mcc add --color COLORandmcc config set profiles.ALIAS.color COLOR(""removes it) setcolorunder[profiles.ALIAS]to one ofblack,red,green,yellow,blue,magenta,cyanorwhite; anything else is refused, bymcc config edittoo.mcc listshows the profile's name in its color (theNAMEcolumn, the project table and--long, which adds aColorrow), and--json/--json-v2addcolorper profile.mcc runnames the profile it starts in its color, followed by its label, e.g.starting `work` (Acme (work)).mcc statusline --prefixadds the label,[work · Acme (work)], and colors the segment. Colors follow--no-colorandNO_COLOR, and are left out when the output isn't a terminal, except in the status line, which Claude Code renders. A label containing an email is left out ofmcc runand the status line while emails are hidden. A label with control characters, hand-written inconfig.toml, is never printed bymcc run,mcc listor the status line.mcc config set profiles.ALIAS.labelalready changes an existing profile's label. (#55) - Prebuilt release binaries. Pushing a
vX.Y.Ztag runs a release workflow that buildsmccfor macOS and Linux on x86_64 and arm64 (x86_64-apple-darwin,aarch64-apple-darwin, and staticx86_64-unknown-linux-musl,aarch64-unknown-linux-musl), checks that the tag matchesCargo.toml, runs the tests on each native target, and attachesmcc-<target>.tar.gz, a.sha256per archive andSHA256SUMSto the release. It creates the release as a draft, attaches the archives and checksums, then publishes it, so the latest release never lacks binaries; a draft that already exists for the tag is reused, with its title and notes. Re-running it for a published tag never replaces that release's assets: Rust builds are not reproducible, so when the newSHA256SUMSdiffers from the published one it stops with an error. Release builds use a pinned Rust toolchain. Its actions are pinned to commit SHAs and only the upload job can write. The install one-liner now downloads the binary for your platform from the latest release (orMCC_VERSION), verifies its SHA-256 checksum (a mismatch, or a missing checksum, stops with nothing installed) and installs it into~/.local/bin(MCC_INSTALL_DIR), so it no longer needs Rust; it builds withcargoas before from a clone, withMCC_FROM_SOURCE=1, or when a release has no binary for the platform (a source build withMCC_VERSIONbuilds that tag). Earlier versions of the script installed into~/.cargo/bin; if an oldmccthere comes first on yourPATH, the script now says so (remove it withcargo uninstall mcc). The Homebrew formula still builds from source; its header describes switching the tap to the binaries. (#53, #89) mcc doctor [PROFILE]checks every profile's setup, the default included, and prints a fix for each problem: mcc set up andclaudefound;~/.local/binonPATH; no rc file settingCLAUDE_CONFIG_DIRfor every shell (export, a bare assignment or fish'sset -gx, outside comments, aliases and mcc's own blocks), which makes plainclaudestop starting the default profile; and per profile its directory (present, mode 0700), launcher (present, written by mcc, executable, starting the profile's directory, with the plugins root exactly when it shares plugins, current), alias block,claude auth statusrun through the launcher (configDirectorymatching the registry on Claude Code 2.1.268+, skipped before; logged in), session-sharing link and store, leftovers of an interruptedmcc share, drift against the default and, when installed, themcc statuslineshim. Token expiry is checked only with[usage] live = trueandMCC_NO_USAGEunset. Fixes are mcc commands where one exists (mcc refresh-launchers ALIAS,mcc share ALIAS,mcc sync ALIAS,mcc exec ALIAS -- claude auth login, …), else the line to remove or the block to paste. Findings areok,warn,problemorskipped; mcc exits 1 when there is a problem.--jsonprints a bare array of{profile, check, status, message, fix}and--json-v2wraps it as{"schema": 2, "checks": [...]}. Doctor never writes~/.mcc/config.toml, not even the first-run setup, and takes no lock.doctoris reserved as an alias. (#44)- Shared plugins.
mcc link ALIAS pluginsmakes a profile use the default profile's plugins instead of its own copy (about 88 MB per profile on one real setup): its launcher,mcc run,mcc execandmcc envsetCLAUDE_CODE_PLUGIN_CACHE_DIRto~/.claude/plugins. That variable replaces Claude Code's whole plugins root, so installed plugins, marketplaces, the plugin cache and plugin data are shared;enabledPluginsstays per profile insettings.json(and its overlay). Installing, updating or uninstalling a plugin in a sharing profile does it for the default and every sharing profile, and a profile started without the variable (e.g.CLAUDE_CONFIG_DIR=… claude) has no plugins; the README documents both.syncanddiffleave a sharing profile's plugins alone, andmcc listcounts the default's installed plugins for it. Turning it on moves the profile's ownplugins/to.mcc-backup/<timestamp>/(nothing is deleted; remove the backup to free the space) and rewrites the launcher; a failed step undoes both.--offcopies the default's plugins back in.mcc link ALIASshows whether plugins are shared. Launchers of profiles with their own plugins, andmcc run/exec/envfor them, clear the variable when it holds mcc's own value, so a value left byeval "$(mcc env work)"doesn't leak into another profile; a value you set yourself is kept. This behavior ofCLAUDE_CODE_PLUGIN_CACHE_DIRwas verified on Claude Code 2.1.286; other versions may treat it differently. If a sharing profile shows no plugins,mcc link ALIAS plugins --offgives it its own copy again (or clone with--copy-plugins). (#47) - Opt-in link mode for read-mostly items.
mcc link ALIAS ITEMS(skills,agents,commands,output-styles,claude-md; comma- or space-separated) replaces the profile's copy of each item with a symlink to the default profile's, kept aslinkunder[profiles.ALIAS]in~/.mcc/config.toml(off for every item by default);mcc add --clone --link ITEMSlinks them when the profile is created. Linked items are never compared, copied, pruned or recorded bysyncanddiff, so they never count as behind, local edits or conflicts inmcc list. Turning it on backs up the profile's own copy to.mcc-backup/first and prints a write-through warning: editing a linked item in a profile changes the default's real files.mcc link ALIAS ITEMS --offremoves only the symlink, never the default's files, and copies the default's item in its place; a failed step undoes the whole run.mcc link ALIASshows each item's mode.settings(merged with the overlay) and the other items can't be linked,mcc config set profiles.ALIAS.linkpoints tomcc link, andlinkis reserved as an alias. (#46) - Per-profile settings overlays. A profile's
settings.jsonis now the default's (paths rewritten) deep-merged with[profiles.<alias>.settings]in~/.mcc/config.tomland then<profile>/settings.mcc.json, so a profile can keep its ownmodel,env,permissionsorstatusLine. Objects merge recursively, arrays and scalars replace the default's value whole, andnull(JSON) removes a key.add --cloneand everysyncrecompute the merge and record the merged result in.mcc-sync.json, so overlay keys never show up as local edits or conflicts insync,difforlist, and changing the overlay is applied by the next sync (with the usual backup). Profiles withsettingsin[sync] excludekeep their ownsettings.jsonand the overlay is not applied (syncwarns); an overlaystatusLinetakes precedence over the default's, themcc statuslineshim included.sync.extraentries namingsettings.mcc.jsonare rejected. (#45) mcc config get|set|edit|pathreads and changes~/.mcc/config.tomlwithout hand-editing TOML.set KEY VALUEacceptssync.exclude,sync.extra(comma-separated,""for none),privacy.show_email(or its old nameusage.show_email),usage.live,launch.banner(true/false),profiles.ALIAS.labelandprofiles.ALIAS.color(""removes either), checks the value as the command that uses it would, and saves through the comment-preserving, locked write; an invalid value is refused and the file is left unchanged. Keys other commands manage name that command instead.get KEYprints any key (dotted as in TOML; lists one item per line) and exits 1 when it isn't set.editopens a copy in$VISUAL/$EDITORand saves it only when it is valid mcc config (sync items, labels andshare_groupnames included), offering to edit again otherwise. It holds no lock while the editor is open, and keeps your copy instead of saving when another command changed the file meanwhile.pathprints the file's location.getandpathnever take the lock. (#48)mcc env [PROFILE]prints the shell line that pointsCLAUDE_CONFIG_DIRat the profilemcc runwould start in the current directory (the project's default, else the default profile, which printsunset CLAUDE_CONFIG_DIR), or at PROFILE. It prints the fish form (set -gx) for fish, chosen with--shellor from$SHELL, and the bash form inside a direnv.envrc; paths are single-quoted, so spaces and quotes are safe. Witheval "$(mcc env)"in a project's.envrc, plainclaude, IDE extensions and scripts use the project's profile; the README documents the direnv setup. (#42)mcc exec PROFILE -- CMD...runs any command with a profile'sCLAUDE_CONFIG_DIRand exits with its status, e.g.mcc exec work -- claude mcp list.mcc exec --all -- CMD...runs it once per profile, the default first, prints each profile's exit status, and exits 0 only when it succeeded everywhere (else with the first failing status). A command that can't be found counts as exit 127 (126 when it can't be run), and Ctrl+C stops at the current profile. Neither command takes mcc's config lock, so the command may run mcc itself.envandexecare reserved and can't be used as aliases. (#43)- Versioned JSON output.
mcc list,mcc usageandmcc project listtake--json-v2, which prints{"schema": 2, "profiles": [...]}("projects"formcc project list) around the same objects as--json, so scripts can check the schema version before reading.--jsonis unchanged: still a bare array (schema 1, implicit), sojq '.[]'scripts keep working; within a schema, fields are only ever added. The README's JSON output and its schema version section documents both shapes and the deprecation path. (#49) - Session-sharing groups and a cross-organization guard.
mcc share ALIAS --group NAMEshares only with the profiles of group NAME, through a store of its own (~/.mcc/shared/groups/NAME/projects), recorded asshare_group; without--group, the profile's recorded group (else thedefaultgroup,~/.mcc/shared/projects) is used, and moving to another group needs--offfirst. Joining a group whose profiles are in another organization, or whose account mcc can't identify, is refused unless--allow-cross-orgis passed (recorded asshare_cross_org_allowed_at, cleared by--off); organization ↔ personal is the strictest case and also needs a confirmation in a terminal.mcc listshows(shared: NAME)andshare_groupin--json. Upgrade note: profiles that already share keep~/.mcc/shared/projectsas thedefaultgroup's store, with nothing moved; a group that mixes organizations gets a warning, never a refusal. (#40) mcc syncis three-way.add --cloneandsyncrecord what they wrote in<profile>/.mcc-sync.json(a hash per path and MCP server), and the next sync compares the default, the profile and that record: a path changed only in the default is updated, one edited or deleted only in the profile is kept and reported as a local edit, and one changed in both (or added on both sides) is a conflict, kept unless--force(which backs it up to.mcc-backup/first, like every overwrite).--dry-runshows the classification and writes nothing, the record included. A profile without a record (or with one that can't be read) is compared two-way once, and that sync,--dry-runincluded, first warns with each path and MCP server it replaces and where they are backed up, even with--quiet(#87).mcc diff ALIAS [--only ITEMS]lists each differing path with its class and a line diff, andmcc listhas aSYNCcolumn (✓,3 behind,2 local edits,1 conflict;syncin--json, aSyncrow in--long).sync.extraentries naming.mcc-sync.jsonare rejected. (#41)mcc addrecords the account a profile logs in to under[profiles.<alias>.account]in~/.mcc/config.toml(email, account UUID, organization name and UUID, as Claude Code reports them), warns when another profile, the default included, is already logged in to the same account, and asks for an optional label (personal, the account's organization, or any text), kept aslabel.--label LABELsets it without asking.mcc listshows the organization (inACCOUNTwhen the profile has no label, and in--long),--longcards addOrganization IDandLabelrows, and--jsonaddsorganization_uuidandlabelper profile;organization(andaccount) fall back to the recorded account when the profile's.claude.jsonhas none. (#39)- Per-project profiles.
mcc project set PROFILEmakes PROFILE the onemcc runstarts in the current git repository (or directory, or--path DIR).--allow NAME(repeatable or comma-separated) names the other profilesmcc runmay offer when PROFILE is at its limit; setting the project again without it clears the list.mcc project unsetandmcc project list [--json]manage them. They are kept as[projects."<dir>"]tables in~/.mcc/config.toml.mcc renameupdates them, andmcc deleteremoves the ones naming the deleted profile and takes it off the allow lists. mcc run [-- CLAUDE_ARGS]starts Claude Code with the current project's default profile, or the default profile outside every project. mcc never switches accounts on its own. When that profile is at its 5-hour or weekly limit,mcc runasks what to do: waiting (starting nothing) is the default, starting it anyway comes next, and other profiles are offered only when the project allows them, by name and without their usage. Only that profile's usage is fetched. Without a terminal it stops (exit 2).--profile NAMEstarts NAME without checking usage. Starting a profile other than the project's, from the prompt or with--profile, prints a terms-of-service note on stderr.--dry-runprints the profile it would start. If the profile's usage can't be read, it starts. (#31)mcc listshows the project defaults under the profile table (project, profile and account, with← herefor the current directory).--longcards have aDefault forrow, and--jsonhas aproject_defaultsarray per profile.projectandrunare reserved and can't be used as aliases.- Opt-in
mcc statusline, a Claude Code status-line shim.mcc statusline installbacks up~/.claude/settings.jsonto~/.mcc/backups/, records the currentstatusLineand setsmcc statuslinein its place (never twice);--prefixstarts the status line with the active profile's name, e.g.[work]. On every refresh the shim works out the profile fromCLAUDE_CONFIG_DIR, saves the session'srate_limits(5-hour and weeklyused_percentageandresets_at) to~/.mcc/state/<profile>/usage.json, runs the previousstatusLinecommand with the same stdin and prints its output, without taking mcc's lock or writing~/.mcc/config.toml. Profiles get it with their nextmcc sync; when[sync] excludehassettings,installoffers to add it to each profile directly (--yesaccepts).mcc statusline uninstallrestores the previousstatusLineeverywhere.mcc renameandmcc deletemove or remove the profile's state, andstatuslineis reserved as an alias. (#33) - A GitHub Actions CI workflow runs
cargo teston Linux and macOS,cargo fmt --check,cargo clippy --all-targets -- -D warnings,shellcheck install.shandbrew auditon the Homebrew formula for every push and pull request. (#32)
Changed
mcc listhides account emails by default, likemcc usage: the table,--long, andaccountin--json/--json-v2(null, as are anorganizationandlabelthat contain an email; no field is removed).--show-email/--hide-emailoverride it for one run. The setting moved to[privacy] show_email(mcc config set privacy.show_email true) and applies to both commands;[usage] show_emailandusage.show_emailstill work, and atruethere moves to[privacy]the next time mcc saves the config (#57).- The default
mcc listtable fits 120 columns:NAME,ACCOUNT(the label, else the organization, after the email when emails are shown; long cells are cut with…),5H,WEEK,LAST USEDandSYNC. The command, organization, skill/agent/command/plugin/MCP/hook counts, size, last sync and usage age moved tomcc list --long; JSON is unchanged (#56). - Internal: the opt-in live usage request (
[usage] live = true) usesureq3 instead of 2, still on rustls with the bundled webpki roots (no native-tls or OpenSSL), and no new minimum Rust version (ureq3 needs 1.85; mcc stays at 1.88). The request behaves as before: 2 s to connect, 4 s in all, no redirects followed (so the bearer token is never sent elsewhere), themcc/<version>User-Agent, and 401, 403, 429 and other statuses reported as before.ureq3 would readHTTPS_PROXYand similar variables by default, so mcc turns that off to keep sending the token straight to the endpoint, asureq2 did. Tests against a local HTTP server check each of these. (#52) mcc add --cloneshares the default's plugins instead of copyingplugins/cacheandplugins/marketplacesinto the new profile (see Shared plugins above);--copy-pluginskeeps the old full copy, and so does[sync] excludewithplugins(nothing plugin-related is copied or shared then). Upgrade note: existing profiles are unchanged and keep their copy untilmcc link ALIAS plugins. (#47)- Internal:
mcc add,mcc rename,mcc refresh-launchersandmcc shareundo a failed run through one shared rollback helper (src/txn.rs) instead of four copies. Rollback order, messages and what is undone are unchanged. (#50) mcc list,mcc usageandmcc runread usage from the rate limitsmcc statuslinecaches in~/.mcc/state/<profile>/usage.json, and no longer read any login by default. They show how old the numbers are (anUPDATEDcolumn,Usage updatedinmcc list --long,· 12m agoinmcc run), treat a cached window whose reset time has passed as obsolete, and show—with a hint to run/usagein Claude Code for a profile with nothing current cached (reason: "no_data"in--json). TheusageJSON object gainssource(cacheorlive) andupdated_at. Upgrade note: the live lookup, which reads each profile's OAuth token (from.credentials.jsonor the macOS Keychain) and calls Anthropic's undocumented usage endpoint, is now opt-in withlive = trueunder[usage]in~/.mcc/config.toml; the first command that uses it prints a one-time disclosure, recorded aslive_disclosed_at.docs/privacy.htmldescribes the change. (#34)- The session-sharing and
mcc runterms-of-service notes, the README and the docs site now mention the updated Anthropic Usage Policy that takes effect November 12, 2026. The README and docs site quote the Team and Enterprise seat rules separately, each in its own words, documentMCC_NON_INTERACTIVE, anddocs/PLAN.mdis marked as a historical plan. (#38) - The full session-sharing note is printed only the first time sharing is turned on for a profile, and when is recorded as
share_note_acknowledged_atunder[profiles.ALIAS]in~/.mcc/config.toml. Latermcc shareandALIAS --mcc-shareruns print a one-line reminder instead. (#36)
Fixed
~/.mcc/logs/mcc.logno longer grows without bound: once it reaches about 1 MB it is moved tomcc.log.1(replacing the previous one) before the next line is written, so at most about 2 MB of log, which can include account emails, is kept. A failed rotation never breaks a command. (#51)mcc addno longer warns about a duplicate account (and suggestsmcc delete) when the same login is used in another organization, such as a personal plan and a work seat: those have usage limits of their own, so it prints a note instead. The warning stays for the same login in the same organization. (#40)- The Keychain login lookup (live usage) no longer needs
USER: when it is unset, as under launchd, cron or some IDE tasks, the account name comes from the password database (getpwuid_r) orid -un, so a logged-in profile is no longer reported asnot_logged_in. (#37) - Saving
~/.mcc/config.tomlkeeps hand-written comments and formatting, and a command that changes nothing (such asALIAS --mcc-shareon a profile that already shares) no longer rewrites it. Every command that writes the config holds an advisory lock on~/.mcc/.lockfrom loading it to saving it, so concurrent runs no longer lose each other's changes; a command that has to wait says so. (#35) - The
mcc projectandmcc listtests expected absolute project paths, but mcc shows a project under$HOMEas~/…; they now expect what mcc prints, so they pass on Linux as well as macOS. (#32) mcc syncbacks up every file or folder it overwrites or removes (such asCLAUDE.md, skills and agents) to<profile>/.mcc-backup/<timestamp>/, not onlysettings.json,settings.local.jsonand.claude.json, so edits made in a profile can be restored. A symlink is backed up as the link itself.sync.extraentries that are empty,.or inside.mcc-backupare rejected. (#30)- A
linkentry under[profiles.ALIAS]in~/.mcc/config.tomlthat can't be linked, such as a hand-writtenlink = ["settings"](orhooks, or an unknown item), no longer makesmcc sync,mcc diffand themcc listSYNC column skip that item and report the profile as in sync. The entry is ignored, with one warning per run that names the profile and the entry and says to remove it withmcc config edit, and the item is synced and compared like any copied item. The entry itself is left in the config. (#86) - The address given to
mcc add --emailno longer appears in~/.mcc/logs/mcc.log: the command line logged for each run has<redacted>in place of the value of--email ADDRESSor--email=ADDRESS, a value mcc then refuses included. After the login, the signed-in account's email, usually the same address, is still shown and logged as for any profile. (#88)
Security
- The install script downloads release files over HTTPS only, redirects included, and builds from a local clone only when it runs as that clone's
install.sh. Piped tosh(curl … | sh), earlier versions took the current directory for a clone and built and installed it whenever itsCargo.tomlhadname = "mcc". (#53)
Full Changelog: v0.3.0...v0.4.0
Install
This is the first release with prebuilt binaries. The install script downloads the one for your platform, checks its SHA-256 checksum and installs it into ~/.local/bin, so you don't need Rust:
curl -fsSL https://raw.githubusercontent.com/luongnv89/mcc/main/install.sh | shOr with Homebrew, which builds mcc from source with its own Rust:
brew install luongnv89/tap/mccEach release now carries mcc-<target>.tar.gz archives for macOS and Linux on x86_64 and arm64 (x86_64-apple-darwin, aarch64-apple-darwin, x86_64-unknown-linux-musl, aarch64-unknown-linux-musl; the Linux builds are static), each with a .sha256 file, plus SHA256SUMS. Earlier versions of the script installed into ~/.cargo/bin; if an old mcc there still comes first on your PATH, the script warns you, and cargo uninstall mcc removes it. To build from source, see the README.