CLI tool to share MCP configuration between coding agents.
Synchronizes Model Context Protocol server definitions across VSCode, GitHub Copilot CLI, Claude Code, OpenAI Codex, Google Gemini CLI, Google Antigravity, and OpenCode.
# Install as a global CLI tool from GitHub
uv tool install git+https://github.com/eggboy/mcpshare.git
# Or install from a local clone of the repo
git clone https://github.com/eggboy/mcpshare.git
cd mcpshare
uv tool install .
# Then run from anywhere
mcpshare init
mcpshare pull
mcpshare sync
mcpshare status
# Upgrade to the latest version
uv tool upgrade mcpshareThe config file lives at ~/.config/mcpshare/config.yaml:
source: /path/to/master # directory for the canonical mcp.json
mode: merge # merge | overwrite
targets:
claude:
path: ~/.claude
vscode:
path: ~/Library/Application Support/Code/User # macOS; see docs for other OS
copilot:
path: ~/.copilot
codex:
path: ~/.codex
gemini:
path: ~/.gemini
antigravity:
path: ~/.gemini/config
opencode:
path: ~/.config/opencode| Mode | Behaviour |
|---|---|
merge |
Collect servers from every target first, merge into master, then distribute |
overwrite |
Use the master as the single source of truth and overwrite all targets |
mcpshare init– creates the config file and master directory.mcpshare pull– pulls MCP server entries from every configured target and merges them into the mastermcp.json(collect only, does not write back to targets).mcpshare sync– distributes the mastermcp.jsonto all configured targets, converting to each tool's native format.mcpshare status– shows the master servers and whether each target config file exists.
Typical workflow: mcpshare pull → mcpshare sync.
Disable state is tracked exclusively in mcp.disabled.yaml, colocated with
the master mcp.json in your source directory. The master itself never
carries a per-entry disabled flag — if you add one by hand, mcpshare
strips it on the next pull / sync. The same applies to any IDE-native
disable flag (Codex enabled = false, etc.) found in target files on read:
it is stripped, and disable state is re-derived from mcp.disabled.yaml
on the next sync.
This means the disabled list survives even when you delete and regenerate
mcp.json, and there is exactly one place to look — mcp.disabled.yaml.
mcpshare disable --servers <server> # disable in all IDEs
mcpshare disable --servers <server> --ide claude # disable in one IDE only
mcpshare enable --servers <server> # re-enable in every IDE
mcpshare enable --servers <server> --ide claude # re-enable in one IDE only
mcpshare status # shows disabled listsmcp.disabled.yaml format (top-level flat mapping, colocated with mcp.json):
"*": # entries here are disabled in every configured IDE
- workiq-mcp
claude: # entries here are disabled only for Claude Code
- filesystem
codex:
- filesystemIf you previously used
~/.config/mcpshare/disabled.yaml, mcpshare moves it next to your mastermcp.jsonautomatically on the first load.
sync writes every master server to every configured IDE. For disabled
servers, mcpshare uses each IDE's native disable mechanism rather than
omitting the entry (so the server stays visible and can be re-enabled in the
tool's UI without round-tripping through mcpshare enable). Claude Code is
the exception: it has no global disable list, so disabled servers are left
out of its config.
| IDE | Native disable mechanism | What sync writes |
|---|---|---|
| Codex | enabled = false in the server's TOML block |
Server written with enabled = false |
| Claude Code | None global (/mcp toggles are stored per project in ~/.claude.json) |
Server omitted from mcpServers in ~/.claude.json |
| Copilot CLI | disabledMcpServers: [...] in ~/.copilot/settings.json |
Server in mcp-config.json plus name in settings.json array |
| Gemini CLI | mcp.excluded: [...] in ~/.gemini/settings.json (security-grade blocklist) |
Server in mcpServers plus name in mcp.excluded (same file) |
| Antigravity | "disabled": true per entry in ~/.gemini/config/mcp_config.json |
Server written with "disabled": true |
| OpenCode | "enabled": false per entry in opencode.json |
Server written with "enabled": false |
| VSCode | None (UI-only per profile) | Server is always written enabled. disable --ide vscode is recorded but warns and is a no-op on sync. |
Source of truth: mcpshare-owns. On each sync, mcpshare replaces
the disabledMcpServers array in Copilot settings.json with its own
view (other settings.json keys are preserved). If you previously ran
/mcp disable foo natively inside Copilot, the next sync will
re-enable foo and log a warning telling you the exact mcpshare disable
command to run if you want to keep it disabled.
The same overwrite-with-mcpshare's-view rule applies to Gemini's
mcp.excluded array and to Codex/OpenCode's per-entry enabled = false —
all other keys in the target files are preserved.
For MCP-dedicated target files (mcp.json, mcp-config.json), sync
deletes the file and recreates it so removed servers cannot linger.
For files shared with other IDE settings (.claude.json, config.toml,
settings.json, mcp_config.json, opencode.json), only the MCP section
is replaced — non-MCP content is preserved. For Copilot, the
disabledMcpServers array in settings.json is also overwritten in place
while preserving every other key.
Claude Code reads user-scope MCP servers only from the top-level
mcpServers object in ~/.claude.json (the same place
claude mcp add --scope user writes). The configured target stays
~/.claude, Claude's config dir; mcpshare resolves it the way Claude does,
so ~/.claude maps to ~/.claude.json and any other directory (as with
CLAUDE_CONFIG_DIR) maps to <dir>/.claude.json.
- Only
mcpServersis replaced, atomically; the rest of~/.claude.jsonis Claude's own state and is left alone. - Each entry gets
"type": "stdio"or"type": "http". Claude rejects a remote server without a type. - Codex-only
toolsallow-lists are not written. - Restart running Claude Code sessions after
syncto pick up the change.
Versions before 0.17.1 wrote ~/.claude/mcp_servers.json and a
disabledMcpServers array in ~/.claude/settings.json. Claude reads
neither, so sync now deletes that file and removes that one key (other
settings.json keys are kept).
The Antigravity app, IDE, and CLI (agy) share one global file,
~/.gemini/config/mcp_config.json, so the target path is
~/.gemini/config. Older locations (~/.gemini/antigravity/ and
~/.gemini/antigravity-cli/) are not written.
- Remote servers are written with
serverUrl, the only key Antigravity accepts (urlfails only when a tool is first called). - No
typefield is written; Codex-onlytoolsallow-lists are dropped. - Disabled servers stay listed with
"disabled": true, so they can be toggled from Antigravity's UI. - After
sync, click Refresh under Installed MCP Servers in the app, or reload from/mcpinagy.
When mcpshare pull finds the same server name in two IDEs with different
commands/args/env, it prompts you to pick one. Identical configs are
silently merged, and any per-entry disabled / enabled flag found in a
target file is stripped before comparison — so a server that differs only
in disable state never counts as a conflict (its disable state is owned by
mcp.disabled.yaml, not by the master or any target file). Pass
--non-interactive (or run without a TTY) to fall back to last-write-wins
with a warning.
| Tool | File | Top-level key | Notes |
|---|---|---|---|
| Claude Code | ~/.claude.json |
mcpServers |
Shared with Claude's state; only mcpServers is replaced. Adds "type". Disabled servers omitted |
| VSCode | mcp.json |
servers |
MCP-dedicated; recreated on sync. Adds "type": "stdio". No native disable |
| Copilot CLI | mcp-config.json |
mcpServers |
MCP-dedicated; recreated on sync. Adds "type": "stdio". Disable via settings.json |
| Codex | config.toml |
[mcp_servers.*] |
TOML; preserves non-MCP sections. Native enabled = false |
| Gemini CLI | settings.json |
mcpServers |
Preserves non-MCP settings. Disable via mcp.excluded (same file) |
| Antigravity | ~/.gemini/config/mcp_config.json |
mcpServers |
url written as serverUrl; no type; Codex tools not written. Native "disabled": true per entry |
| OpenCode | opencode.json |
mcp |
Uses command array, environment. Native "enabled": false per entry |
pip install -e .
pip install pytest
python -m pytest tests/ -v