ccmcp
A dynamic CLI and TUI for managing Claude Code's MCP servers, plugins, and per-project overrides across every scope Claude Code honors.
Why
Claude Code auto-loads MCP servers from at least six distinct places - user config, local project state, committed .mcp.json, bundled plugin .mcp.jsons, claude.ai OAuth integrations, and in-memory overrides. Starting a session with all of them enabled burns hundreds of tokens of context before you type a character. The built-in /mcp menu lets you toggle things one row at a time; ccmcp lets you toggle them in bulk, by scope, or by filter, and gives you a single view that reflects what will actually load in the current project.
Install
Homebrew (macOS, Linux):
brew install robworks-code/tap/ccmcp
Homebrew 5.x tap trust: Homebrew 5.x requires third-party taps to be trusted before it
will load them, so on install or brew upgrade you may see
Refusing to load formula robworks-code/tap/ccmcp from untrusted tap. Trust the tap once with:
brew trust robworks-code/tap
This is a one-time, per-machine step recorded in ~/.homebrew/trust.json and applies to every
non-official tap, not just this one.
go install (needs Go 1.25+):
go install github.com/ringo380/ccmcp@latest
Prebuilt binaries: download from the releases page.
From source:
git clone https://github.com/ringo380/ccmcp
cd ccmcp
go build -ldflags='-s -w' -o ~/bin/ccmcp .
Shell completions
ccmcp completion <shell> prints a completion script for bash, zsh, fish, or powershell. The static --scope flag and dynamic mcp stash <name> / mcp restore <name> arguments tab-complete to live values.
bash (Linux):
ccmcp completion bash | sudo tee /etc/bash_completion.d/ccmcp
bash (Homebrew on macOS, after brew install bash-completion):
ccmcp completion bash > "$(brew --prefix)/etc/bash_completion.d/ccmcp"
zsh (one-shot, current shell):
source <(ccmcp completion zsh)
zsh (persistent, requires compinit enabled in ~/.zshrc):
ccmcp completion zsh > "${fpath[1]}/_ccmcp"
fish:
ccmcp completion fish > ~/.config/fish/completions/ccmcp.fish
powershell:
ccmcp completion powershell | Out-String | Invoke-Expression
Quick start
# In any project directory:
ccmcp
The "effective" view is the default. It lists every MCP that will load in this project, with a badge showing its source and a checkbox showing whether it's currently enabled here. Three keystrokes to turn off every plugin-registered MCP for this project:
/plugin <enter> N w
/plugin - filter rows whose name contains "plugin" (or filter on a plugin name like cloudflare)
N - bulk-disable everything visible (writes to disabledMcpServers for this project only)
w - save
Undo the same way with A.
What ccmcp sees
| Badge |
Source |
Location |
Effect |
u |
user |
~/.claude.json#/mcpServers |
loads in every project |
l |
local |
~/.claude.json#/projects[<cwd>]/mcpServers |
this directory only |
p |
project |
./.mcp.json |
shared via git, team-wide |
P |
plugin |
bundled .mcp.json per enabled plugin |
loads whenever plugin enabled |
@ |
claude.ai |
OAuth remote integration |
loads wherever you're signed in |
s |
stash |
~/.claude-mcp-stash.json |
ccmcp-owned, parked |
The effective view's [x] / [~] / [ ] marks mean:
[x] - will load in this project
[~] - disabled here via a per-project override (easy to undo)
[ ] - not active in this project by any source
[?] - in disabledMcpServers but no live source found (stale); the row description
explains why (plugin not installed, name deleted/renamed, etc.) and ccmcp mcp prune
offers to clean it up.
Every row has a resolved source. MCPs whose parent plugin is installed but globally
disabled render as [ ] with description (currently disabled) so you can tell them
apart from genuinely absent sources.
TUI keys
MCPs tab (default)
| Key |
Action |
space |
toggle current row in the active scope |
A / N |
bulk enable/disable every visible row |
S |
stash the current row (or unstash if it's already in stash) |
m |
move an MCP's config between user / local / stash |
s |
cycle scope: effective → local → user → project → stash |
/ |
filter by substring (enter to lock, esc to cancel) |
c |
clear filter |
j / k / arrows |
navigate |
g / G |
top / bottom |
pgup / pgdn |
page |
Plugins tab
| Key |
Action |
space |
toggle plugin enabled/disabled |
A / N |
bulk enable/disable every visible plugin |
B |
bulk update - re-fetch every installed plugin |
F |
show last bulk-update failures (stderr + hint; R retries one) |
x |
remove (two-step confirm); clean-removes plugins flagged ⚠ removed from marketplace, including their cache dir |
R |
refresh update probes + recheck marketplace membership (live) |
f |
cycle filter: all → enabled → disabled |
/ |
search |
Discover tab
| Key |
Action |
enter |
drill in (marketplace → plugins → conflict preview) |
a |
add the selected marketplace (clones it + writes settings) |
i |
install the selected/previewed plugin (adds its marketplace first if needed; i twice to reinstall an existing one) |
H |
show/hide already-installed marketplaces (hidden by default) |
r |
refresh discovery sources (bypass cache) |
/ |
filter (matches name, description, tags) |
c |
clear filter |
j / k / arrows |
navigate · g / G top / bottom · b / esc back |
Browse a substantial built-in registry of curated Claude Code marketplaces (plus the awesome-list scraper and any discoverySources URLs), sorted by GitHub stars. By default the list shows only marketplaces you haven't installed yet - managing existing ones is the Marketplaces tab's job; press H to reveal installed entries (marked [=]). a adopts a marketplace without retyping it in the Marketplaces tab; i installs a plugin in one keystroke and enables it.
Tweaks - Profiles sub-view
| Key |
Action |
enter |
apply profile (replaces current project's MCPs) |
n |
create profile from current state |
d |
delete |
j/k, g/G, pgup/pgdn |
navigate / jump / page |
Tweaks - Summary sub-view
| Key |
Action |
j / k / arrows |
navigate fixable issues (skips display rows; scrolls past the ends) |
f |
preview a fix for the selected issue (in-place orphan/stash prune, or claude --print for config edits) |
F |
bulk-fix all issues in the cursor's category in one claude run (skill/agent/command frontmatter rewrites, slash conflicts, etc.) |
l |
run an LLM review on the selected issue without applying it |
y / n / esc |
approve / cancel in the confirm panel; u reverts a landed CLI fix |
p |
bulk-prune orphan overrides (legacy; press twice to confirm) |
Bird's-eye overview of every scope's counts, per-project overrides, and redundancies. Each actionable row (orphan override, stash redundancy, duplicate-load, slash-command conflict, plugin registration drift) is cursor-selectable and fixable in place - orphan prunes and stash drops apply directly to the in-memory state (save with w), and config edits hand off to claude --print non-interactively with an in-TUI spinner.
Tweaks - Doctor sub-view
| Key |
Action |
r |
re-run lint checks |
l |
run one bundled LLM review across CLAUDE.md + MEMORY.md (Haiku, single call) |
a |
apply the bundled review back to disk (single Claude call) |
j / k / arrows |
scroll |
g / G |
top / bottom |
f |
preview a fix for the selected issue (programmatic when possible) |
F |
bulk-fix every issue sharing the cursor's code in one keystroke (programmatic stack, or single bundled claude call when CLI-only) |
y / n |
approve / reject the previewed fix (in confirm panel) |
u |
revert a CLI fix from its on-disk snapshot (in post-review panel) |
Runs structural lint on CLAUDE.md and MEMORY.md for the current project. Pressing f opens a preview panel: in-TUI fixes show a unified diff of the exact change before you approve; Claude-CLI fixes show the full prompt first, then after the CLI runs, show the resulting diff and let you keep (y) or revert (u). Every fix snapshots the original file to ~/.claude-mcp-backups/doctor/ (kept: 20 newest per file, max age 30 days). F bulk-fixes every issue that shares the cursor's lint code in a single keystroke - programmatic codes (broken index entries, missing frontmatter fields, standalone broken links, empty MEMORY.md) are applied directly with per-file snapshots; CLI codes (line-too-long, file-too-long, content rewrites) are bundled into one claude --print --max-turns 4 invocation with a strict-imperative envelope that forces Edit-tool calls instead of prose responses.
ccmcp's doctor lints content quality (CLAUDE.md/MEMORY.md structure, skill/agent/command description and token-budget limits) - it complements, and does not duplicate, Claude Code's own built-in /doctor, which validates config (auto-updater health, settings/.mcp.json schema). The asset-lint limits calibrate to the installed Claude Code version (detected via claude --version and shown as · CC <ver> in the header); the per-skill description cap honors your skillListingMaxDescChars setting. To support a new Claude Code version, the version logic lives in one place - internal/claudecode/CapabilitiesFor.
Tweaks tab (t to open)
The Tweaks tab consolidates app-level controls into five sub-views, cycled with [ / ] (or left/right arrows):
- Settings - toggle ccmcp preferences (offline discovery mode, etc.) with
space/enter. Settings are persisted to ~/.claude-mcp-config.json and honored on the next launch.
- Maintenance - one-keystroke ops: snapshot the current config, prune stale overrides, GC orphaned plugin caches, and run a health-check. Press
enter on any action to run it.
- Summary - the bird's-eye overview (same content as the former top-level Summary tab).
- Doctor - the CLAUDE.md/MEMORY.md linter (same content as the former top-level Doctor tab).
- Profiles - save/apply MCP profiles (same content as the former top-level Profiles tab).
Digits 8/9/0 are unbound; all former numeric shortcuts for Profiles/Summary/Doctor are replaced by t + [/] navigation.
Global
| Key |
Action |
tab / shift+tab |
cycle tabs |
1-7 |
jump to MCPs / Plugins / Marketplaces / Discover / Skills / Agents / Commands |
t |
jump to Tweaks tab (Settings sub-view) |
[ / ] or arrows |
switch Tweaks sub-sections: Settings / Maintenance / Summary / Doctor / Profiles |
ctrl+g |
global search across all tabs (enter jumps to the row, esc closes) |
w |
save all staged changes |
q |
quit (warns if unsaved) |
Q |
force quit, discard changes |
Changes are staged until you press w. An UNSAVED badge appears in the header whenever you have pending edits.
CLI
Every TUI action is scriptable from the CLI:
ccmcp status # show everything at once
ccmcp status --json # JSON for scripts
ccmcp mcp list [--scope SCOPE] # SCOPE: user|local|project|plugin|claudeai|overrides|stash|all
ccmcp mcp enable <name> [--scope SCOPE]
ccmcp mcp disable <name> [--scope SCOPE] [--to-stash]
ccmcp mcp move <name> --to {user|local|stash}
ccmcp mcp override <name> [--undo] # per-project disable (writes disabledMcpServers)
ccmcp mcp prune [--dry-run] [--yes] # remove stale entries from disabledMcpServers
[--include-stash-ghosts] # (keeps disabled-but-installed plugin entries)
ccmcp mcp stash [<name>...] # user-scope → stash
ccmcp mcp restore [<name>...] # stash → user-scope (alias: unstash)
ccmcp mcp unstash [<name>...] # same as restore
ccmcp profile save|list|show|use|delete <name> [<mcp>...]
ccmcp profile export <name> [--out FILE] [--with-config]
ccmcp profile import [FILE|-] [--overwrite]
ccmcp plugin list [--enabled|--disabled]
ccmcp plugin enable|disable|install|remove <id> [--marketplace M] [--purge]
ccmcp marketplace list|add|remove <name> [--source github|git|local] [--repo R]
ccmcp skill list [--scope user|project|plugin] [--enabled|--disabled]
ccmcp skill enable|disable <name> [<name>...] # writes to skillOverrides
ccmcp skill new <name> [--scope user|project] [--description D]
ccmcp skill move <name> --to {user|project}
ccmcp skill rm <name> [--scope user|project]
ccmcp skill show <name>
ccmcp agent list|new|move|rm|show <name> # same verb shape as skill
ccmcp command list [--scope user|project|plugin]
ccmcp command conflicts [--include-ignored] [--json]
ccmcp command resolve <effective-name> [--strategy disable-skill|ignore|list]
ccmcp report snapshot [--out FILE] [--format json|md|csv] # point-in-time state dump
ccmcp report sweep [--base PATH] [--format json|md|csv] # summary table across all projects
ccmcp report drift --from <snapshot.json> [--format json|md] # what changed since baseline
ccmcp report audit [--format json|md|csv] # stale overrides, conflicts, redundancies
ccmcp doctor md [--user] [--memory-dir DIR] # lint CLAUDE.md + MEMORY.md
ccmcp doctor md --llm-review [--provider anthropic|openai] # + LLM quality review
ccmcp tui --dump [--tab mcps|plugins|marketplaces|discover|skills|agents|commands|profiles|summary|doctor] # print initial render, no TTY
Global flags: --path <dir> (override cwd), --dry-run, --json, --no-color, --no-update-check (also $CCMCP_NO_UPDATE_CHECK).
Self-update check: the first interactive launch each day queries
api.github.com/repos/ringo380/ccmcp/releases/latest (2-second timeout, silent on failure),
and if a newer release is out it prints a release-notes excerpt and prompts
Update now? [Y/n]. On Y it auto-detects Homebrew vs go install vs a manual
binary and runs the matching upgrade command. On n the prompt is suppressed for
24 hours or until a newer release ships. Cached at
~/.claude/plugins/cache/ccmcp-update-check.json.
Per-project overrides
/mcp in Claude Code writes per-project disables to ~/.claude.json#/projects[<path>].disabledMcpServers. ccmcp reads and writes the same field with the same prefix encoding:
| Example key |
Source |
"dropbox" |
stdio MCP (plain name) |
"claude.ai Gmail" |
claude.ai integration |
"plugin:context7:context7" |
plugin-registered MCP (plugin:<plugin>:<server>) |
Pressing space on an effective-view row flips the appropriate override key; A/N do it for every visible row at once.
Pruning stale overrides
Over time, disabledMcpServers collects entries that no longer match any live source - e.g. an MCP that was renamed, a plugin that was uninstalled, or a server that moved into the stash. ccmcp mcp prune classifies every entry and removes the stale ones:
ccmcp mcp prune --dry-run # list proposed removals, no changes
ccmcp mcp prune --yes # go ahead, skip confirmation
ccmcp mcp prune --include-stash-ghosts # also sweep plain-name overrides that match a stash entry
Orphan entries (plugin not installed, plain name with no source) are pruned by default. Disabled-but-installed plugin overrides are preserved - re-enabling the plugin would re-activate the MCP, and the user likely wanted it off per-project. Remove those explicitly with ccmcp mcp override <key> --undo if that's actually what you want.
Plugin installer
ccmcp plugin install <name> --marketplace <m> fetches source code from the marketplace and records the install in ~/.claude/plugins/installed_plugins.json. Four marketplace source formats are supported:
- bare string path (
./plugins/foo) - subdir of an already-cloned marketplace repo
url - full-repo clone, optional sha pin
git-subdir - clone a repo and copy a subpath
github - repo-shorthand (owner/name, optional ref)
--register-only skips the fetch if the cache dir already exists. --purge on plugin remove also deletes the cache.
Safety
- Every write is atomic (
temp + fsync + rename). No partial writes survive a crash.
- Before any mutation, ccmcp snapshots the target file to
~/.claude-mcp-backups/<name>-<YYYYMMDD-HHMMSS>.json. Same-second collisions get a numeric suffix.
--dry-run never touches disk (asserted by a test against before/after md5s).
~/.claude.json has 80+ telemetry / onboarding / cache fields; ccmcp mutates only what it owns and preserves unknown fields round-trip. Verified by TestClaudeJSONPreservesUnknownFields.
Testing
go test ./...
412 tests across config readers/writers, CLI sandbox runs, installer, skill/agent CRUD, command discovery + conflict classifier + ignore list, profile export/import, marketplace + plugin update probes, plugin MCP scanning (.mcp.json + plugin.json manifest merge), doctor LLM-review provider precedence, doctor autofix preview/snapshot/revert flow, asset lint (skill/agent/command/plugin description + slug rules + skill-shadow detection), Claude Code version detection + capability calibration (probe/cache/mtime-invalidation, version-gated fallback-model, model-override precedence), bulk plugin-update failure capture + retry, marketplace discovery (sources, cache, conflict scan), shell-completion script generation + dynamic arg completion, TUI scroll-window clamping for multi-line list views, and a headless TUI state-machine that drives the real tea.Model with synthesized key events.
Project layout
cmd/ cobra subcommands (status, mcp, profile, plugin, marketplace,
skill, agent, command, discover, report, doctor, compat
aliases)
internal/
agents/ agent CRUD + file-backed store
classify/ override-key classifier (7 buckets)
claudecode/ installed Claude Code version probe + capability calibration
(single place to update per CC release)
commands/ command discovery, conflict detection, ignore list
config/ readers + writers for every Claude Code config file
discovery/ remote marketplace discovery (curated registry + awesome-list
+ user URLs merged, preview-clone + conflict detection)
doctor/ CLAUDE.md + MEMORY.md structural linter + asset lint
install/ plugin marketplace installer (4 source formats)
paths/ config path resolution ($CLAUDE_CONFIG_DIR aware)
report/ snapshot / sweep / drift / audit report generators
selfupdate/ launch-time check vs GitHub releases + Y/n prompt + brew/go
dispatch (oh-my-zsh style)
skills/ skill CRUD + file-backed store
stringslice/ shared slice helpers
tui/ bubbletea app: 8 top-level tabs (MCPs, Plugins, Marketplaces,
Discover, Skills, Agents, Commands, Tweaks); Tweaks folds
Settings, Maintenance, Summary, Doctor, and Profiles as
sub-views cycled with [ / ]
updates/ upstream version probes for marketplaces, plugins, MCPs
main.go
Scope terminology
ccmcp uses Claude Code's native names:
- user -
~/.claude.json#/mcpServers
- local -
~/.claude.json#/projects[<cwd>]/mcpServers (what Claude Code calls "local")
- project -
./.mcp.json (what Claude Code calls "project" - shared via git)
- stash -
~/.claude-mcp-stash.json (ccmcp-owned)
- effective - the union of what actually loads in the current project
Legacy aliases --scope project → local and --scope mcpjson → project are still accepted so older scripts keep working.
Contributing
Contributions are welcome. See CONTRIBUTING.md for build/test
instructions, coding guidelines, and the Developer Certificate of Origin
sign-off requirement (git commit -s). For substantial features, open an issue
first to align on the approach.
License & attribution
ccmcp is open source under the MIT License - free to use, fork,
modify, and redistribute, provided you retain the copyright notice and license
text (see NOTICE).
The "ccmcp" name and brand are reserved and are not covered by the MIT
grant. Forks and derivative works must use a clearly distinct name and may not
imply official status or endorsement - see TRADEMARK.md. You're
welcome to refer to the project by name accurately (e.g. "a fork of ccmcp").